
| Current Path : /var/www/html/store/web/modules/contrib/entity/src/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : /var/www/html/store/web/modules/contrib/entity/src/EntityViewsData.php |
<?php
namespace Drupal\entity;
use Drupal\Core\Entity\ContentEntityType;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\Sql\TableMappingInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\views\EntityViewsData as CoreEntityViewsData;
/**
* Provides improvements to core's generic views integration for entities.
*
* Contains special handling for the following base field types:
* - datetime
* - list_float, list_integer, list_string
* Workaround for core issue #2337515.
*
* Provides views data for bundle plugin fields,
* as a workaround for core issue #2898635.
*
* Provides the missing delta field for multi-value fields,
* as a workaround for core issue #3097568.
*
* Fixes handling of fields with multiple properties,
* as a workaround for core issue #3097636.
*
* Provides reverse relationships for base entity_reference fields,
* as a workaround for core issue #2706431.
*/
class EntityViewsData extends CoreEntityViewsData {
/**
* The table mapping.
*
* @var \Drupal\Core\Entity\Sql\DefaultTableMapping
*/
protected $tableMapping;
/**
* {@inheritdoc}
*/
public function getViewsData() {
$data = parent::getViewsData();
$this->tableMapping = $this->storage->getTableMapping();
$entity_type_id = $this->entityType->id();
// Workaround for core issue #3004300.
if ($this->entityType->isRevisionable()) {
$revision_table = $this->tableMapping->getRevisionTable();
$data[$revision_table]['table']['entity revision'] = TRUE;
}
// Add missing reverse relationships. Workaround for core issue #2706431.
$base_fields = $this->getEntityFieldManager()->getBaseFieldDefinitions($entity_type_id);
$entity_reference_fields = array_filter($base_fields, function (BaseFieldDefinition $field) {
return $field->getType() == 'entity_reference';
});
$this->addReverseRelationships($data, $entity_reference_fields);
// Add views integration for bundle plugin fields.
// Workaround for core issue #2898635.
if ($this->entityType->hasHandlerClass('bundle_plugin')) {
$bundles = $this->getEntityTypeBundleInfo()->getBundleInfo($entity_type_id);
foreach (array_keys($bundles) as $bundle) {
$field_definitions = $this->getEntityFieldManager()->getFieldDefinitions($entity_type_id, $bundle);
foreach ($field_definitions as $field_definition) {
if ($field_definition instanceof BundleFieldDefinition) {
$this->addBundleFieldData($data, $field_definition);
}
}
}
}
return $data;
}
/**
* Adds views data for the given bundle field.
*
* Based on views_field_default_views_data(), which is only invoked
* for configurable fields.
*
* Assumes that the bundle field is not shared between bundles, since
* the bundle plugin API doesn't support that.
*
* @param array $data
* The views data.
* @param \Drupal\entity\BundleFieldDefinition $bundle_field
* The bundle field.
*/
protected function addBundleFieldData(array &$data, BundleFieldDefinition $bundle_field) {
$field_name = $bundle_field->getName();
$entity_type_id = $this->entityType->id();
$base_table = $this->getViewsTableForEntityType($this->entityType);
$revision_table = '';
if ($this->entityType->isRevisionable()) {
$revision_table = $this->tableMapping->getRevisionDataTable();
if (!$revision_table) {
$revision_table = $this->tableMapping->getRevisionTable();
}
}
$field_tables = [];
$field_tables[EntityStorageInterface::FIELD_LOAD_CURRENT] = [
'table' => $this->tableMapping->getDedicatedDataTableName($bundle_field),
'alias' => "{$entity_type_id}__{$field_name}",
];
if ($this->entityType->isRevisionable()) {
$field_tables[EntityStorageInterface::FIELD_LOAD_REVISION] = [
'table' => $this->tableMapping->getDedicatedRevisionTableName($bundle_field),
'alias' => "{$entity_type_id}_revision__{$field_name}",
];
}
$table_alias = $field_tables[EntityStorageInterface::FIELD_LOAD_CURRENT]['alias'];
$data[$table_alias]['table']['group'] = $this->entityType->getLabel();
$data[$table_alias]['table']['join'][$base_table] = [
'table' => $this->tableMapping->getDedicatedDataTableName($bundle_field),
'left_field' => $this->entityType->getKey('id'),
'field' => 'entity_id',
'extra' => [
['field' => 'deleted', 'value' => 0, 'numeric' => TRUE],
],
];
if ($bundle_field->isTranslatable()) {
$data[$table_alias]['table']['join'][$base_table]['extra'][] = [
'left_field' => 'langcode',
'field' => 'langcode',
];
}
if ($this->entityType->isRevisionable()) {
$table_alias = $field_tables[EntityStorageInterface::FIELD_LOAD_REVISION]['alias'];
$data[$table_alias]['table']['group'] = $this->t('@group (historical data)', [
'@group' => $this->entityType->getLabel(),
]);
$data[$table_alias]['table']['join'][$revision_table] = [
'table' => $this->tableMapping->getDedicatedRevisionTableName($bundle_field),
'left_field' => $this->entityType->getKey('revision'),
'field' => 'revision_id',
'extra' => [
['field' => 'deleted', 'value' => 0, 'numeric' => TRUE],
],
];
if ($bundle_field->isTranslatable()) {
$data[$table_alias]['table']['join'][$revision_table]['extra'][] = [
'left_field' => 'langcode',
'field' => 'langcode',
];
}
}
foreach ($field_tables as $type => $table_info) {
$table_alias = $table_info['alias'];
$data[$table_alias]['table']['title'] = $bundle_field->getLabel();
$data[$table_alias]['table']['help'] = $bundle_field->getDescription();
$data[$table_alias]['table']['entity type'] = $this->entityType->id();
$data[$table_alias]['table']['provider'] = $this->entityType->getProvider();
$this->mapFieldDefinition($table_info['table'], $field_name, $bundle_field, $this->tableMapping, $data[$table_alias]);
}
}
/**
* {@inheritDoc}
*/
protected function mapFieldDefinition($table, $field_name, FieldDefinitionInterface $field_definition, TableMappingInterface $table_mapping, &$table_data) {
$field_column_mapping = $table_mapping->getColumnNames($field_name);
$field_storage = $this->getFieldStorageDefinitions()[$field_name];
$field_schema = $field_storage->getSchema();
$field_definition_type = $field_definition->getType();
// Add all properties to views table data. We need an entry for each
// column of each field, with the main one given special treatment.
$main_property = $field_storage->getMainPropertyName();
if (!$main_property) {
// The mapSingleFieldViewsData() method always expects a main property,
// so there must be a fallback to the first defined property.
// See #2337517 for the related core issue.
$property_names = array_keys($field_column_mapping);
$main_property = reset($property_names);
}
foreach ($field_column_mapping as $field_column_name => $schema_field_name) {
$first = ($main_property == $field_column_name);
$table_data[$schema_field_name] = $this->mapSingleFieldViewsData($table, $field_name, $field_definition_type, $field_column_name, $field_schema['columns'][$field_column_name]['type'], $first, $field_definition);
$table_data[$schema_field_name]['entity field'] = $field_name;
// By default core makes every property render the entire field, which
// confuses users. Fix that so only the property itself is rendered.
// Workaround for core issue #3097636.
if (!$first && $table_data[$schema_field_name]['field']['id'] == 'field') {
$table_data[$schema_field_name]['field']['id'] = 'standard';
}
// Many handlers (datetime, list) crash without the field_name defined.
foreach (['argument', 'filter', 'sort'] as $handler_type) {
if (isset($table_data[$schema_field_name][$handler_type])) {
$table_data[$schema_field_name][$handler_type]['field_name'] = $field_name;
}
}
}
// Expose additional delta column for multiple value fields.
// Workaround for core issue #3097568.
if ($field_storage->isMultiple()) {
$label = $field_definition->getLabel();
$table_data['delta'] = [
'title' => $this->t('@label (@name:delta)', ['@label' => $label, '@name' => $field_name]),
'title short' => $this->t('@label:delta', ['@label' => $label]),
];
$table_data['delta']['field'] = [
'id' => 'numeric',
];
$table_data['delta']['argument'] = [
'field' => 'delta',
'table' => $table,
'id' => 'numeric',
'empty field name' => $this->t('- No value -'),
'field_name' => $field_name,
'entity_type' => $this->entityType->id(),
];
$table_data['delta']['filter'] = [
'field' => 'delta',
'table' => $table,
'id' => 'numeric',
'field_name' => $field_name,
'entity_type' => $this->entityType->id(),
'allow empty' => TRUE,
];
$table_data['delta']['sort'] = [
'field' => 'delta',
'table' => $table,
'id' => 'standard',
'field_name' => $field_name,
'entity_type' => $this->entityType->id(),
];
}
}
/**
* Corrects the views data for datetime base fields.
*
* Based on datetime_field_views_data().
*
* @param string $table
* The table name.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition.
* @param array $views_field
* The views field data.
* @param string $field_column_name
* The field column being processed.
*/
protected function processViewsDataForDatetime($table, FieldDefinitionInterface $field_definition, array &$views_field, $field_column_name) {
if ($field_column_name == 'value') {
$views_field['filter']['id'] = 'datetime';
$views_field['argument']['id'] = 'datetime';
$views_field['sort']['id'] = 'datetime';
}
}
/**
* Corrects the views data for list_float base fields.
*
* Based on options_field_views_data().
*
* @param string $table
* The table name.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition.
* @param array $views_field
* The views field data.
* @param string $field_column_name
* The field column being processed.
*/
protected function processViewsDataForListFloat($table, FieldDefinitionInterface $field_definition, array &$views_field, $field_column_name) {
$this->processViewsDataForListInteger($table, $field_definition, $views_field, $field_column_name);
}
/**
* Corrects the views data for list_integer base fields.
*
* Based on options_field_views_data().
*
* @param string $table
* The table name.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition.
* @param array $views_field
* The views field data.
* @param string $field_column_name
* The field column being processed.
*/
protected function processViewsDataForListInteger($table, FieldDefinitionInterface $field_definition, array &$views_field, $field_column_name) {
if ($field_column_name == 'value') {
$views_field['filter']['id'] = 'list_field';
$views_field['argument']['id'] = 'number_list_field';
}
}
/**
* Corrects the views data for list_string base fields.
*
* Based on options_field_views_data().
*
* @param string $table
* The table name.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition.
* @param array $views_field
* The views field data.
* @param string $field_column_name
* The field column being processed.
*/
protected function processViewsDataForListString($table, FieldDefinitionInterface $field_definition, array &$views_field, $field_column_name) {
if ($field_column_name == 'value') {
$views_field['filter']['id'] = 'list_field';
$views_field['argument']['id'] = 'string_list_field';
}
}
/**
* Adds reverse relationships for the base entity reference fields.
*
* @param array $data
* The views data.
* @param \Drupal\Core\Field\BaseFieldDefinition[] $fields
* The entity reference fields.
*/
protected function addReverseRelationships(array &$data, array $fields) {
$entity_type_id = $this->entityType->id();
$base_table = $this->getViewsTableForEntityType($this->entityType);
assert($this->entityType instanceof ContentEntityType);
$revision_metadata_field_names = array_flip($this->entityType->getRevisionMetadataKeys());
foreach ($fields as $field) {
$target_entity_type_id = $field->getSettings()['target_type'];
$target_entity_type = $this->getEntityTypeManager()->getDefinition($target_entity_type_id);
if (!($target_entity_type instanceof ContentEntityType)) {
continue;
}
$target_table = $this->getViewsTableForEntityType($target_entity_type);
$field_name = $field->getName();
$field_storage = $field->getFieldStorageDefinition();
$args = [
'@label' => $target_entity_type->getSingularLabel(),
'@entity' => $this->entityType->getLabel(),
'@field_name' => $field_name,
];
$pseudo_field_name = 'reverse__' . $entity_type_id . '__' . $field_name;
$relationship_data = [
'label' => $this->entityType->getLabel(),
'group' => $target_entity_type->getLabel(),
'entity_type' => $entity_type_id,
];
if ($this->tableMapping->requiresDedicatedTableStorage($field_storage)) {
$data[$target_table][$pseudo_field_name]['relationship'] = [
'id' => 'entity_reverse',
'title' => $this->t('@entity using @field_name', $args),
'help' => $this->t('Relate each @entity with a @field_name field set to the @label.', $args),
'base' => $base_table,
'base field' => $this->entityType->getKey('id'),
'field_name' => $field_name,
'field table' => $this->tableMapping->getFieldTableName($field_name),
'field field' => $this->tableMapping->getFieldColumnName($field_storage, 'target_id'),
] + $relationship_data;
}
elseif (isset($revision_metadata_field_names[$field_name])) {
// Revision metadata fields exist only on the revision table, so the
// relationship has to be to that rather than to the base table.
$revision_table = $this->tableMapping->getRevisionTable();
$data[$target_table][$pseudo_field_name]['relationship'] = [
'id' => 'standard',
'title' => $this->t('@entity revision using @field_name', $args),
'help' => $this->t('Relate each @entity revision with a @field_name field set to the @label.', $args),
'base' => $revision_table,
'base field' => $this->tableMapping->getFieldColumnName($field_storage, 'target_id'),
'relationship field' => $target_entity_type->getKey('id'),
] + $relationship_data;
}
else {
// The data is on the base table.
$data[$target_table][$pseudo_field_name]['relationship'] = [
'id' => 'standard',
'title' => $this->t('@entity using @field_name', $args),
'help' => $this->t('Relate each @entity with a @field_name field set to the @label.', $args),
'base' => $base_table,
'base field' => $this->tableMapping->getFieldColumnName($field_storage, 'target_id'),
'relationship field' => $target_entity_type->getKey('id'),
] + $relationship_data;
}
}
}
/**
* Gets the entity field manager.
*
* @todo Remove when support for Drupal 8.7 is dropped.
*
* @return \Drupal\Core\Entity\EntityFieldManagerInterface
* The entity field manager.
*/
protected function getEntityFieldManager() {
if (!isset($this->entityFieldManager)) {
return \Drupal::service('entity_field.manager');
}
return $this->entityFieldManager;
}
/**
* Gets the entity type bundle info.
*
* @todo Remove when support for Drupal 8.7 is dropped.
*
* @return \Drupal\Core\Entity\EntityTypeBundleInfoInterface
* The entity type bundle info.
*/
protected function getEntityTypeBundleInfo() {
if (!isset($this->entityTypeBundleInfo)) {
return \Drupal::service('entity_type.bundle.info');
}
return $this->entityTypeBundleInfo;
}
/**
* Gets the entity type manager.
*
* @todo Remove when support for Drupal 8.7 is dropped.
*
* @return \Drupal\Core\Entity\EntityTypeManagerInterface
* The entity type manager.
*/
protected function getEntityTypeManager() {
if (!isset($this->entityTypeManager)) {
return \Drupal::entityTypeManager();
}
return $this->entityTypeManager;
}
}