diff --git a/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php index 0870262..6d81981 100644 --- a/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php +++ b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php @@ -76,6 +76,20 @@ class DefaultTableMapping implements TableMappingInterface { protected $allColumns = []; /** + * A list of table names per field name. + * + * @var array + */ + protected $fieldTableNames = []; + + /** + * A list of revision table names per field name. + * + * @var array + */ + protected $fieldRevisionTableNames = []; + + /** * Constructs a DefaultTableMapping. * * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type @@ -135,9 +149,7 @@ public function getFieldNames($table_name) { * {@inheritdoc} */ public function getFieldTableName($field_name) { - $result = NULL; - - if (isset($this->fieldStorageDefinitions[$field_name])) { + if (!isset($this->fieldTableNames[$field_name]) && isset($this->fieldStorageDefinitions[$field_name])) { // Since a field may be stored in more than one table, we inspect tables // in order of relevance: the data table if present is the main place // where field data is stored, otherwise the base table is responsible for @@ -148,35 +160,21 @@ public function getFieldTableName($field_name) { /** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $storage */ $storage = \Drupal::entityManager()->getStorage($this->entityType->id()); $storage_definition = $this->fieldStorageDefinitions[$field_name]; - $table_names = [ + $candidate_table_names = [ $storage->getDataTable(), $storage->getBaseTable(), $storage->getRevisionTable(), $this->getDedicatedDataTableName($storage_definition), ]; - // Collect field columns. - $field_columns = []; - foreach (array_keys($storage_definition->getColumns()) as $property_name) { - $field_columns[] = $this->getFieldColumnName($storage_definition, $property_name); - } - - foreach (array_filter($table_names) as $table_name) { - $columns = $this->getAllColumns($table_name); - // We assume finding one field column belonging to the mapping is enough - // to identify the field table. - if (array_intersect($columns, $field_columns)) { - $result = $table_name; - break; - } - } + $this->fieldTableNames[$field_name] = $this->identifyFieldTableName($storage_definition, $candidate_table_names); } - if (!isset($result)) { + if (!isset($this->fieldTableNames[$field_name])) { throw new SqlContentEntityStorageException("Table information not available for the '$field_name' field."); } - return $result; + return $this->fieldTableNames[$field_name]; } /** @@ -187,20 +185,17 @@ public function getFieldRevisionTableName($field_name) { return $this->getFieldTableName($field_name); } - $result = NULL; - - if (isset($this->fieldStorageDefinitions[$field_name])) { + if (!isset($this->fieldRevisionTableNames[$field_name]) && isset($this->fieldStorageDefinitions[$field_name])) { // Since a field may be stored in more than one table, we inspect tables // in order of relevance: the revision data table if present is the main - // place where field revision data is stored, otherwise the base table is - // responsible for storing field data. Revision metadata is an exception - // as it's stored only in the revision table. + // place where field revision data is stored, otherwise the revision table + // is responsible for storing field data. // @todo The table mapping itself should know about entity tables. See // https://www.drupal.org/node/2274017. /** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $storage */ $storage = \Drupal::entityTypeManager()->getStorage($this->entityType->id()); $storage_definition = $this->fieldStorageDefinitions[$field_name]; - $table_names = [ + $candidate_table_names = [ $storage->getRevisionDataTable(), $storage->getRevisionTable(), $storage->getDataTable(), @@ -208,27 +203,46 @@ public function getFieldRevisionTableName($field_name) { $this->getDedicatedRevisionTableName($storage_definition), ]; - // Collect field columns. - $field_columns = []; - foreach (array_keys($storage_definition->getColumns()) as $property_name) { - $field_columns[] = $this->getFieldColumnName($storage_definition, $property_name); - } - - foreach (array_filter($table_names) as $table_name) { - $columns = $this->getAllColumns($table_name); - // We assume finding one field column belonging to the mapping is enough - // to identify the field table. - if (array_intersect($columns, $field_columns)) { - $result = $table_name; - break; - } - } + $this->fieldRevisionTableNames[$field_name] = $this->identifyFieldTableName($storage_definition, $candidate_table_names); } - if (!isset($result)) { + if (!isset($this->fieldRevisionTableNames[$field_name])) { throw new SqlContentEntityStorageException("Revision table information not available for the '$field_name' field."); } + return $this->fieldRevisionTableNames[$field_name]; + } + + /** + * Finds the table name for a field from a list of candidates. + * + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition + * A field storage definition. + * @param string[] $table_name_candidates + * An array of possible table names. + * + * @return string|null + * The field table name, or NULL if no candidate matches. + */ + protected function identifyFieldTableName(FieldStorageDefinitionInterface $storage_definition, array $table_name_candidates) { + $result = NULL; + + // Collect field columns. + $field_columns = []; + foreach (array_keys($storage_definition->getColumns()) as $property_name) { + $field_columns[] = $this->getFieldColumnName($storage_definition, $property_name); + } + + foreach (array_filter($table_name_candidates) as $table_name) { + $columns = $this->getAllColumns($table_name); + // We assume finding one field column belonging to the mapping is enough + // to identify the field table. + if (array_intersect($columns, $field_columns)) { + $result = $table_name; + break; + } + } + return $result; } diff --git a/core/modules/views/views.views.inc b/core/modules/views/views.views.inc index dc1df2c..91fd289 100644 --- a/core/modules/views/views.views.inc +++ b/core/modules/views/views.views.inc @@ -338,16 +338,20 @@ function views_field_default_views_data(FieldStorageConfigInterface $field_stora // Description of the field tables. // @todo Generalize this code to make it work with any table layout. See // https://www.drupal.org/node/2079019. + // @todo Use TableMappingInterface::getFieldTableName() when + // https://www.drupal.org/project/drupal/issues/2705205 is fixed. $table_mapping = $storage->getTableMapping(); $field_tables = [ EntityStorageInterface::FIELD_LOAD_CURRENT => [ - 'table' => $table_mapping->getFieldTableName($field_storage->getName()), + 'table' => $table_mapping->getDedicatedDataTableName($field_storage), 'alias' => "{$entity_type_id}__{$field_name}", ], ]; + // @todo Use TableMappingInterface::getFieldRevisionTableName() when + // https://www.drupal.org/project/drupal/issues/2705205 is fixed. if ($supports_revisions) { $field_tables[EntityStorageInterface::FIELD_LOAD_REVISION] = [ - 'table' => $table_mapping->getFieldRevisionTableName($field_storage->getName()), + 'table' => $table_mapping->getDedicatedRevisionTableName($field_storage), 'alias' => "{$entity_type_id}_revision__{$field_name}", ]; } @@ -442,8 +446,10 @@ function views_field_default_views_data(FieldStorageConfigInterface $field_stora $table_alias = $field_tables[EntityStorageInterface::FIELD_LOAD_REVISION]['alias']; if ($entity_revision_data_table) { // Tell Views how to join to the revision table, via the data table. + // @todo Use TableMappingInterface::getFieldTableName() when + // https://www.drupal.org/project/drupal/issues/2705205 is fixed. $data[$table_alias]['table']['join'][$entity_revision_data_table] = [ - 'table' => $table_mapping->getFieldRevisionTableName($field_storage->getName()), + 'table' => $table_mapping->getDedicatedRevisionTableName($field_storage), 'left_field' => $entity_type->getKey('revision'), 'field' => 'revision_id', 'extra' => [ @@ -453,8 +459,10 @@ function views_field_default_views_data(FieldStorageConfigInterface $field_stora } else { // If there is no data table, just join directly. + // @todo Use TableMappingInterface::getFieldRevisionTableName() when + // https://www.drupal.org/project/drupal/issues/2705205 is fixed. $data[$table_alias]['table']['join'][$entity_revision_table] = [ - 'table' => $table_mapping->getFieldRevisionTableName($field_storage->getName()), + 'table' => $table_mapping->getDedicatedRevisionTableName($field_storage), 'left_field' => $entity_type->getKey('revision'), 'field' => 'revision_id', 'extra' => [