name-8.x-1.x-dev/src/NameFormatter.php
src/NameFormatter.php
<?php
namespace Drupal\name;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\name\Render\NameListFormattableMarkup;
/**
* Primary name formatter for an array of name components.
*
* This service should be used for any name formatting requests and direct
* calls to the "name.format_parser" service should be avoided.
*
* Usage:
* \Drupal::service('name.formatter')->format().
*/
class NameFormatter implements NameFormatterInterface {
use StringTranslationTrait;
/**
* The name format parser.
*
* @var \Drupal\name\NameFormatParser
*/
protected $parser;
/**
* The name format storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $nameFormatStorage;
/**
* The name list format storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $listFormatStorage;
/**
* Language manager for retrieving the default language code if needed.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* The factory for configuration objects.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* Settings for the formatter.
*
* Values include:
* - sep1: First defined separator.
* - sep2: Second defined separator.
* - sep3: Third defined separator.
* - markup: To markup the individual components.
*
* @var array
*/
protected $settings = [
'sep1' => ' ',
'sep2' => ', ',
'sep3' => '',
'markup' => 'none',
];
/**
* Constructs a name formatter object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity manager.
* @param \Drupal\name\NameFormatParser $parser
* The name format parser.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
* The string translation.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The factory for configuration objects.
*/
public function __construct(EntityTypeManagerInterface $entityTypeManager, NameFormatParser $parser, LanguageManagerInterface $language_manager, TranslationInterface $translation, ConfigFactoryInterface $config_factory) {
$this->nameFormatStorage = $entityTypeManager->getStorage('name_format');
$this->listFormatStorage = $entityTypeManager->getStorage('name_list_format');
$this->parser = $parser;
$this->languageManager = $language_manager;
$this->stringTranslation = $translation;
$this->configFactory = $config_factory;
$config = $this->configFactory->get('name.settings');
$this->settings['sep1'] = $config->get('sep1');
$this->settings['sep2'] = $config->get('sep2');
$this->settings['sep3'] = $config->get('sep3');
}
/**
* {@inheritdoc}
*/
public function setSetting($key, $value) {
$this->settings[$key] = $value;
return $this;
}
/**
* {@inheritdoc}
*/
public function getSetting($key) {
return $this->settings[$key] ?? NULL;
}
/**
* {@inheritdoc}
*/
public function format(array $components, $type = 'default', $langcode = NULL) {
$format_string = $this->getNameFormatString($type);
$name = $this->parser->parse($components, $format_string, $this->settings);
if (!empty($components['url'])) {
$name = new FormattableMarkup('<a href=":link">' . $name . '</a>', [
':link' => $components['url']->toString(),
]);
}
return $name;
}
/**
* {@inheritdoc}
*/
public function formatList(array $items, $type = 'default', $list_type = 'default', $langcode = NULL) {
$name_count = count($items);
// Avoid any computations if none or one names only.
if (!$name_count) {
return '';
}
if ($name_count == 1) {
$item = reset($items);
return $this->format($item, $type, $langcode);
}
$settings = $this->getListSettings($list_type);
// Removed names that don't need to be formatted.
if ($settings['el_al_min'] && $name_count > $settings['el_al_min']) {
$items = array_slice($items, 0, $settings['el_al_first']);
}
$names = [];
foreach ($items as $item) {
$names[] = $this->format($item, $type, $langcode);
}
if ($name_count > $settings['el_al_min']) {
$etal = $this->t('et al', [], ['context' => 'name']);
if ($this->settings['markup'] !== 'none') {
$etal = new FormattableMarkup('<em>@etal</em>', ['@etal' => $etal]);
}
if (count($names) == 1) {
return $this->t('@name@delimiter @etal', [
'@name' => reset($names),
'@delimiter' => trim($settings['delimiter']),
'@etal' => $etal,
]);
}
else {
$names = new NameListFormattableMarkup($names, $settings['delimiter']);
return $this->t('@names@delimiter @etal', [
'@names' => $names,
'@delimiter' => trim($settings['delimiter']),
'@etal' => $etal,
]);
}
}
else {
if ($settings['and'] == 'inherit') {
return new NameListFormattableMarkup($names, $settings['delimiter']);
}
$t_args = [
'@lastname' => array_pop($names),
'@names' => new NameListFormattableMarkup($names, $settings['delimiter']),
'@delimiter' => trim($settings['delimiter']),
];
if ($settings['and'] == 'text') {
$t_args['@and'] = $this->t('and', [], ['context' => 'name']);
}
else {
$t_args['@and'] = $this->t('&', [], ['context' => 'name']);
}
// Strange rule from citationstyles.org.
// @see http://citationstyles.org/downloads/specification.html
if (($settings['delimiter_precedes_last'] == 'contextual' && $name_count > 2)
|| $settings['delimiter_precedes_last'] == 'always') {
return $this->t('@names@delimiter @and @lastname', $t_args);
}
else {
return $this->t('@names @and @lastname', $t_args);
}
}
}
/**
* Helper function to get the format pattern.
*
* @param string $format
* The ID of the preferred format to use. This will fallback to the default
* format if the format can not be loaded.
*
* @return string
* The pattern to parse.
*/
protected function getNameFormatString($format) {
$config = $this->nameFormatStorage->load($format);
if (!$config) {
$config = $this->nameFormatStorage->load('default');
}
return $config->get('pattern');
}
/**
* Helper function to load and get the format list settings.
*
* @param string $format
* The ID of the preferred format to use. This will fallback to the default
* format if the format can not be loaded.
*
* @return array
* The settings to use to format the list.
*/
protected function getListSettings($format) {
/** @var \Drupal\name\Entity\NameListFormat $list_format */
$list_format = $this->listFormatStorage->load($format);
if (!$list_format) {
$list_format = $this->listFormatStorage->load('default');
}
return $list_format->listSettings();
}
/**
* {@inheritdoc}
*/
public function getLastDelimitorTypes($include_examples = TRUE) {
// cspell:ignore delimitor
@trigger_error('getLastDelimitorTypes() is deprecated in name:8.x-1.1 and is removed from name:2.0.0. use getLastDelimiterTypes(). See https://www.drupal.org/project/name/issues/3518599', E_USER_DEPRECATED);
return $this->getLastDelimiterTypes($include_examples);
}
/**
* {@inheritdoc}
*/
public function getLastDelimiterTypes($include_examples = TRUE) {
if ($include_examples) {
return [
'text' => $this->t('Textual (and)'),
'symbol' => $this->t('Ampersand (&)'),
'inherit' => $this->t('Inherit delimiter'),
];
}
else {
return [
'text' => $this->t('Textual'),
'symbol' => $this->t('Ampersand'),
'inherit' => $this->t('Inherit delimiter'),
];
}
}
/**
* {@inheritdoc}
*/
public function getLastDelimitorBehaviors($include_examples = TRUE) {
// cspell:ignore delimitor
@trigger_error('getLastDelimitorBehaviors() is deprecated in name:8.x-1.1 and is removed from name:2.0.0. use getLastDelimiterBehaviors(). See https://www.drupal.org/project/name/issues/3518599', E_USER_DEPRECATED);
return $this->getLastDelimiterBehaviors($include_examples);
}
/**
* {@inheritdoc}
*/
public function getLastDelimiterBehaviors($include_examples = TRUE) {
if ($include_examples) {
return [
'never' => $this->t('Never (i.e. "J. Doe and T. Williams")'),
'always' => $this->t('Always (i.e. "J. Doe<strong>,</strong> and T. Williams")'),
'contextual' => $this->t('Contextual (i.e. "J. Doe and T. Williams" <em>or</em> "J. Doe, S. Smith<strong>,</strong> and T. Williams")'),
];
}
else {
return [
'never' => $this->t('Never'),
'always' => $this->t('Always'),
'contextual' => $this->t('Contextual'),
];
}
}
}
