name-8.x-1.x-dev/src/Plugin/Field/FieldFormatter/NameFormatter.php
src/Plugin/Field/FieldFormatter/NameFormatter.php
<?php
namespace Drupal\name\Plugin\Field\FieldFormatter;
use Drupal\Core\Entity\EntityFieldManager;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException;
use Drupal\Core\Field\Attribute\FieldFormatter;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\name\NameFormatParser;
use Drupal\name\NameFormatter as NameFormatterService;
use Drupal\name\NameGeneratorInterface;
use Drupal\name\Traits\NameAdditionalPreferredTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the 'name' formatter.
*
* The 'Default' formatter is different for integer fields on the one hand, and
* for decimal and float fields on the other hand, in order to be able to use
* different settings.
*/
#[FieldFormatter(
id: "name_default",
label: new TranslatableMarkup("Name formatter"),
field_types: ["name"]
)]
class NameFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
use NameAdditionalPreferredTrait;
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManager
*/
protected $entityFieldManager;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The field renderer for any additional components.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* The name formatter.
*
* @var \Drupal\name\NameFormatter
*/
protected $formatter;
/**
* The name format parser.
*
* Directly called to format the examples without the fallback.
*
* @var \Drupal\name\NameFormatParser
*/
protected $parser;
/**
* The name generator.
*
* @var \Drupal\name\NameGeneratorInterface
*/
protected $generator;
/**
* Constructs a NameFormatter instance.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Any third party settings settings.
* @param \Drupal\Core\Entity\EntityFieldManager $entityFieldManager
* The entity field manager.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The rendering service.
* @param \Drupal\name\NameFormatter $formatter
* The name formatter.
* @param \Drupal\name\NameFormatParser $parser
* The name format parser.
* @param \Drupal\name\NameGeneratorInterface $generator
* The name format parser.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, EntityFieldManager $entityFieldManager, EntityTypeManagerInterface $entityTypeManager, RendererInterface $renderer, NameFormatterService $formatter, NameFormatParser $parser, NameGeneratorInterface $generator) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->entityFieldManager = $entityFieldManager;
$this->entityTypeManager = $entityTypeManager;
$this->renderer = $renderer;
$this->formatter = $formatter;
$this->parser = $parser;
$this->generator = $generator;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['view_mode'],
$configuration['third_party_settings'],
$container->get('entity_field.manager'),
$container->get('entity_type.manager'),
$container->get('renderer'),
$container->get('name.formatter'),
$container->get('name.format_parser'),
$container->get('name.generator')
);
}
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
$settings = parent::defaultSettings();
$settings += [
"format" => "default",
"markup" => "none",
"list_format" => "",
"link_target" => "",
];
$settings += self::getDefaultAdditionalPreferredSettings();
return $settings;
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements = parent::settingsForm($form, $form_state);
$elements['format'] = [
'#type' => 'select',
'#title' => $this->t('Name format'),
'#default_value' => $this->getSetting('format'),
'#options' => name_get_custom_format_options(),
'#required' => TRUE,
];
$elements['list_format'] = [
'#type' => 'select',
'#title' => $this->t('List format'),
'#default_value' => $this->getSetting('list_format'),
'#empty_option' => $this->t('-- individually --'),
'#options' => name_get_custom_list_format_options(),
];
$elements['markup'] = [
'#type' => 'select',
'#title' => $this->t('Markup'),
'#default_value' => $this->getSetting('markup'),
'#options' => $this->parser->getMarkupOptions(),
'#description' => $this->t('This option wraps the individual components of the name in SPAN elements with corresponding classes to the component.'),
'#required' => TRUE,
];
if (!empty($this->fieldDefinition->getTargetBundle())) {
$elements['link_target'] = [
'#type' => 'select',
'#title' => $this->t('Link Target'),
'#default_value' => $this->getSetting('link_target'),
'#empty_option' => $this->t('-- no link --'),
'#options' => $this->getLinkableTargets(),
];
$elements += $this->getNameAdditionalPreferredSettingsForm($form, $form_state);
}
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$settings = $this->getSettings();
$summary = [];
// Name format.
$machine_name = $settings['format'] ?? 'default';
$name_format = $this->entityTypeManager->getStorage('name_format')->load($machine_name);
if ($name_format) {
$summary[] = $this->t('Format: @format (@machine_name)', [
'@format' => $name_format->label(),
'@machine_name' => $name_format->id(),
]);
}
else {
$summary[] = $this->t('Format: <strong>Missing format.</strong><br/>This field will be displayed using the Default format.');
}
// List format.
if (!isset($settings['list_format']) || $settings['list_format'] == '') {
$summary[] = $this->t('List format: Individually');
}
else {
$machine_name = $settings['list_format'] ?? 'default';
$name_format = $this->entityTypeManager->getStorage('name_list_format')->load($machine_name);
if ($name_format) {
$summary[] = $this->t('List format: @format (@machine_name)', [
'@format' => $name_format->label(),
'@machine_name' => $name_format->id(),
]);
}
else {
$summary[] = $this->t('List format: <strong>Missing list format.</strong><br/>This field will be displayed using the Default list format.');
}
}
// Additional options.
$markup_options = $this->parser->getMarkupOptions();
$summary[] = $this->t('Markup: @type', [
'@type' => $markup_options[$this->getSetting('markup')],
]);
if (!empty($settings['link_target'])) {
$targets = $this->getLinkableTargets();
$summary[] = $this->t('Link: @target', [
'@target' => empty($targets[$settings['link_target']]) ? $this->t('-- invalid --') : $targets[$settings['link_target']],
]);
}
$this->settingsNameAdditionalPreferredSummary($summary);
// Provide an example of the selected format.
if ($name_format) {
$names = $this->generator->loadSampleValues(1, $this->fieldDefinition);
if ($name = reset($names)) {
$formatted = $this->parser->parse($name, $name_format->get('pattern'));
if (empty($formatted)) {
$summary[] = $this->t('Example: <em><<empty>></em>');
}
else {
$summary[] = $this->t('Example: @example', [
'@example' => $formatted,
]);
}
}
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
$items_count = $items->count();
if (!$items_count) {
return $elements;
}
$settings = $this->settings;
$format = $settings['format'] ?? 'default';
$is_multiple = $this->fieldDefinition->getFieldStorageDefinition()->isMultiple() && $items_count > 1;
$list_format = $is_multiple && !empty($settings['list_format']) ? $settings['list_format'] : '';
$extra = $this->parseAdditionalComponents($items);
$extra['url'] = empty($settings['link_target']) ? NULL : $this->getLinkableTargetUrl($items);
$item_array = [];
foreach ($items as $item) {
$components = $item->toArray() + $extra;
$item_array[] = $components;
}
$this->formatter->setSetting('markup', $this->getSetting('markup'));
if ($list_format) {
$elements[0]['#markup'] = $this->formatter->formatList($item_array, $format, $list_format, $langcode);
}
else {
foreach ($item_array as $delta => $item) {
$elements[$delta]['#markup'] = $this->formatter->format($item, $format, $langcode);
}
}
return $elements;
}
/**
* Determines with markup should be added to the results.
*
* @return bool
* Returns TRUE if markup should be applied.
*/
protected function useMarkup() {
return $this->settings['markup'];
}
/**
* Find any linkable targets.
*
* @return array
* An array of possible targets.
*/
protected function getLinkableTargets() {
$targets = ['_self' => $this->t('Entity URL')];
$bundle = $this->fieldDefinition->getTargetBundle();
$entity_type_id = $this->fieldDefinition->getTargetEntityTypeId();
$fields = $this->entityFieldManager->getFieldDefinitions($entity_type_id, $bundle);
foreach ($fields as $field) {
if (!$field->getFieldStorageDefinition()->isBaseField()) {
switch ($field->getType()) {
case 'entity_reference':
case 'link':
$targets[$field->getName()] = $field->getLabel();
break;
}
}
}
return $targets;
}
/**
* Gets the URL object.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The name formatters FieldItemList.
*
* @return \Drupal\Core\Url
* Returns a Url object.
*/
protected function getLinkableTargetUrl(FieldItemListInterface $items) {
try {
$parent = $items->getEntity();
if ($this->settings['link_target'] == '_self') {
if (!$parent->isNew() && $parent->access('view')) {
return $parent->toUrl();
}
}
elseif ($parent->hasField($this->settings['link_target'])) {
$target_items = $parent->get($this->settings['link_target']);
if (!$target_items->isEmpty()) {
$field = $target_items->getFieldDefinition();
switch ($field->getType()) {
case 'entity_reference':
foreach ($target_items as $item) {
if (!empty($item->entity) && !$item->entity->isNew() && $item->entity->access('view')) {
return $item->entity->toUrl();
}
}
break;
case 'link':
foreach ($target_items as $item) {
if ($url = $item->getUrl()) {
return $url;
}
}
break;
}
}
}
}
catch (UndefinedLinkTemplateException $e) {
}
return Url::fromRoute('<none>');
}
/**
* Gets any additional linked components.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The name formatters FieldItemList.
*
* @return array
* An array of any additional components if set.
*/
protected function parseAdditionalComponents(FieldItemListInterface $items) {
$extra = [];
foreach (['preferred', 'alternative'] as $key) {
$key_value = $this->getSetting($key . '_field_reference');
$sep_value = $this->getSetting($key . '_field_reference_separator');
if (!$key_value) {
$key_value = $this->fieldDefinition->getSetting($key . '_field_reference');
$sep_value = $this->fieldDefinition->getSetting($key . '_field_reference_separator');
}
if ($value = name_get_additional_component($this->entityTypeManager, $this->renderer, $items, $key_value, $sep_value)) {
$extra[$key] = $value;
}
}
return $extra;
}
/**
* Gets the field definition.
*
* This is used by NameAdditionalPreferredTrait.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface
* The field definition.
*/
protected function getFieldDefinition() {
return $this->fieldDefinition;
}
}
