function field_tokens in Token 8
Same name and namespace in other branches
- 7 token.tokens.inc \field_tokens()
Implements hook_tokens() on behalf of field.module.
File
- ./
token.tokens.inc, line 1677 - Token callbacks for the token module.
Code
function field_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
$replacements = [];
$langcode = isset($options['langcode']) ? $options['langcode'] : NULL;
// Entity tokens.
if ($type == 'entity' && !empty($data['entity_type']) && !empty($data['entity']) && !empty($data['token_type'])) {
/* @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $data['entity'];
if (!$entity instanceof ContentEntityInterface) {
return $replacements;
}
if (!isset($options['langcode'])) {
// Set the active language in $options, so that it is passed along.
$langcode = $options['langcode'] = $entity
->language()
->getId();
}
// Obtain the entity with the correct language.
$entity = \Drupal::service('entity.repository')
->getTranslationFromContext($entity, $langcode);
foreach ($tokens as $name => $original) {
// For the [entity:field_name] token.
if (strpos($name, ':') === FALSE) {
$field_name = $name;
$token_name = $name;
}
else {
list($field_name, $delta) = explode(':', $name, 2);
if (!is_numeric($delta)) {
unset($delta);
}
$token_name = $field_name;
}
// Ensure the entity has the requested field and that the token for it is
// defined by token.module.
if (!$entity
->hasField($field_name) || _token_module($data['token_type'], $token_name) != 'token') {
continue;
}
$display_options = 'token';
// Do not continue if the field is empty.
if ($entity
->get($field_name)
->isEmpty()) {
continue;
}
// Handle [entity:field_name] and [entity:field_name:0] tokens.
if ($field_name === $name || isset($delta)) {
$view_display = token_get_token_view_display($entity);
if (!$view_display) {
// We don't have the token view display and should fall back on
// default formatters. If the field has specified a specific formatter
// to be used by default with tokens, use that, otherwise use the
// default formatter.
/** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager */
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
$field_type_definition = $field_type_manager
->getDefinition($entity
->getFieldDefinition($field_name)
->getType());
$display_options = [
'type' => !empty($field_type_definition['default_token_formatter']) ? $field_type_definition['default_token_formatter'] : $field_type_definition['default_formatter'],
'label' => 'hidden',
];
}
// Render only one delta.
if (isset($delta)) {
if ($field_delta = $entity->{$field_name}[$delta]) {
$field_output = $field_delta
->view($display_options);
}
else {
continue;
}
}
else {
$field_output = $entity->{$field_name}
->view($display_options);
// If we are displaying all field items we need this #pre_render
// callback.
$field_output['#pre_render'][] = '\\Drupal\\token\\TokenFieldRender::preRender';
}
$field_output['#token_options'] = $options;
$replacements[$original] = \Drupal::service('renderer')
->renderPlain($field_output);
}
elseif ($field_tokens = \Drupal::token()
->findWithPrefix($tokens, $field_name)) {
// With multiple nested tokens for the same field name, this might
// match the same field multiple times. Filter out those that have
// already been replaced.
$field_tokens = array_filter($field_tokens, function ($token) use ($replacements) {
return !isset($replacements[$token]);
});
if ($field_tokens) {
$property_token_data = [
'field_property' => TRUE,
$data['entity_type'] . '-' . $field_name => $entity->{$field_name},
'field_name' => $data['entity_type'] . '-' . $field_name,
];
// The optimization above only works on the root level, if there is
// more than one nested field it would still replace all in every
// call. Replace them one by one instead, which is slightly slower
// but ensures that the number of replacements do not grow
// exponentialy.
foreach ($field_tokens as $field_token_key => $field_token_value) {
$replacements += \Drupal::token()
->generate($field_name, [
$field_token_key => $field_token_value,
], $property_token_data, $options, $bubbleable_metadata);
}
}
}
}
// Remove the cloned object from memory.
unset($entity);
}
elseif (!empty($data['field_property'])) {
foreach ($tokens as $token => $original) {
$filtered_tokens = $tokens;
$delta = 0;
$parts = explode(':', $token);
if (is_numeric($parts[0])) {
if (count($parts) > 1) {
$delta = $parts[0];
$property_name = $parts[1];
// Pre-filter the tokens to select those with the correct delta.
$filtered_tokens = \Drupal::token()
->findWithPrefix($tokens, $delta);
// Remove the delta to unify between having and not having one.
array_shift($parts);
}
else {
// Token is fieldname:delta, which is invalid.
continue;
}
}
else {
$property_name = $parts[0];
}
if (isset($data[$data['field_name']][$delta])) {
$field_item = $data[$data['field_name']][$delta];
}
else {
// The field has no such delta, abort replacement.
continue;
}
if (isset($field_item->{$property_name}) && $field_item->{$property_name} instanceof FieldableEntityInterface) {
// Entity reference field.
$entity = $field_item->{$property_name};
// Obtain the referenced entity with the correct language.
$entity = \Drupal::service('entity.repository')
->getTranslationFromContext($entity, $langcode);
if (count($parts) > 1) {
$field_tokens = \Drupal::token()
->findWithPrefix($filtered_tokens, $property_name);
$token_type = \Drupal::service('token.entity_mapper')
->getTokenTypeForEntityType($entity
->getEntityTypeId(), TRUE);
$replacements += \Drupal::token()
->generate($token_type, $field_tokens, [
$token_type => $entity,
], $options, $bubbleable_metadata);
}
else {
$replacements[$original] = $entity
->label();
}
}
elseif ($field_item
->getFieldDefinition()
->getType() == 'image' && ($style = ImageStyle::load($property_name))) {
// Handle [node:field_name:image_style:property] tokens and multivalued
// [node:field_name:delta:image_style:property] tokens. If the token is
// of the form [node:field_name:image_style], provide the URL as a
// replacement.
$property_name = isset($parts[1]) ? $parts[1] : 'url';
$entity = $field_item->entity;
if (!empty($field_item->entity)) {
$original_uri = $entity
->getFileUri();
// Only generate the image derivative if needed.
if ($property_name === 'width' || $property_name === 'height') {
$dimensions = [
'width' => $field_item->width,
'height' => $field_item->height,
];
$style
->transformDimensions($dimensions, $original_uri);
$replacements[$original] = $dimensions[$property_name];
}
elseif ($property_name === 'uri') {
$replacements[$original] = $style
->buildUri($original_uri);
}
elseif ($property_name === 'url') {
$replacements[$original] = $style
->buildUrl($original_uri);
}
else {
// Generate the image derivative, if it doesn't already exist.
$derivative_uri = $style
->buildUri($original_uri);
$derivative_exists = TRUE;
if (!file_exists($derivative_uri)) {
$derivative_exists = $style
->createDerivative($original_uri, $derivative_uri);
}
if ($derivative_exists) {
$image = \Drupal::service('image.factory')
->get($derivative_uri);
// Provide the replacement.
switch ($property_name) {
case 'mimetype':
$replacements[$original] = $image
->getMimeType();
break;
case 'filesize':
$replacements[$original] = $image
->getFileSize();
break;
}
}
}
}
}
elseif (in_array($field_item
->getFieldDefinition()
->getType(), [
'datetime',
'daterange',
'date_recur',
]) && in_array($property_name, [
'date',
'start_date',
'end_date',
]) && !empty($field_item->{$property_name})) {
$timestamp = $field_item->{$property_name}
->getTimestamp();
// If the token is an exact match for the property or the delta and the
// property, use the timestamp as-is.
if ($property_name == $token || "{$delta}:{$property_name}" == $token) {
$replacements[$original] = $timestamp;
}
else {
$date_tokens = \Drupal::token()
->findWithPrefix($filtered_tokens, $property_name);
$replacements += \Drupal::token()
->generate('date', $date_tokens, [
'date' => $timestamp,
], $options, $bubbleable_metadata);
}
}
elseif (in_array($field_item
->getFieldDefinition()
->getType(), [
'timestamp',
'created',
'changed',
]) && in_array($property_name, [
'date',
])) {
$timestamp = $field_item->value;
if ($property_name == $token || "{$delta}:{$property_name}" == $token) {
$replacements[$original] = $timestamp;
}
else {
$field_tokens = \Drupal::token()
->findWithPrefix($filtered_tokens, $property_name);
$replacements += \Drupal::token()
->generate('date', $field_tokens, [
'date' => $timestamp,
], $options, $bubbleable_metadata);
}
}
else {
$replacements[$original] = $field_item->{$property_name};
}
}
}
return $replacements;
}