custom_field-1.0.x-dev/src/Plugin/Field/FieldFormatter/CustomTemplateFormatter.php
src/Plugin/Field/FieldFormatter/CustomTemplateFormatter.php
<?php
namespace Drupal\custom_field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\Attribute\FieldFormatter;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Utility\Token;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the 'custom_template' formatter.
*/
#[FieldFormatter(
id: 'custom_template',
label: new TranslatableMarkup('Custom template'),
description: new TranslatableMarkup('Render the custom field using a custom template with token replacement.'),
field_types: [
'custom',
],
weight: 4,
)]
class CustomTemplateFormatter extends BaseFormatter {
/**
* The token service.
*
* @var \Drupal\Core\Utility\Token
*/
protected Token $tokenService;
/**
* The token entity mapper service.
*
* @var \Drupal\token\TokenEntityMapperInterface
*/
protected $tokenEntityMapper;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$instance->tokenService = $container->get('token');
$instance->tokenEntityMapper = $container->get(
'token.entity_mapper',
ContainerInterface::NULL_ON_INVALID_REFERENCE
);
return $instance;
}
/**
* {@inheritdoc}
*/
public static function defaultSettings(): array {
return [
'template' => '',
'tokens' => 'basic',
'advanced_tokens' => [
'recursion_limit' => 3,
'global_types' => FALSE,
],
];
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state): array {
$form = parent::settingsForm($form, $form_state);
$entity_type = $this->fieldDefinition->getTargetEntityTypeId();
$custom_items = $this->getCustomFieldItems();
$token_module_installed = $this->moduleHandler->moduleExists('token');
$field_name = $this->fieldDefinition->getName();
$is_views_form = $form_state->getFormObject()->getFormId() == 'views_ui_config_item_form';
$visibility_path = 'fields[' . $field_name . '][settings_edit_form][settings]';
if ($is_views_form) {
$visibility_path = 'options[settings]';
}
// Remove field level settings as they are not applicable.
unset($form['fields']);
$form['tokens'] = [
'#type' => 'radios',
'#title' => $this->t('Tokens'),
'#options' => [
'basic' => $this->t('Basic'),
'advanced' => $this->t('Advanced'),
],
'#required' => TRUE,
'#default_value' => $this->getSetting('tokens'),
];
$form['template'] = [
'#type' => 'textarea',
'#title' => $this->t('Template'),
'#rows' => 5,
'#default_value' => $this->getSetting('template'),
'#attributes' => [
'placeholder' => $this->t('Insert tokens here along with any desired html.'),
],
];
// Build the basic token markup.
$basic_tokens = [
'#theme' => 'item_list',
'#items' => [],
];
foreach ($custom_items as $name => $custom_item) {
$label = $custom_item->getLabel();
$basic_tokens['#items'][] = "[$name]: $label value";
$basic_tokens['#items'][] = "[$name:label]: $label label";
}
$form['basic_tokens'] = [
'#type' => 'item',
'#description' => $this->t('<p><strong>The following tokens are available for replacement.</strong></p>') . $this->renderer->renderInIsolation($basic_tokens),
'#states' => [
'visible' => [
':input[name="' . $visibility_path . '[tokens]"]' => ['value' => 'basic'],
],
],
];
if (!$token_module_installed) {
$form['advanced_tokens_notice'] = [
'#theme_wrappers' => ['container'],
'#type' => 'markup',
'#markup' => $this->t('Advanced token replacement requires the <a href=":url" target="_blank">Token module</a>.', [
':url' => 'https://www.drupal.org/project/token',
]),
'#states' => [
'visible' => [
':input[name="' . $visibility_path . '[tokens]"]' => ['value' => 'advanced'],
],
],
];
}
else {
$token_type = $this->tokenEntityMapper->getTokenTypeForEntityType($entity_type);
// Use token_browser_link if available.
if ($this->moduleHandler->moduleExists('token_browser')) {
$form['advanced_tokens'] = [
'#theme' => 'token_browser_link',
'#theme_wrappers' => ['container'],
'#token_types' => [$token_type],
'#global_types' => TRUE,
'#states' => [
'visible' => [
':input[name="' . $visibility_path . '[tokens]"]' => ['value' => 'advanced'],
],
],
];
}
else {
$form['advanced_tokens'] = [
'#type' => 'token_browser',
'#theme_wrappers' => ['fieldset'],
'#title' => $this->t('Advanced Tokens'),
'#token_types' => [$token_type],
'#recursion_limit' => $this->getSetting('advanced_tokens')['recursion_limit'],
'#recursion_limit_max' => 6,
'#global_types' => $this->getSetting('advanced_tokens')['global_types'],
'#show_settings' => TRUE,
'#states' => [
'visible' => [
':input[name="' . $visibility_path . '[tokens]"]' => ['value' => 'advanced'],
],
],
];
}
}
return $form;
}
/**
* {@inheritdoc}
*/
public function settingsSummary(): array {
$tokens_type = $this->getSetting('tokens');
$summary[] = $this->t('Tokens: @tokens', ['@tokens' => $tokens_type]);
if ($tokens_type === 'advanced') {
$summary[] = $this->t('Recursion limit: @limit', ['@limit' => $this->getSetting('advanced_tokens')['recursion_limit']]);
$summary[] = $this->t('Global types: @global', ['@global' => $this->getSetting('advanced_tokens')['global_types'] ? 'Yes' : 'No']);
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewValue(FieldItemInterface $item, string $langcode): array {
$replacements = [];
$entity = $item->getEntity();
$entity_type = $entity->getEntityTypeId();
$template = $this->getSetting('template');
$bubbleable_metadata = new BubbleableMetadata();
// Replace advanced tokens.
if ($this->getSetting('tokens') === 'advanced' && $this->moduleHandler->moduleExists('token')) {
$output = $this->tokenService->replace($template, [$entity_type => $entity], [
'clear' => TRUE,
'langcode' => $langcode,
], $bubbleable_metadata);
}
// Replace basic tokens.
else {
foreach ($this->getCustomFieldItems() as $name => $custom_item) {
$markup = $custom_item->value($item) ?? '';
$replacements["[$name]"] = $markup;
$replacements["[$name:label]"] = $custom_item->getLabel();
}
$output = strtr($template, $replacements);
}
$build['#markup'] = $output;
$bubbleable_metadata->applyTo($build);
return $build;
}
}
