custom_field-1.0.x-dev/src/Hook/ViewsHooks.php
src/Hook/ViewsHooks.php
<?php
declare(strict_types=1);
namespace Drupal\custom_field\Hook;
use Drupal\Component\Utility\DeprecationHelper;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Sql\DefaultTableMapping;
use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
use Drupal\Core\Hook\Attribute\Hook;
use Drupal\custom_field\Plugin\CustomFieldFormatterManagerInterface;
use Drupal\custom_field\Plugin\CustomFieldTypeManagerInterface;
use Drupal\field\FieldStorageConfigInterface;
use Drupal\views\FieldViewsDataProvider;
/**
* Provides hooks related to config schemas.
*/
class ViewsHooks {
/**
* Constructs a ViewsHooks object.
*/
public function __construct(
protected EntityTypeManagerInterface $entityTypeManager,
protected CustomFieldTypeManagerInterface $customFieldTypeManager,
protected CustomFieldFormatterManagerInterface $customFieldFormatterManager,
protected ?FieldViewsDataProvider $fieldViewsDataProvider,
) {}
/**
* Implements hook_field_views_data().
*/
#[Hook('field_views_data')]
public function fieldViewsData(FieldStorageConfigInterface $field_storage): array {
$data = DeprecationHelper::backwardsCompatibleCall(
currentVersion: \Drupal::VERSION,
deprecatedVersion: '11.2.0',
currentCallable: fn() => $this->fieldViewsDataProvider->defaultFieldImplementation($field_storage),
deprecatedCallable: fn() => views_field_default_views_data($field_storage),
);
$separator = '__';
$entity_type_id = $field_storage->getTargetEntityTypeId();
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
$entity_type_storage = $this->entityTypeManager->getStorage($entity_type_id);
assert($entity_type_storage instanceof SqlEntityStorageInterface);
$table_mapping = $entity_type_storage->getTableMapping();
assert($table_mapping instanceof DefaultTableMapping);
$field_name = $field_storage->getName();
$custom_fields = $this->customFieldTypeManager->getCustomFieldItems($field_storage->getSettings());
$types_with_relationships = [
'file',
'image',
'entity_reference',
];
foreach ($data as $table_name => $table_data) {
foreach ($custom_fields as $name => $custom_field) {
$default_formatter = $custom_field->getDefaultFormatter();
$instance_options = $this->customFieldFormatterManager->createOptionsForInstance($custom_field, $default_formatter, [], 'default');
$instance = $this->customFieldFormatterManager->getInstance($instance_options);
// Build the list of additional fields to add to queries.
$subfield = $field_name . '_' . $name;
$add_fields = ['delta', 'langcode', 'bundle'];
$extra_columns = [];
if ($custom_field->getDataType() === 'image') {
$image_cols = ['alt', 'title', 'width', 'height'];
foreach ($image_cols as $col) {
$add_fields[] = $subfield . '__' . $col;
$extra_columns[$col] = t('@column', ['@column' => $col]);
}
}
elseif ($custom_field->getDataType() === 'link') {
$add_fields[] = $subfield . '__title';
$extra_columns['title'] = t('title');
}
elseif (in_array($custom_field->getDataType(), ['daterange', 'time_range'])) {
$add_fields[] = $subfield . '__end';
$add_fields[] = $subfield . '__duration';
$extra_columns['end'] = t('end');
$extra_columns['duration'] = t('duration');
}
$field = [
'id' => 'custom_field',
'field_name' => $field_name,
'property' => $name,
'entity_type' => $entity_type_id,
'default_formatter' => $default_formatter,
'default_formatter_settings' => $instance::defaultSettings(),
'additional fields' => $add_fields,
'extra columns' => $extra_columns,
];
$filter = [
'id' => 'standard',
];
$sort = [
'id' => 'standard',
];
$argument = [
'id' => 'standard',
];
$type = $custom_field->getDataType();
switch ($type) {
case 'boolean':
$filter['id'] = 'boolean';
break;
case 'string':
case 'string_long':
case 'telephone':
case 'email':
case 'color':
$filter['id'] = 'string';
$argument['id'] = 'string';
break;
case 'datetime':
case 'daterange':
$datetime_type = $custom_field->getDatetimeType();
$argument = [
'id' => 'custom_field_datetime',
'datetime_type' => $custom_field->getDatetimeType(),
];
$arguments = [
// Argument type => help text.
'year' => t('Date in the form of YYYY.'),
'month' => t('Date in the form of MM (01 - 12).'),
'day' => t('Date in the form of DD (01 - 31).'),
'week' => t('Date in the form of WW (01 - 53).'),
'year_month' => t('Date in the form of YYYYMM.'),
'full_date' => t('Date in the form of CCYYMMDD.'),
];
$filter['id'] = 'custom_field_datetime';
$filter['datetime_type'] = $datetime_type;
foreach ($arguments as $argument_type => $help_text) {
$data[$table_name][$subfield . '_' . $argument_type] = [
'title' => t('@label@column (@argument)', [
'@label' => $field_storage->getLabel(),
'@column' => ':' . $name,
'@argument' => $argument_type,
]),
'help' => $help_text,
'argument' => [
'field' => $subfield,
'id' => 'custom_field_datetime_' . $argument_type,
'entity_type' => $entity_type_id,
'field_name' => $field_name,
'datetime_type' => $datetime_type,
],
'group' => $entity_type->getLabel(),
];
}
$sort = [
'id' => 'custom_field_datetime',
'datetime_type' => $datetime_type,
];
$data[$table_name][$subfield . $separator . 'timezone']['field'] = [
'id' => 'standard',
'field_name' => $field_name,
'property' => $subfield . '__timezone',
];
$data[$table_name][$subfield . $separator . 'timezone']['filter']['id'] = 'string';
$data[$table_name][$subfield . $separator . 'timezone']['sort']['id'] = 'standard';
$data[$table_name][$subfield . $separator . 'timezone']['argument']['id'] = 'string';
// Daterange specific settings.
if ($type === 'daterange') {
$data[$table_name][$subfield . $separator . 'end']['filter'] = $filter;
$data[$table_name][$subfield . $separator . 'end']['sort'] = $sort;
$data[$table_name][$subfield . $separator . 'end']['argument'] = $argument;
$data[$table_name][$subfield . $separator . 'duration']['field'] = [
'id' => 'standard',
'field_name' => $field_name,
'property' => $subfield . '__duration',
];
$data[$table_name][$subfield . $separator . 'duration']['filter']['id'] = 'numeric';
$data[$table_name][$subfield . $separator . 'duration']['argument']['id'] = 'numeric';
}
break;
case 'integer':
case 'float':
case 'decimal':
case 'duration':
case 'file':
case 'image':
case 'time':
$filter['id'] = 'numeric';
$argument['id'] = 'numeric';
break;
case 'time_range':
$filter['id'] = 'numeric';
$argument['id'] = 'numeric';
$data[$table_name][$subfield . $separator . 'end']['filter']['id'] = 'numeric';
$data[$table_name][$subfield . $separator . 'end']['argument']['id'] = 'numeric';
$data[$table_name][$subfield . $separator . 'duration']['filter']['id'] = 'numeric';
$data[$table_name][$subfield . $separator . 'duration']['argument']['id'] = 'numeric';
break;
case 'map':
$filter = [];
$sort = [];
$argument = [];
break;
case 'entity_reference':
$target_entity_type_id = $custom_field->getTargetType();
$filter = [
'id' => 'custom_field_entity_reference',
'target_type' => $target_entity_type_id,
'allow empty' => TRUE,
'field_name' => $field_name,
'subfield' => $name,
];
// Use entity_target_id for argument to get entity label handling.
$argument = [
'id' => 'entity_target_id',
'target_entity_type_id' => $target_entity_type_id,
];
// Keep the sort as numeric/string based on the data type.
$data_type = $field_storage->getPropertyDefinition($name)
->getSetting('data_type');
$id_type = $data_type === 'integer' ? 'numeric' : 'string';
$sort['id'] = $id_type;
break;
}
$data[$table_name][$subfield]['field'] = $field;
$data[$table_name][$subfield]['filter'] = $filter;
$data[$table_name][$subfield]['sort'] = $sort;
$data[$table_name][$subfield]['argument'] = $argument;
// Build views relationships.
if (in_array($type, $types_with_relationships)) {
$target_entity_type_id = $custom_field->getTargetType();
$target_entity_type = $this->entityTypeManager->getDefinition($target_entity_type_id);
$target_base_table = $target_entity_type->getDataTable() ?: $target_entity_type->getBaseTable();
if ($target_entity_type instanceof ContentEntityTypeInterface) {
// Provide a relationship for the entity type with the entity
// reference field.
$args = [
'@label' => $target_entity_type->getLabel(),
'@field_name' => $field_name . '_' . $name,
];
$data[$table_name][$subfield]['relationship'] = [
'title' => t('@label referenced from @field_name', $args),
'label' => t('@field_name: @label', $args),
'group' => $entity_type->getLabel(),
'help' => t('Appears in: @bundles.', ['@bundles' => implode(', ', $field_storage->getBundles())]),
'id' => 'standard',
'base' => $target_base_table,
'entity type' => $target_entity_type_id,
'base field' => $target_entity_type->getKey('id'),
'relationship field' => $subfield,
];
// Provide a reverse relationship for the entity type that is
// referenced by the field.
$args['@entity'] = $entity_type->getLabel();
$args['@label'] = $target_entity_type->getSingularLabel();
$pseudo_field_name = 'reverse__' . $entity_type_id . '__' . $subfield;
$data[$target_base_table][$pseudo_field_name]['relationship'] = [
'title' => t('@entity using @field_name', $args),
'label' => t('@field_name', ['@field_name' => $field_name]),
'group' => $target_entity_type->getLabel(),
'help' => t('Relate each @entity with a @field_name set to the @label.', $args),
'id' => 'entity_reverse',
'base' => $entity_type->getDataTable() ?: $entity_type->getBaseTable(),
'entity_type' => $entity_type_id,
'base field' => $entity_type->getKey('id'),
'field_name' => $field_name,
'field table' => $table_mapping->getDedicatedDataTableName($field_storage),
'field field' => $subfield,
'join_extra' => [
[
'field' => 'deleted',
'value' => 0,
'numeric' => TRUE,
],
],
];
}
}
}
}
return $data;
}
}
