class EntityViewsData in Entity API 8
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.
Hierarchy
- class \Drupal\views\EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterface uses DeprecatedServicePropertyTrait, StringTranslationTrait
- class \Drupal\entity\EntityViewsData
Expanded class hierarchy of EntityViewsData
File
- src/
EntityViewsData.php, line 32
Namespace
Drupal\entityView source
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' => t('@label (@name:delta)', [
'@label' => $label,
'@name' => $field_name,
]),
'title short' => 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;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DeprecatedServicePropertyTrait:: |
public | function | Allows to access deprecated/removed properties. | |
EntityViewsData:: |
protected | property | ||
EntityViewsData:: |
protected | property | The entity field manager. | |
EntityViewsData:: |
protected | property | Entity type for this views data handler instance. | |
EntityViewsData:: |
protected | property | The entity type manager. | |
EntityViewsData:: |
protected | property | The field storage definitions for all base fields of the entity type. | |
EntityViewsData:: |
protected | property | The module handler. | |
EntityViewsData:: |
protected | property | The storage used for this entity type. | |
EntityViewsData:: |
protected | property | The table mapping. | |
EntityViewsData:: |
protected | property | The translation manager. | |
EntityViewsData:: |
protected | function | Adds views data for the given bundle field. | |
EntityViewsData:: |
protected | function | Sets the entity links in case corresponding link templates exist. | |
EntityViewsData:: |
protected | function | Adds reverse relationships for the base entity reference fields. | |
EntityViewsData:: |
public static | function |
Instantiates a new instance of this entity handler. Overrides EntityHandlerInterface:: |
|
EntityViewsData:: |
protected | function | Gets the entity field manager. | |
EntityViewsData:: |
protected | function | Gets the entity type bundle info. | |
EntityViewsData:: |
protected | function | Gets the entity type manager. | |
EntityViewsData:: |
protected | function | Gets the field storage definitions. | |
EntityViewsData:: |
public | function |
Returns views data for the entity type. Overrides EntityViewsData:: |
|
EntityViewsData:: |
public | function |
Gets the table of an entity type to be used as base table in views. Overrides EntityViewsDataInterface:: |
|
EntityViewsData:: |
protected | function |
Puts the views data for a single field onto the views data. Overrides EntityViewsData:: |
|
EntityViewsData:: |
protected | function | Provides the views data for a given data type and schema field. | |
EntityViewsData:: |
protected | function | Corrects the views data for datetime base fields. | |
EntityViewsData:: |
protected | function | Processes the views data for an entity reference field. | |
EntityViewsData:: |
protected | function | Processes the views data for a language field. | |
EntityViewsData:: |
protected | function | Corrects the views data for list_float base fields. | |
EntityViewsData:: |
protected | function | Corrects the views data for list_integer base fields. | |
EntityViewsData:: |
protected | function | Corrects the views data for list_string base fields. | |
EntityViewsData:: |
protected | function | Processes the views data for a text field with formatting. | |
EntityViewsData:: |
protected | function | Processes the views data for a UUID field. | |
EntityViewsData:: |
public | function | Constructs an EntityViewsData object. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |