diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php index 5391da2..8df8006 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php @@ -242,16 +242,17 @@ protected function schemaHandler() { /** * {@inheritdoc} */ - public function getTableMapping() { - if (!isset($this->tableMapping)) { - - $definitions = array_filter($this->getFieldStorageDefinitions(), function (FieldStorageDefinitionInterface $definition) { - // @todo Remove the check for FieldDefinitionInterface::isMultiple() when - // multiple-value base fields are supported in - // https://drupal.org/node/2248977. - return !$definition->hasCustomStorage() && !$definition->isMultiple(); + public function getTableMapping(array $storage_definitions = NULL) { + $table_mapping = $this->tableMapping; + + if (!isset($this->tableMapping) || $storage_definitions) { + $definitions = $storage_definitions ?: $this->entityManager->getFieldStorageDefinitions($this->entityTypeId); + $base_field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityTypeId); + $table_mapping = new DefaultTableMapping($definitions, $base_field_definitions); + + $definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use ($table_mapping) { + return $table_mapping->allowsSharedTableStorage($definition); }); - $this->tableMapping = new DefaultTableMapping($definitions); $key_fields = array_values(array_filter(array($this->idKey, $this->revisionKey, $this->bundleKey, $this->uuidKey, $this->langcodeKey))); $all_fields = array_keys($definitions); @@ -279,16 +280,16 @@ public function getTableMapping() { $translatable = $this->entityType->getDataTable() && $this->entityType->isTranslatable(); if (!$revisionable && !$translatable) { // The base layout stores all the base field values in the base table. - $this->tableMapping->setFieldNames($this->baseTable, $all_fields); + $table_mapping->setFieldNames($this->baseTable, $all_fields); } elseif ($revisionable && !$translatable) { // The revisionable layout stores all the base field values in the base // table, except for revision metadata fields. Revisionable fields // denormalized in the base table but also stored in the revision table // together with the entity ID and the revision ID as identifiers. - $this->tableMapping->setFieldNames($this->baseTable, array_diff($all_fields, $revision_metadata_fields)); + $table_mapping->setFieldNames($this->baseTable, array_diff($all_fields, $revision_metadata_fields)); $revision_key_fields = array($this->idKey, $this->revisionKey); - $this->tableMapping->setFieldNames($this->revisionTable, array_merge($revision_key_fields, $revisionable_fields)); + $table_mapping->setFieldNames($this->revisionTable, array_merge($revision_key_fields, $revisionable_fields)); } elseif (!$revisionable && $translatable) { // Multilingual layouts store key field values in the base table. The @@ -297,7 +298,7 @@ public function getTableMapping() { // denormalized copy of the bundle field value to allow for more // performant queries. This means that only the UUID is not stored on // the data table. - $this->tableMapping + $table_mapping ->setFieldNames($this->baseTable, $key_fields) ->setFieldNames($this->dataTable, array_values(array_diff($all_fields, array($this->uuidKey)))) // Add the denormalized 'default_langcode' field to the mapping. Its @@ -313,13 +314,13 @@ public function getTableMapping() { // holds the data field values for all non-revisionable fields. The data // field values of revisionable fields are denormalized in the data // table, as well. - $this->tableMapping->setFieldNames($this->baseTable, array_values(array_diff($key_fields, array($this->langcodeKey)))); + $table_mapping->setFieldNames($this->baseTable, array_values(array_diff($key_fields, array($this->langcodeKey)))); // Like in the multilingual, non-revisionable case the UUID is not // in the data table. Additionally, do not store revision metadata // fields in the data table. $data_fields = array_values(array_diff($all_fields, array($this->uuidKey), $revision_metadata_fields)); - $this->tableMapping + $table_mapping ->setFieldNames($this->dataTable, $data_fields) // Add the denormalized 'default_langcode' field to the mapping. Its // value is identical to the query expression @@ -328,20 +329,43 @@ public function getTableMapping() { ->setExtraColumns($this->dataTable, array('default_langcode')); $revision_base_fields = array_merge(array($this->idKey, $this->revisionKey, $this->langcodeKey), $revision_metadata_fields); - $this->tableMapping->setFieldNames($this->revisionTable, $revision_base_fields); + $table_mapping->setFieldNames($this->revisionTable, $revision_base_fields); $revision_data_key_fields = array($this->idKey, $this->revisionKey, $this->langcodeKey); - $revision_data_fields = array_diff($revisionable_fields, $revision_metadata_fields); - $this->tableMapping + $revision_data_fields = array_diff($revisionable_fields, $revision_metadata_fields, array($this->langcodeKey)); + $table_mapping ->setFieldNames($this->revisionDataTable, array_merge($revision_data_key_fields, $revision_data_fields)) // Add the denormalized 'default_langcode' field to the mapping. Its // value is identical to the query expression // "revision_table.langcode = data_table.langcode". ->setExtraColumns($this->revisionDataTable, array('default_langcode')); } + + // Add dedicated tables. + $definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use ($table_mapping) { + return $table_mapping->requiresDedicatedTableStorage($definition); + }); + $extra_columns = array( + 'bundle', + 'deleted', + 'entity_id', + 'revision_id', + 'langcode', + 'delta', + ); + foreach ($definitions as $field_name => $definition) { + foreach (array($table_mapping->getDedicatedDataTableName($definition), $table_mapping->getDedicatedRevisionTableName($definition)) as $table_name) { + $table_mapping->setFieldNames($table_name, array($field_name)); + $table_mapping->setExtraColumns($table_name, $extra_columns); + } + } + + if (!$storage_definitions) { + $this->tableMapping = $table_mapping; + } } - return $this->tableMapping; + return $table_mapping; } /** @@ -588,7 +612,7 @@ protected function attachPropertyData(array &$entities) { $table_mapping = $this->getTableMapping(); $translations = array(); if ($this->revisionDataTable) { - $data_fields = array_diff_key($table_mapping->getFieldNames($this->revisionDataTable), $table_mapping->getFieldNames($this->baseTable)); + $data_fields = array_diff($table_mapping->getFieldNames($this->revisionDataTable), $table_mapping->getFieldNames($this->baseTable)); } else { $data_fields = $table_mapping->getFieldNames($this->dataTable); @@ -1159,11 +1183,12 @@ protected function loadFieldItems(array $entities) { // Collect impacted fields. $storage_definitions = array(); $definitions = array(); + $table_mapping = $this->getTableMapping(); foreach ($bundles as $bundle => $v) { $definitions[$bundle] = $this->entityManager->getFieldDefinitions($this->entityTypeId, $bundle); foreach ($definitions[$bundle] as $field_name => $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); - if ($this->usesDedicatedTable($storage_definition)) { + if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) { $storage_definitions[$field_name] = $storage_definition; } } @@ -1172,7 +1197,7 @@ protected function loadFieldItems(array $entities) { // Load field data. $langcodes = array_keys(language_list(LanguageInterface::STATE_ALL)); foreach ($storage_definitions as $field_name => $storage_definition) { - $table = $load_current ? static::_fieldTableName($storage_definition) : static::_fieldRevisionTableName($storage_definition); + $table = $load_current ? $table_mapping->getDedicatedDataTableName($storage_definition) : $table_mapping->getDedicatedRevisionTableName($storage_definition); // Ensure that only values having valid languages are retrieved. Since we // are loading values for multiple entities, we cannot limit the query to @@ -1201,7 +1226,7 @@ protected function loadFieldItems(array $entities) { // For each column declared by the field, populate the item from the // prefixed database column. foreach ($storage_definition->getColumns() as $column => $attributes) { - $column_name = static::_fieldColumnName($storage_definition, $column); + $column_name = $table_mapping->getFieldColumnName($storage_definition, $column); // Unserialize the value if specified in the column schema. $item[$column] = (!empty($attributes['serialize'])) ? unserialize($row->$column_name) : $row->$column_name; } @@ -1230,6 +1255,7 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) { $entity_type = $entity->getEntityTypeId(); $default_langcode = $entity->getUntranslated()->language()->id; $translation_langcodes = array_keys($entity->getTranslationLanguages()); + $table_mapping = $this->getTableMapping(); if (!isset($vid)) { $vid = $id; @@ -1237,11 +1263,11 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) { foreach ($this->entityManager->getFieldDefinitions($entity_type, $bundle) as $field_name => $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); - if (!$this->usesDedicatedTable($storage_definition)) { + if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) { continue; } - $table_name = static::_fieldTableName($storage_definition); - $revision_name = static::_fieldRevisionTableName($storage_definition); + $table_name = $table_mapping->getDedicatedDataTableName($storage_definition); + $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition); // Delete and insert, rather than update, in case a value was added. if ($update) { @@ -1252,20 +1278,24 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) { ->condition('entity_id', $id) ->execute(); } - $this->database->delete($revision_name) - ->condition('entity_id', $id) - ->condition('revision_id', $vid) - ->execute(); + if ($this->entityType->isRevisionable()) { + $this->database->delete($revision_name) + ->condition('entity_id', $id) + ->condition('revision_id', $vid) + ->execute(); + } } // Prepare the multi-insert query. $do_insert = FALSE; $columns = array('entity_id', 'revision_id', 'bundle', 'delta', 'langcode'); foreach ($storage_definition->getColumns() as $column => $attributes) { - $columns[] = static::_fieldColumnName($storage_definition, $column); + $columns[] = $table_mapping->getFieldColumnName($storage_definition, $column); } $query = $this->database->insert($table_name)->fields($columns); - $revision_query = $this->database->insert($revision_name)->fields($columns); + if ($this->entityType->isRevisionable()) { + $revision_query = $this->database->insert($revision_name)->fields($columns); + } $langcodes = $field_definition->isTranslatable() ? $translation_langcodes : array($default_langcode); foreach ($langcodes as $langcode) { @@ -1283,12 +1313,14 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) { 'langcode' => $langcode, ); foreach ($storage_definition->getColumns() as $column => $attributes) { - $column_name = static::_fieldColumnName($storage_definition, $column); + $column_name = $table_mapping->getFieldColumnName($storage_definition, $column); // Serialize the value if specified in the column schema. $record[$column_name] = !empty($attributes['serialize']) ? serialize($item->$column) : $item->$column; } $query->values($record); - $revision_query->values($record); + if ($this->entityType->isRevisionable()) { + $revision_query->values($record); + } if ($storage_definition->getCardinality() != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && ++$delta_count == $storage_definition->getCardinality()) { break; @@ -1303,7 +1335,9 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) { if ($entity->isDefaultRevision()) { $query->execute(); } - $revision_query->execute(); + if ($this->entityType->isRevisionable()) { + $revision_query->execute(); + } } } } @@ -1315,19 +1349,22 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) { * The entity. */ protected function deleteFieldItems(EntityInterface $entity) { + $table_mapping = $this->getTableMapping(); foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); - if (!$this->usesDedicatedTable($storage_definition)) { + if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) { continue; } - $table_name = static::_fieldTableName($storage_definition); - $revision_name = static::_fieldRevisionTableName($storage_definition); + $table_name = $table_mapping->getDedicatedDataTableName($storage_definition); + $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition); $this->database->delete($table_name) ->condition('entity_id', $entity->id()) ->execute(); - $this->database->delete($revision_name) - ->condition('entity_id', $entity->id()) - ->execute(); + if ($this->entityType->isRevisionable()) { + $this->database->delete($revision_name) + ->condition('entity_id', $entity->id()) + ->execute(); + } } } @@ -1339,13 +1376,14 @@ protected function deleteFieldItems(EntityInterface $entity) { */ protected function deleteFieldItemsRevision(EntityInterface $entity) { $vid = $entity->getRevisionId(); - if (isset($vid)) { + if (isset($vid) && $this->entityType->isRevisionable()) { + $table_mapping = $this->getTableMapping(); foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); - if (!$this->usesDedicatedTable($storage_definition)) { + if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) { continue; } - $revision_name = static::_fieldRevisionTableName($storage_definition); + $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition); $this->database->delete($revision_name) ->condition('entity_id', $entity->id()) ->condition('revision_id', $vid) @@ -1355,21 +1393,6 @@ protected function deleteFieldItemsRevision(EntityInterface $entity) { } /** - * Returns whether the field uses a dedicated table for storage. - * - * @param FieldStorageDefinitionInterface $definition - * The field storage definition. - * - * @return bool - * Whether the field uses a dedicated table for storage. - */ - protected function usesDedicatedTable(FieldStorageDefinitionInterface $definition) { - // Everything that is not provided by the entity type is stored in a - // dedicated table. - return $definition->getProvider() != $this->entityType->getProvider() && !$definition->hasCustomStorage(); - } - - /** * {@inheritdoc} */ public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) { @@ -1425,8 +1448,9 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $ // There is data, so there are no column changes. Drop all the prior // indexes and create all the new ones, except for all the priors that // exist unchanged. - $table = static::_fieldTableName($original); - $revision_table = static::_fieldRevisionTableName($original); + $table_mapping = $this->getTableMapping(); + $table = $table_mapping->getDedicatedDataTableName($original); + $revision_table = $table_mapping->getDedicatedRevisionTableName($original); $schema = $storage_definition->getSchema(); $original_schema = $original->getSchema(); @@ -1438,8 +1462,8 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $ $this->database->schema()->dropIndex($revision_table, $real_name); } } - $table = static::_fieldTableName($storage_definition); - $revision_table = static::_fieldRevisionTableName($storage_definition); + $table = $table_mapping->getDedicatedDataTableName($storage_definition); + $revision_table = $table_mapping->getDedicatedRevisionTableName($storage_definition); foreach ($schema['indexes'] as $name => $columns) { if (!isset($original_schema['indexes'][$name]) || $columns != $original_schema['indexes'][$name]) { $real_name = static::_fieldIndexName($storage_definition, $name); @@ -1449,12 +1473,12 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $ // column name and length. Allow for either case. if (is_array($column_name)) { $real_columns[] = array( - static::_fieldColumnName($storage_definition, $column_name[0]), + $table_mapping->getFieldColumnName($storage_definition, $column_name[0]), $column_name[1], ); } else { - $real_columns[] = static::_fieldColumnName($storage_definition, $column_name); + $real_columns[] = $table_mapping->getFieldColumnName($storage_definition, $column_name); } } $this->database->schema()->addIndex($table, $real_name, $real_columns); @@ -1468,17 +1492,26 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $ * {@inheritdoc} */ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) { - // Mark all data associated with the field for deletion. - $table = static::_fieldTableName($storage_definition); - $revision_table = static::_fieldRevisionTableName($storage_definition); - $this->database->update($table) - ->fields(array('deleted' => 1)) - ->execute(); + $table_mapping = $this->getTableMapping(); + + if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) { + // Mark all data associated with the field for deletion. + $table = $table_mapping->getDedicatedDataTableName($storage_definition); + $revision_table = $table_mapping->getDedicatedRevisionTableName($storage_definition); + $this->database->update($table) + ->fields(array('deleted' => 1)) + ->execute(); + if ($this->entityType->isRevisionable()) { + $this->database->update($revision_table) + ->fields(array('deleted' => 1)) + ->execute(); + } + } // Move the table to a unique name while the table contents are being // deleted. - $new_table = static::_fieldTableName($storage_definition, TRUE); - $revision_new_table = static::_fieldRevisionTableName($storage_definition, TRUE); + $new_table = $table_mapping->getDedicatedDataTableName($storage_definition, TRUE); + $revision_new_table = $table_mapping->getDedicatedRevisionTableName($storage_definition, TRUE); $this->database->schema()->renameTable($table, $new_table); $this->database->schema()->renameTable($revision_table, $revision_new_table); } @@ -1487,19 +1520,23 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $ * {@inheritdoc} */ public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definition) { + $table_mapping = $this->getTableMapping(); $storage_definition = $field_definition->getFieldStorageDefinition(); - $table_name = static::_fieldTableName($storage_definition); - $revision_name = static::_fieldRevisionTableName($storage_definition); - // Mark field data as deleted. - $this->database->update($table_name) - ->fields(array('deleted' => 1)) - ->condition('bundle', $field_definition->getBundle()) - ->execute(); - $this->database->update($revision_name) - ->fields(array('deleted' => 1)) - ->condition('bundle', $field_definition->getBundle()) - ->execute(); + if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) { + $table_name = $table_mapping->getDedicatedDataTableName($storage_definition); + $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition); + $this->database->update($table_name) + ->fields(array('deleted' => 1)) + ->condition('bundle', $field_definition->getBundle()) + ->execute(); + if ($this->entityType->isRevisionable()) { + $this->database->update($revision_name) + ->fields(array('deleted' => 1)) + ->condition('bundle', $field_definition->getBundle()) + ->execute(); + } + } } /** @@ -1514,21 +1551,24 @@ public function onBundleRename($bundle, $bundle_new) { // @todo Use the unified store of deleted field definitions instead in // https://www.drupal.org/node/2282119 $field_definitions += entity_load_multiple_by_properties('field_instance_config', array('entity_type' => $this->entityTypeId, 'bundle' => $bundle, 'deleted' => TRUE, 'include_deleted' => TRUE)); + $table_mapping = $this->getTableMapping(); foreach ($field_definitions as $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); - if ($this->usesDedicatedTable($storage_definition)) { + if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) { $is_deleted = $this->storageDefinitionIsDeleted($storage_definition); - $table_name = static::_fieldTableName($storage_definition, $is_deleted); - $revision_name = static::_fieldRevisionTableName($storage_definition, $is_deleted); + $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted); + $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, $is_deleted); $this->database->update($table_name) ->fields(array('bundle' => $bundle_new)) ->condition('bundle', $bundle) ->execute(); - $this->database->update($revision_name) - ->fields(array('bundle' => $bundle_new)) - ->condition('bundle', $bundle) - ->execute(); + if ($this->entityType->isRevisionable()) { + $this->database->update($revision_name) + ->fields(array('bundle' => $bundle_new)) + ->condition('bundle', $bundle) + ->execute(); + } } } } @@ -1541,13 +1581,14 @@ protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definit // bundle fields. $storage_definition = $field_definition->getFieldStorageDefinition(); $is_deleted = $this->storageDefinitionIsDeleted($storage_definition); - $table_name = static::_fieldTableName($storage_definition, $is_deleted); + $table_mapping = $this->getTableMapping(); + $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted); // Get the entities which we want to purge first. $entity_query = $this->database->select($table_name, 't', array('fetch' => \PDO::FETCH_ASSOC)); $or = $entity_query->orConditionGroup(); foreach ($storage_definition->getColumns() as $column_name => $data) { - $or->isNotNull(static::_fieldColumnName($storage_definition, $column_name)); + $or->isNotNull($table_mapping->getFieldColumnName($storage_definition, $column_name)); } $entity_query ->distinct(TRUE) @@ -1558,7 +1599,7 @@ protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definit // Create a map of field data table column names to field column names. $column_map = array(); foreach ($storage_definition->getColumns() as $column_name => $data) { - $column_map[static::_fieldColumnName($storage_definition, $column_name)] = $column_name; + $column_map[$table_mapping->getFieldColumnName($storage_definition, $column_name)] = $column_name; } $entities = array(); @@ -1598,23 +1639,27 @@ protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definit protected function purgeFieldItems(ContentEntityInterface $entity, FieldDefinitionInterface $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); $is_deleted = $this->storageDefinitionIsDeleted($storage_definition); - $table_name = static::_fieldTableName($storage_definition, $is_deleted); - $revision_name = static::_fieldRevisionTableName($storage_definition, $is_deleted); + $table_mapping = $this->getTableMapping(); + $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted); + $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, $is_deleted); $revision_id = $this->entityType->isRevisionable() ? $entity->getRevisionId() : $entity->id(); $this->database->delete($table_name) ->condition('revision_id', $revision_id) ->execute(); - $this->database->delete($revision_name) - ->condition('revision_id', $revision_id) - ->execute(); + if ($this->entityType->isRevisionable()) { + $this->database->delete($revision_name) + ->condition('revision_id', $revision_id) + ->execute(); + } } /** * {@inheritdoc} */ public function finalizePurge(FieldStorageDefinitionInterface $storage_definition) { - $table_name = static::_fieldTableName($storage_definition, TRUE); - $revision_name = static::_fieldRevisionTableName($storage_definition, TRUE); + $table_mapping = $this->getTableMapping(); + $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, TRUE); + $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, TRUE); $this->database->schema()->dropTable($table_name); $this->database->schema()->dropTable($revision_name); } @@ -1623,24 +1668,54 @@ public function finalizePurge(FieldStorageDefinitionInterface $storage_definitio * {@inheritdoc} */ public function countFieldData($storage_definition, $as_bool = FALSE) { - $is_deleted = $this->storageDefinitionIsDeleted($storage_definition); - $table_name = static::_fieldTableName($storage_definition, $is_deleted); + $table_mapping = $this->getTableMapping(); - $query = $this->database->select($table_name, 't'); - $or = $query->orConditionGroup(); - foreach ($storage_definition->getColumns() as $column_name => $data) { - $or->isNotNull(static::_fieldColumnName($storage_definition, $column_name)); + if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) { + $is_deleted = $this->storageDefinitionIsDeleted($storage_definition); + $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted); + $query = $this->database->select($table_name, 't'); + $or = $query->orConditionGroup(); + foreach ($storage_definition->getColumns() as $column_name => $data) { + $or->isNotNull($table_mapping->getFieldColumnName($storage_definition, $column_name)); + } + $query + ->condition($or) + ->fields('t', array('entity_id')) + ->distinct(TRUE); + // If we are performing the query just to check if the field has data + // limit the number of rows. + if ($as_bool) { + $query->range(0, 1); + } + $count = $query->countQuery()->execute()->fetchField(); } - $query - ->condition($or) - ->fields('t', array('entity_id')) - ->distinct(TRUE); - // If we are performing the query just to check if the field has data - // limit the number of rows. - if ($as_bool) { - $query->range(0, 1); + else { + if ($as_bool) { + $count = $this->hasData(); + } + else { + $data_table = $this->dataTable ?: $this->baseTable; + $query = $this->database->select($data_table, 't'); + $columns = $storage_definition->getColumns(); + if (count($columns) > 1) { + $or = $query->orConditionGroup(); + foreach ($columns as $column_name => $data) { + $or->isNotNull($table_mapping->getFieldColumnName($storage_definition, $column_name)); + } + $query->condition($or); + } + else { + $query->isNotNull($storage_definition->getName()); + } + $count = $query + ->fields('t', array($this->idKey)) + ->distinct(TRUE) + ->countQuery() + ->execute() + ->fetchField(); + } } - $count = $query->countQuery()->execute()->fetchField(); + return $as_bool ? (bool) $count : (int) $count; } @@ -1678,7 +1753,9 @@ protected function storageDefinitionIsDeleted(FieldStorageDefinitionInterface $s * * @see hook_schema() */ - public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_definition, array $schema = NULL, $deleted = FALSE) { + protected function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_definition, array $schema = NULL, $deleted = FALSE) { + $table_mapping = $this->getTableMapping(); + $description_current = "Data storage for {$storage_definition->getTargetEntityTypeId()} field {$storage_definition->getName()}."; $description_revision = "Revision archive storage for {$storage_definition->getTargetEntityTypeId()} field {$storage_definition->getName()}."; @@ -1775,7 +1852,7 @@ public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_ // Add field columns. foreach ($schema['columns'] as $column_name => $attributes) { - $real_name = static::_fieldColumnName($storage_definition, $column_name); + $real_name = $table_mapping->getFieldColumnName($storage_definition, $column_name); $current['fields'][$real_name] = $attributes; } @@ -1783,7 +1860,7 @@ public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_ foreach ($schema['unique keys'] as $unique_key_name => $columns) { $real_name = static::_fieldIndexName($storage_definition, $unique_key_name); foreach ($columns as $column_name) { - $current['unique keys'][$real_name][] = static::_fieldColumnName($storage_definition, $column_name); + $current['unique keys'][$real_name][] = $table_mapping->getFieldColumnName($storage_definition, $column_name); } } @@ -1795,12 +1872,12 @@ public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_ // column name and length. Allow for either case. if (is_array($column_name)) { $current['indexes'][$real_name][] = array( - static::_fieldColumnName($storage_definition, $column_name[0]), + $table_mapping->getFieldColumnName($storage_definition, $column_name[0]), $column_name[1], ); } else { - $current['indexes'][$real_name][] = static::_fieldColumnName($storage_definition, $column_name); + $current['indexes'][$real_name][] = $table_mapping->getFieldColumnName($storage_definition, $column_name); } } } @@ -1810,7 +1887,7 @@ public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_ $real_name = static::_fieldIndexName($storage_definition, $specifier); $current['foreign keys'][$real_name]['table'] = $specification['table']; foreach ($specification['columns'] as $column_name => $referenced) { - $sql_storage_column = static::_fieldColumnName($storage_definition, $column_name); + $sql_storage_column = $table_mapping->getFieldColumnName($storage_definition, $column_name); $current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced; } } @@ -1823,109 +1900,12 @@ public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_ $revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to'; return array( - static::_fieldTableName($storage_definition) => $current, - static::_fieldRevisionTableName($storage_definition) => $revision, + $table_mapping->getDedicatedDataTableName($storage_definition) => $current, + $table_mapping->getDedicatedRevisionTableName($storage_definition) => $revision, ); } /** - * Generates a table name for a field data table. - * - * @private Calling this function circumvents the entity system and is - * strongly discouraged. This function is not considered part of the public - * API and modules relying on it might break even in minor releases. Only - * call this function to write a query that \Drupal::entityQuery() does not - * support. Always call entity_load() before using the data found in the - * table. - * - * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition - * The field storage definition. - * @param bool $is_deleted - * (optional) Whether the table name holding the values of a deleted field - * should be returned. - * - * @return string - * A string containing the generated name for the database table. - */ - public static function _fieldTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) { - if ($is_deleted) { - // When a field is a deleted, the table is renamed to - // {field_deleted_data_FIELD_UUID}. To make sure we don't end up with - // table names longer than 64 characters, we hash the unique storage - // identifier and return the first 10 characters so we end up with a short - // unique ID. - return "field_deleted_data_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10); - } - else { - return static::_generateFieldTableName($storage_definition, FALSE); - } - } - - /** - * Generates a table name for a field revision archive table. - * - * @private Calling this function circumvents the entity system and is - * strongly discouraged. This function is not considered part of the public - * API and modules relying on it might break even in minor releases. Only - * call this function to write a query that \Drupal::entityQuery() does not - * support. Always call entity_load() before using the data found in the - * table. - * - * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition - * The field storage definition. - * @param bool $is_deleted - * (optional) Whether the table name holding the values of a deleted field - * should be returned. - * - * @return string - * A string containing the generated name for the database table. - */ - public static function _fieldRevisionTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) { - if ($is_deleted) { - // When a field is a deleted, the table is renamed to - // {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with - // table names longer than 64 characters, we hash the unique storage - // identifier and return the first 10 characters so we end up with a short - // unique ID. - return "field_deleted_revision_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10); - } - else { - return static::_generateFieldTableName($storage_definition, TRUE); - } - } - - /** - * Generates a safe and unanbiguous field table name. - * - * The method accounts for a maximum table name length of 64 characters, and - * takes care of disambiguation. - * - * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition - * The field storage definition. - * @param bool $revision - * TRUE for revision table, FALSE otherwise. - * - * @return string - * The final table name. - */ - protected static function _generateFieldTableName(FieldStorageDefinitionInterface $storage_definition, $revision) { - $separator = $revision ? '_revision__' : '__'; - $table_name = $storage_definition->getTargetEntityTypeId() . $separator . $storage_definition->getName(); - // Limit the string to 48 characters, keeping a 16 characters margin for db - // prefixes. - if (strlen($table_name) > 48) { - // Use a shorter separator, a truncated entity_type, and a hash of the - // field UUID. - $separator = $revision ? '_r__' : '__'; - // Truncate to the same length for the current and revision tables. - $entity_type = substr($storage_definition->getTargetEntityTypeId(), 0, 34); - $field_hash = substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10); - $table_name = $entity_type . $separator . $field_hash; - } - return $table_name; - } - - /** * Generates an index name for a field data table. * * @private Calling this function circumvents the entity system and is @@ -1941,31 +1921,8 @@ protected static function _generateFieldTableName(FieldStorageDefinitionInterfac * A string containing a generated index name for a field data table that is * unique among all other fields. */ - public static function _fieldIndexName(FieldStorageDefinitionInterface $storage_definition, $index) { + protected static function _fieldIndexName(FieldStorageDefinitionInterface $storage_definition, $index) { return $storage_definition->getName() . '_' . $index; } - /** - * Generates a column name for a field data table. - * - * @private Calling this function circumvents the entity system and is - * strongly discouraged. This function is not considered part of the public - * API and modules relying on it might break even in minor releases. Only - * call this function to write a query that \Drupal::entityQuery() does not - * support. Always call entity_load() before using the data found in the - * table. - * - * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition - * The field storage definition. - * @param string $column - * The name of the column. - * - * @return string - * A string containing a generated column name for a field data table that is - * unique among all other fields. - */ - public static function _fieldColumnName(FieldStorageDefinitionInterface $storage_definition, $column) { - return in_array($column, FieldStorageConfig::getReservedColumns()) ? $column : $storage_definition->getName() . '_' . $column; - } - } diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php index 23ce020..c2bc6d8 100644 --- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php @@ -10,10 +10,8 @@ use Drupal\Core\Database\Query\SelectInterface; use Drupal\Core\Entity\ContentEntityTypeInterface; use Drupal\Core\Entity\EntityStorageInterface; -use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\Core\Entity\Query\QueryException; use Drupal\Core\Entity\Sql\SqlEntityStorageInterface; -use Drupal\field\Entity\FieldStorageConfig; use Drupal\field\FieldStorageConfigInterface; /** @@ -116,11 +114,14 @@ public function addField($field, $type, $langcode) { if ($field_storage instanceof FieldStorageConfigInterface) { // Find the field column. $column = $field_storage->getMainPropertyName(); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = $this->entityManager->getStorage($entity_type_id)->getTableMapping(); + if ($key < $count) { $next = $specifiers[$key + 1]; // Is this a field column? $columns = $field_storage->getColumns(); - if (isset($columns[$next]) || in_array($next, FieldStorageConfig::getReservedColumns())) { + if (isset($columns[$next]) || in_array($next, $table_mapping->getReservedColumns())) { // Use it. $column = $next; // Do not process it again. @@ -142,7 +143,7 @@ public function addField($field, $type, $langcode) { } } $table = $this->ensureFieldTable($index_prefix, $field_storage, $type, $langcode, $base_table, $entity_id_field, $field_id_field); - $sql_column = ContentEntityDatabaseStorage::_fieldColumnName($field_storage, $column); + $sql_column = $table_mapping->getFieldColumnName($field_storage, $column); } // This is an entity base field (non-configurable field). else { @@ -220,11 +221,13 @@ protected function ensureEntityTable($index_prefix, $property, $type, $langcode, protected function ensureFieldTable($index_prefix, &$field, $type, $langcode, $base_table, $entity_id_field, $field_id_field) { $field_name = $field->getName(); if (!isset($this->fieldTables[$index_prefix . $field_name])) { - $table = $this->sqlQuery->getMetaData('age') == EntityStorageInterface::FIELD_LOAD_CURRENT ? ContentEntityDatabaseStorage::_fieldTableName($field) : ContentEntityDatabaseStorage::_fieldRevisionTableName($field); + $entity_type_id = $this->sqlQuery->getMetaData('entity_type'); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = $this->entityManager->getStorage($entity_type_id)->getTableMapping(); + $table = $this->sqlQuery->getMetaData('age') == EntityStorageInterface::FIELD_LOAD_CURRENT ? $table_mapping->getDedicatedDataTableName($field) : $table_mapping->getDedicatedRevisionTableName($field); if ($field->getCardinality() != 1) { $this->sqlQuery->addMetaData('simple_query', FALSE); } - $entity_type = $this->sqlQuery->getMetaData('entity_type'); $this->fieldTables[$index_prefix . $field_name] = $this->addJoin($type, $table, "%alias.$field_id_field = $base_table.$entity_id_field", $langcode); } return $this->fieldTables[$index_prefix . $field_name]; diff --git a/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php index e872d57..f950887 100644 --- a/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php +++ b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php @@ -7,19 +7,28 @@ namespace Drupal\Core\Entity\Sql; +use Drupal\Core\Field\FieldStorageDefinitionInterface; + /** * Defines a default table mapping class. */ -class DefaultTableMapping implements TableMappingInterface { +class DefaultTableMapping implements DefaultTableMappingInterface { /** - * A list of field storage definitions that are available for this mapping. + * The field storage definitions of this mapping. * * @var \Drupal\Core\Field\FieldStorageDefinitionInterface[] */ protected $fieldStorageDefinitions = array(); /** + * The base field definitions of this mapping. + * + * @var \Drupal\Core\Field\FieldDefinitionInterface[] + */ + protected $baseFieldDefinitions = array(); + + /** * A list of field names per table. * * This corresponds to the return value of @@ -74,11 +83,13 @@ class DefaultTableMapping implements TableMappingInterface { * Constructs a DefaultTableMapping. * * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $storage_definitions - * A list of field storage definitions that should be available for the - * field columns of this table mapping. + * The field storage definitions for which to create the table mapping. + * @param \Drupal\Core\Field\FieldDefinitionInterface[] $base_field_definitions + * The base field definitions for which to create the table mapping. */ - public function __construct(array $storage_definitions) { + public function __construct(array $storage_definitions, array $base_field_definitions) { $this->fieldStorageDefinitions = $storage_definitions; + $this->baseFieldDefinitions = $base_field_definitions; } /** @@ -99,7 +110,16 @@ public function getAllColumns($table_name) { $this->allColumns[$table_name] = array_merge($this->allColumns[$table_name], array_values($this->getColumnNames($field_name))); } - $this->allColumns[$table_name] = array_merge($this->allColumns[$table_name], $this->getExtraColumns($table_name)); + // There is just one field for each dedicated storage table, thus + // $field_name can only refer to it. + if (isset($field_name) && $this->requiresDedicatedTableStorage($this->fieldStorageDefinitions[$field_name])) { + // Unlike in shared storage tables, in dedicated ones field columns are + // positioned last. + $this->allColumns[$table_name] = array_merge($this->getExtraColumns($table_name), $this->allColumns[$table_name]); + } + else { + $this->allColumns[$table_name] = array_merge($this->allColumns[$table_name], $this->getExtraColumns($table_name)); + } } return $this->allColumns[$table_name]; } @@ -177,4 +197,109 @@ public function setExtraColumns($table_name, array $column_names) { return $this; } + /** + * {@inheritdoc} + */ + public function allowsSharedTableStorage(FieldStorageDefinitionInterface $storage_definition) { + return !$storage_definition->hasCustomStorage() && isset($this->baseFieldDefinitions[$storage_definition->getName()]) && !$storage_definition->isMultiple(); + } + + /** + * {@inheritdoc} + */ + public function requiresDedicatedTableStorage(FieldStorageDefinitionInterface $storage_definition) { + return !$storage_definition->hasCustomStorage() && !$this->allowsSharedTableStorage($storage_definition); + } + + /** + * {@inheritdoc} + */ + public function getDedicatedTableNames() { + $table_mapping = $this; + $definitions = array_filter($this->fieldStorageDefinitions, function($definition) use ($table_mapping) { return $table_mapping->requiresDedicatedTableStorage($definition); }); + $data_tables = array_map(function($definition) use ($table_mapping) { return $table_mapping->getDedicatedDataTableName($definition); }, $definitions); + $revision_tables = array_map(function($definition) use ($table_mapping) { return $table_mapping->getDedicatedRevisionTableName($definition); }, $definitions); + $dedicated_tables = array_merge(array_values($data_tables), array_values($revision_tables)); + return $dedicated_tables; + } + + /** + * {@inheritdoc} + */ + public function getReservedColumns() { + return array('deleted'); + } + + /** + * {@inheritdoc} + */ + public function getDedicatedDataTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) { + if ($is_deleted) { + // When a field is a deleted, the table is renamed to + // {field_deleted_data_FIELD_UUID}. To make sure we don't end up with + // table names longer than 64 characters, we hash the unique storage + // identifier and return the first 10 characters so we end up with a short + // unique ID. + return "field_deleted_data_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10); + } + else { + return $this->generateFieldTableName($storage_definition, FALSE); + } + } + + /** + * {@inheritdoc} + */ + public function getDedicatedRevisionTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) { + if ($is_deleted) { + // When a field is a deleted, the table is renamed to + // {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with + // table names longer than 64 characters, we hash the unique storage + // identifier and return the first 10 characters so we end up with a short + // unique ID. + return "field_deleted_revision_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10); + } + else { + return $this->generateFieldTableName($storage_definition, TRUE); + } + } + + /** + * Generates a safe and unambiguous field table name. + * + * The method accounts for a maximum table name length of 64 characters, and + * takes care of disambiguation. + * + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition + * The field storage definition. + * @param bool $revision + * TRUE for revision table, FALSE otherwise. + * + * @return string + * The final table name. + */ + protected function generateFieldTableName(FieldStorageDefinitionInterface $storage_definition, $revision) { + $separator = $revision ? '_revision__' : '__'; + $table_name = $storage_definition->getTargetEntityTypeId() . $separator . $storage_definition->getName(); + // Limit the string to 48 characters, keeping a 16 characters margin for db + // prefixes. + if (strlen($table_name) > 48) { + // Use a shorter separator, a truncated entity_type, and a hash of the + // field UUID. + $separator = $revision ? '_r__' : '__'; + // Truncate to the same length for the current and revision tables. + $entity_type = substr($storage_definition->getTargetEntityTypeId(), 0, 34); + $field_hash = substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10); + $table_name = $entity_type . $separator . $field_hash; + } + return $table_name; + } + + /** + * {@inheritdoc} + */ + public function getFieldColumnName(FieldStorageDefinitionInterface $storage_definition, $column) { + return in_array($column, $this->getReservedColumns()) ? $column : $storage_definition->getName() . '_' . $column; + } + } diff --git a/core/lib/Drupal/Core/Entity/Sql/DefaultTableMappingInterface.php b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMappingInterface.php new file mode 100644 index 0000000..26b58a1 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMappingInterface.php @@ -0,0 +1,118 @@ +field_storage); + $table_name = 'contact_message__' . $this->field_storage->getName(); $data = $this->container->get('views.views_data')->get($table_name); $this->assertFalse($data, 'The field is not exposed to Views.'); } diff --git a/core/modules/entity_reference/entity_reference.views.inc b/core/modules/entity_reference/entity_reference.views.inc index e35884b..4580bce 100644 --- a/core/modules/entity_reference/entity_reference.views.inc +++ b/core/modules/entity_reference/entity_reference.views.inc @@ -5,7 +5,6 @@ * Provides views data for the entity_reference module. */ -use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\field\FieldStorageConfigInterface; /** @@ -14,6 +13,7 @@ function entity_reference_field_views_data(FieldStorageConfigInterface $field_storage) { $data = field_views_field_default_views_data($field_storage); $entity_manager = \Drupal::entityManager(); + $table_mapping = $entity_manager->getStorage($field_storage->getTargetEntityTypeId())->getTableMapping(); foreach ($data as $table_name => $table_data) { // Add a relationship to the target entity type. $target_entity_type_id = $field_storage->getSetting('target_type'); @@ -38,13 +38,16 @@ function entity_reference_field_views_data(FieldStorageConfigInterface $field_st // Provide a reverse relationship for the entity type that is referenced by // the field. - $pseudo_field_name = 'reverse__' . $field_storage->getTargetEntityTypeId() . '__' . $field_storage->getName(); + $entity_type_id = $field_storage->getTargetEntityTypeId(); + $pseudo_field_name = 'reverse__' . $entity_type_id . '__' . $field_storage->getName(); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = \Drupal::entityManager()->getStorage($entity_type_id)->getTableMapping(); $data[$target_base_table][$pseudo_field_name]['relationship'] = array( 'title' => t('@label using @field_name', $args), 'help' => t('Relate each @label with a @field_name.', $args), 'id' => 'entity_reverse', 'field_name' => $field_storage->getName(), - 'field table' => ContentEntityDatabaseStorage::_fieldTableName($field_storage), + 'field table' => $table_mapping->getDedicatedDataTableName($field_storage), 'field field' => $field_storage->getName() . '_target_id', 'base' => $target_entity_type->getBaseTable(), 'base field' => $target_entity_type->getKey('id'), diff --git a/core/modules/field/field.views.inc b/core/modules/field/field.views.inc index 3f68419..95e360e 100644 --- a/core/modules/field/field.views.inc +++ b/core/modules/field/field.views.inc @@ -22,7 +22,7 @@ function field_views_data() { $module_handler = \Drupal::moduleHandler(); foreach (\Drupal::entityManager()->getStorage('field_storage_config')->loadMultiple() as $field) { - if (_field_views_is_sql_entity_type($field)) { + if (_field_views_get_entity_type_storage($field)) { $result = (array) $module_handler->invoke($field->module, 'field_views_data', array($field)); if (empty($result)) { $result = field_views_field_default_views_data($field); @@ -48,7 +48,7 @@ function field_views_data() { */ function field_views_data_alter(&$data) { foreach (\Drupal::entityManager()->getStorage('field_storage_config')->loadMultiple() as $field) { - if (_field_views_is_sql_entity_type($field)) { + if (_field_views_get_entity_type_storage($field)) { $function = $field->module . '_field_views_data_views_data_alter'; if (function_exists($function)) { $function($data, $field); @@ -63,12 +63,17 @@ function field_views_data_alter(&$data) { * @param \Drupal\field\FieldStorageConfigInterface $field_storage * The field storage definition. * - * @return bool - * True if the entity type uses ContentEntityDatabaseStorage. + * @return \Drupal\Core\Entity\ContentEntityDatabaseStorage + * Returns the entity type storage if supported. */ -function _field_views_is_sql_entity_type(FieldStorageConfigInterface $field_storage) { +function _field_views_get_entity_type_storage(FieldStorageConfigInterface $field_storage) { + $result = FALSE; $entity_manager = \Drupal::entityManager(); - return $entity_manager->hasDefinition($field_storage->entity_type) && $entity_manager->getStorage($field_storage->entity_type) instanceof ContentEntityDatabaseStorage; + if ($entity_manager->hasDefinition($field_storage->getTargetEntityTypeId())) { + $storage = $entity_manager->getStorage($field_storage->getTargetEntityTypeId()); + $result = $storage instanceof ContentEntityDatabaseStorage ? $storage : FALSE; + } + return $result; } /** @@ -120,6 +125,11 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field if (!$field_storage->getBundles()) { return $data; } + // Check whether the entity type storage is supported. + $storage = _field_views_get_entity_type_storage($field_storage); + if (!$storage) { + return $data; + } $field_name = $field_storage->getName(); $field_columns = $field_storage->getColumns(); @@ -127,7 +137,7 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field // Grab information about the entity type tables. // We need to join to both the base table and the data table, if available. $entity_manager = \Drupal::entityManager(); - $entity_type_id = $field_storage->entity_type; + $entity_type_id = $field_storage->getTargetEntityTypeId(); $entity_type = $entity_manager->getDefinition($entity_type_id); if (!$base_table = $entity_type->getBaseTable()) { // We cannot do anything if for some reason there is no base table. @@ -150,15 +160,18 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field } // Description of the field tables. + // @todo Generalize this code to make it work with any table layout. See + // https://drupal.org/node/2079019. + $table_mapping = $storage->getTableMapping(); $field_tables = array( EntityStorageInterface::FIELD_LOAD_CURRENT => array( - 'table' => ContentEntityDatabaseStorage::_fieldTableName($field_storage), + 'table' => $table_mapping->getDedicatedDataTableName($field_storage), 'alias' => "{$entity_type_id}__{$field_name}", ), ); if ($supports_revisions) { $field_tables[EntityStorageInterface::FIELD_LOAD_REVISION] = array( - 'table' => ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage), + 'table' => $table_mapping->getDedicatedRevisionTableName($field_storage), 'alias' => "{$entity_type_id}_revision__{$field_name}", ); } @@ -220,7 +233,7 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field // Build the list of additional fields to add to queries. $add_fields = array('delta', 'langcode', 'bundle'); foreach (array_keys($field_columns) as $column) { - $add_fields[] = ContentEntityDatabaseStorage::_fieldColumnName($field_storage, $column); + $add_fields[] = $table_mapping->getFieldColumnName($field_storage, $column); } // Determine the label to use for the field. We don't have a label available // at the field level, so we just go through all instances and take the one @@ -344,11 +357,10 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field else { $group = t('@group (historical data)', array('@group' => $group_name)); } - $column_real_name = ContentEntityDatabaseStorage::_fieldColumnName($field_storage, $column); + $column_real_name = $table_mapping->getFieldColumnName($field_storage, $column); // Load all the fields from the table by default. - $field_sql_schema = ContentEntityDatabaseStorage::_fieldSqlSchema($field_storage); - $additional_fields = array_keys($field_sql_schema[$table]['fields']); + $additional_fields = $table_mapping->getAllColumns($table); $data[$table_alias][$column_real_name] = array( 'group' => $group, diff --git a/core/modules/field/src/Plugin/views/field/Field.php b/core/modules/field/src/Plugin/views/field/Field.php index ad3a99e..0610843 100644 --- a/core/modules/field/src/Plugin/views/field/Field.php +++ b/core/modules/field/src/Plugin/views/field/Field.php @@ -9,7 +9,6 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\Xss; -use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityStorageInterface; @@ -353,9 +352,12 @@ public function clickSort($order) { } $this->ensureMyTable(); - $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($this->definition['entity_type']); + $entity_type_id = $this->definition['entity_type']; + $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id); $field_storage = $field_storage_definitions[$this->definition['field_name']]; - $column = ContentEntityDatabaseStorage::_fieldColumnName($field_storage, $this->options['click_sort_column']); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = $this->entityManager->getStorage($entity_type_id)->getTableMapping(); + $column = $table_mapping->getFieldColumnName($field_storage, $this->options['click_sort_column']); if (!isset($this->aliases[$column])) { // Column is not in query; add a sort on it (without adding the column). $this->aliases[$column] = $this->tableAlias . '.' . $column; diff --git a/core/modules/field/src/Tests/BulkDeleteTest.php b/core/modules/field/src/Tests/BulkDeleteTest.php index ee099a8..30d8e4b 100644 --- a/core/modules/field/src/Tests/BulkDeleteTest.php +++ b/core/modules/field/src/Tests/BulkDeleteTest.php @@ -7,7 +7,6 @@ namespace Drupal\field\Tests; -use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\Core\Entity\EntityInterface; use Drupal\field\Entity\FieldInstanceConfig; @@ -178,11 +177,13 @@ function testDeleteFieldInstance() { $this->assertEqual($instance->bundle, $bundle, 'The deleted instance is for the correct bundle'); // Check that the actual stored content did not change during delete. - $schema = ContentEntityDatabaseStorage::_fieldSqlSchema($field_storage); - $table = ContentEntityDatabaseStorage::_fieldTableName($field_storage); - $column = ContentEntityDatabaseStorage::_fieldColumnName($field_storage, 'value'); + $storage = \Drupal::entityManager()->getStorage($this->entity_type); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = $storage->getTableMapping(); + $table = $table_mapping->getDedicatedDataTableName($field_storage); + $column = $table_mapping->getFieldColumnName($field_storage, 'value'); $result = db_select($table, 't') - ->fields('t', array_keys($schema[$table]['fields'])) + ->fields('t') ->execute(); foreach ($result as $row) { $this->assertEqual($this->entities[$row->entity_id]->{$field_storage->name}->value, $row->$column); diff --git a/core/modules/field/src/Tests/FieldDataCountTest.php b/core/modules/field/src/Tests/FieldDataCountTest.php index 1a57fc9..66e60db 100644 --- a/core/modules/field/src/Tests/FieldDataCountTest.php +++ b/core/modules/field/src/Tests/FieldDataCountTest.php @@ -76,7 +76,8 @@ public function testEntityCountAndHasData() { $storage = \Drupal::entityManager()->getStorage('entity_test'); if ($storage instanceof ContentEntityDatabaseStorage) { // Count the actual number of rows in the field table. - $field_table_name = $storage->_fieldTableName($field_storage); + $table_mapping = $storage->getTableMapping(); + $field_table_name = $table_mapping->getDedicatedDataTableName($field_storage); $result = db_select($field_table_name, 't') ->fields('t') ->countQuery() diff --git a/core/modules/field/src/Tests/Views/ApiDataTest.php b/core/modules/field/src/Tests/Views/ApiDataTest.php index 7631e29..5ad8a53 100644 --- a/core/modules/field/src/Tests/Views/ApiDataTest.php +++ b/core/modules/field/src/Tests/Views/ApiDataTest.php @@ -6,7 +6,6 @@ */ namespace Drupal\field\Tests\Views; -use Drupal\Core\Entity\ContentEntityDatabaseStorage; /** * Tests the Field Views data. @@ -51,8 +50,10 @@ function testViewsData() { // Check the table and the joins of the first field. // Attached to node only. $field_storage = $this->fieldStorages[0]; - $current_table = ContentEntityDatabaseStorage::_fieldTableName($field_storage); - $revision_table = ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = \Drupal::entityManager()->getStorage('node')->getTableMapping(); + $current_table = $table_mapping->getDedicatedDataTableName($field_storage); + $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage); $data[$current_table] = $views_data->get($current_table); $data[$revision_table] = $views_data->get($revision_table); diff --git a/core/modules/file/file.views.inc b/core/modules/file/file.views.inc index bfa3d15..eeb16fd 100644 --- a/core/modules/file/file.views.inc +++ b/core/modules/file/file.views.inc @@ -5,7 +5,6 @@ * Provide views data for file.module. */ -use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\field\FieldStorageConfigInterface; /** @@ -39,9 +38,12 @@ function file_field_views_data(FieldStorageConfigInterface $field_storage) { */ function file_field_views_data_views_data_alter(array &$data, FieldStorageConfigInterface $field_storage) { $entity_type_id = $field_storage->entity_type; - $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id); + $entity_manager = \Drupal::entityManager(); + $entity_type = $entity_manager->getDefinition($entity_type_id); $field_name = $field_storage->getName(); $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type_id; + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = $entity_manager->getStorage($entity_type_id)->getTableMapping(); list($label) = field_views_field_label($entity_type_id, $field_name); @@ -51,7 +53,7 @@ function file_field_views_data_views_data_alter(array &$data, FieldStorageConfig 'id' => 'entity_reverse', 'field_name' => $field_name, 'entity_type' => $entity_type_id, - 'field table' => ContentEntityDatabaseStorage::_fieldTableName($field_storage), + 'field table' => $table_mapping->getDedicatedDataTableName($field_storage), 'field field' => $field_name . '_target_id', 'base' => $entity_type->getBaseTable(), 'base field' => $entity_type->getKey('id'), diff --git a/core/modules/image/image.views.inc b/core/modules/image/image.views.inc index 5120350..04c32b9 100644 --- a/core/modules/image/image.views.inc +++ b/core/modules/image/image.views.inc @@ -5,7 +5,6 @@ * Provide views data for image.module. */ -use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\field\FieldStorageConfigInterface; /** @@ -37,10 +36,13 @@ function image_field_views_data(FieldStorageConfigInterface $field_storage) { * Views integration to provide reverse relationships on image fields. */ function image_field_views_data_views_data_alter(array &$data, FieldStorageConfigInterface $field_storage) { - $entity_type_id = $field_storage->entity_type; + $entity_type_id = $field_storage->getTargetEntityTypeId(); $field_name = $field_storage->getName(); - $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id); + $entity_manager = \Drupal::entityManager(); + $entity_type = $entity_manager->getDefinition($entity_type_id); $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type_id; + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = $entity_manager->getStorage($entity_type_id)->getTableMapping(); list($label) = field_views_field_label($entity_type_id, $field_name); @@ -50,7 +52,7 @@ function image_field_views_data_views_data_alter(array &$data, FieldStorageConfi 'id' => 'entity_reverse', 'field_name' => $field_name, 'entity_type' => $entity_type_id, - 'field table' => ContentEntityDatabaseStorage::_fieldTableName($field_storage), + 'field table' => $table_mapping->getDedicatedDataTableName($field_storage), 'field field' => $field_name . '_target_id', 'base' => $entity_type->getBaseTable(), 'base field' => $entity_type->getKey('id'), diff --git a/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php b/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php index 377312a..4ed1598 100644 --- a/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php +++ b/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php @@ -49,7 +49,9 @@ public function testCustomBundleFieldCreateDelete() { $this->assertNotNull($definition, 'Field definition found.'); // Make sure the table has been created. - $table = $this->entityManager->getStorage('entity_test')->_fieldTableName($definition); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = $this->entityManager->getStorage('entity_test')->getTableMapping(); + $table = $table_mapping->getDedicatedDataTableName($definition); $this->assertTrue($this->database->schema()->tableExists($table), 'Table created'); $this->moduleHandler->uninstall(array('entity_bundle_field_test'), FALSE); $this->assertFalse($this->database->schema()->tableExists($table), 'Table dropped'); @@ -87,7 +89,9 @@ public function testCustomBundleFieldUsage() { $this->assertEqual($entity->custom_field->value, 'cozy', 'Entity was updated correctly.'); $entity->delete(); - $table = $storage->_fieldTableName($entity->getFieldDefinition('custom_field')); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = $storage->getTableMapping(); + $table = $table_mapping->getDedicatedDataTableName($entity->getFieldDefinition('custom_field')); $result = $this->database->select($table, 'f') ->fields('f') ->condition('f.entity_id', $entity->id()) @@ -100,7 +104,7 @@ public function testCustomBundleFieldUsage() { $entity->save(); entity_test_delete_bundle('custom'); - $table = $storage->_fieldTableName($entity->getFieldDefinition('custom_field')); + $table = $table_mapping->getDedicatedDataTableName($entity->getFieldDefinition('custom_field')); $result = $this->database->select($table, 'f') ->condition('f.entity_id', $entity->id()) ->condition('deleted', 1) diff --git a/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php b/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php index 924164c..375012c 100644 --- a/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php +++ b/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php @@ -8,7 +8,6 @@ namespace Drupal\system\Tests\Entity; use Drupal\Core\Database\Database; -use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException; use Drupal\field\Entity\FieldStorageConfig; @@ -56,6 +55,8 @@ class FieldSqlStorageTest extends EntityUnitTestBase { protected $instance; /** + * Name of the data table of the field. + * * @var string */ protected $table; @@ -67,6 +68,13 @@ class FieldSqlStorageTest extends EntityUnitTestBase { */ protected $revision_table; + /** + * The table mapping for the tested entity type. + * + * @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping + */ + protected $table_mapping; + protected function setUp() { parent::setUp(); @@ -88,8 +96,11 @@ protected function setUp() { )); $this->instance->save(); - $this->table = ContentEntityDatabaseStorage::_fieldTableName($this->fieldStorage); - $this->revision_table = ContentEntityDatabaseStorage::_fieldRevisionTableName($this->fieldStorage); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = \Drupal::entityManager()->getStorage($entity_type)->getTableMapping(); + $this->table_mapping = $table_mapping; + $this->table = $table_mapping->getDedicatedDataTableName($this->fieldStorage); + $this->revision_table = $table_mapping->getDedicatedRevisionTableName($this->fieldStorage); } /** @@ -99,7 +110,7 @@ function testFieldLoad() { $entity_type = $bundle = 'entity_test_rev'; $storage = $this->container->get('entity.manager')->getStorage($entity_type); - $columns = array('bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'langcode', ContentEntityDatabaseStorage::_fieldColumnName($this->fieldStorage, 'value')); + $columns = array('bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'langcode', $this->table_mapping->getFieldColumnName($this->fieldStorage, 'value')); // Create an entity with four revisions. $revision_ids = array(); @@ -337,7 +348,7 @@ function testFieldUpdateFailure() { // Create a text field. $field_storage = entity_create('field_storage_config', array( 'name' => 'test_text', - 'entity_type' => 'entity_test', + 'entity_type' => 'entity_test_rev', 'type' => 'text', 'settings' => array('max_length' => 255), )); @@ -355,7 +366,11 @@ function testFieldUpdateFailure() { } // Ensure that the field tables are still there. - foreach (ContentEntityDatabaseStorage::_fieldSqlSchema($prior_field_storage) as $table_name => $table_info) { + $tables = array( + $this->table_mapping->getDedicatedDataTableName($prior_field_storage), + $this->table_mapping->getDedicatedRevisionTableName($prior_field_storage), + ); + foreach ($tables as $table_name) { $this->assertTrue(db_table_exists($table_name), t('Table %table exists.', array('%table' => $table_name))); } } @@ -378,7 +393,7 @@ function testFieldUpdateIndexesWithData() { 'bundle' => $entity_type, )); $instance->save(); - $tables = array(ContentEntityDatabaseStorage::_fieldTableName($field_storage), ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage)); + $tables = array($this->table_mapping->getDedicatedDataTableName($field_storage), $this->table_mapping->getDedicatedRevisionTableName($field_storage)); // Verify the indexes we will create do not exist yet. foreach ($tables as $table) { @@ -444,19 +459,9 @@ function testFieldSqlStorageForeignKeys() { // Reload the field schema after the update. $schema = $field_storage->getSchema(); - // Retrieve the field definition and check that the foreign key is in place. - $field_storage = FieldStorageConfig::loadByName('entity_test', $field_name); + // Check that the foreign key is in place. $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name modified after update'); $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update'); - - // Verify the SQL schema. - $schemas = ContentEntityDatabaseStorage::_fieldSqlSchema($field_storage); - $schema = $schemas[ContentEntityDatabaseStorage::_fieldTableName($field_storage)]; - $this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema'); - $foreign_key = reset($schema['foreign keys']); - $foreign_key_column = ContentEntityDatabaseStorage::_fieldColumnName($field_storage, $foreign_key_name); - $this->assertEqual($foreign_key['table'], $foreign_key_name, 'Foreign key table name preserved in the schema'); - $this->assertEqual($foreign_key['columns'][$foreign_key_column], 'id', 'Foreign key column name preserved in the schema'); } /** @@ -504,9 +509,9 @@ public function testTableNames() { 'type' => 'test_field', )); $expected = 'short_entity_type__short_field_name'; - $this->assertEqual(ContentEntityDatabaseStorage::_fieldTableName($field_storage), $expected); + $this->assertEqual($this->table_mapping->getDedicatedDataTableName($field_storage), $expected); $expected = 'short_entity_type_revision__short_field_name'; - $this->assertEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage), $expected); + $this->assertEqual($this->table_mapping->getDedicatedRevisionTableName($field_storage), $expected); // Short entity type, long field name $entity_type = 'short_entity_type'; @@ -517,9 +522,9 @@ public function testTableNames() { 'type' => 'test_field', )); $expected = 'short_entity_type__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual(ContentEntityDatabaseStorage::_fieldTableName($field_storage), $expected); + $this->assertEqual($this->table_mapping->getDedicatedDataTableName($field_storage), $expected); $expected = 'short_entity_type_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage), $expected); + $this->assertEqual($this->table_mapping->getDedicatedRevisionTableName($field_storage), $expected); // Long entity type, short field name $entity_type = 'long_entity_type_abcdefghijklmnopqrstuvwxyz'; @@ -530,9 +535,9 @@ public function testTableNames() { 'type' => 'test_field', )); $expected = 'long_entity_type_abcdefghijklmnopq__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual(ContentEntityDatabaseStorage::_fieldTableName($field_storage), $expected); + $this->assertEqual($this->table_mapping->getDedicatedDataTableName($field_storage), $expected); $expected = 'long_entity_type_abcdefghijklmnopq_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage), $expected); + $this->assertEqual($this->table_mapping->getDedicatedRevisionTableName($field_storage), $expected); // Long entity type and field name. $entity_type = 'long_entity_type_abcdefghijklmnopqrstuvwxyz'; @@ -543,17 +548,17 @@ public function testTableNames() { 'type' => 'test_field', )); $expected = 'long_entity_type_abcdefghijklmnopq__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual(ContentEntityDatabaseStorage::_fieldTableName($field_storage), $expected); + $this->assertEqual($this->table_mapping->getDedicatedDataTableName($field_storage), $expected); $expected = 'long_entity_type_abcdefghijklmnopq_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage), $expected); + $this->assertEqual($this->table_mapping->getDedicatedRevisionTableName($field_storage), $expected); // Try creating a second field and check there are no clashes. $field_storage2 = entity_create('field_storage_config', array( 'entity_type' => $entity_type, 'name' => $field_name . '2', 'type' => 'test_field', )); - $this->assertNotEqual(ContentEntityDatabaseStorage::_fieldTableName($field_storage), ContentEntityDatabaseStorage::_fieldTableName($field_storage2)); - $this->assertNotEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage), ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage2)); + $this->assertNotEqual($this->table_mapping->getDedicatedDataTableName($field_storage), $this->table_mapping->getDedicatedDataTableName($field_storage2)); + $this->assertNotEqual($this->table_mapping->getDedicatedRevisionTableName($field_storage), $this->table_mapping->getDedicatedRevisionTableName($field_storage2)); // Deleted field. $field_storage = entity_create('field_storage_config', array( @@ -563,9 +568,9 @@ public function testTableNames() { 'deleted' => TRUE, )); $expected = 'field_deleted_data_' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual(ContentEntityDatabaseStorage::_fieldTableName($field_storage, TRUE), $expected); + $this->assertEqual($this->table_mapping->getDedicatedDataTableName($field_storage, TRUE), $expected); $expected = 'field_deleted_revision_' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage, TRUE), $expected); + $this->assertEqual($this->table_mapping->getDedicatedRevisionTableName($field_storage, TRUE), $expected); } } diff --git a/core/modules/system/src/Tests/Entity/FieldTranslationSqlStorageTest.php b/core/modules/system/src/Tests/Entity/FieldTranslationSqlStorageTest.php index 511eeec..d23d75f 100644 --- a/core/modules/system/src/Tests/Entity/FieldTranslationSqlStorageTest.php +++ b/core/modules/system/src/Tests/Entity/FieldTranslationSqlStorageTest.php @@ -8,7 +8,6 @@ namespace Drupal\system\Tests\Entity; use Drupal\Core\Entity\ContentEntityInterface; -use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\Core\Language\LanguageInterface; use Drupal\field\Entity\FieldStorageConfig; @@ -83,27 +82,24 @@ protected function assertFieldStorageLangcode(ContentEntityInterface $entity, $m $id = $entity->id(); $langcode = $entity->getUntranslated()->language()->id; $fields = array($this->field_name, $this->untranslatable_field_name); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = \Drupal::entityManager()->getStorage($entity_type)->getTableMapping(); foreach ($fields as $field_name) { $field_storage = FieldStorageConfig::loadByName($entity_type, $field_name); - $tables = array( - ContentEntityDatabaseStorage::_fieldTableName($field_storage), - ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage), - ); + $table = $table_mapping->getDedicatedDataTableName($field_storage); - foreach ($tables as $table) { - $record = \Drupal::database() - ->select($table, 'f') - ->fields('f') - ->condition('f.entity_id', $id) - ->condition('f.revision_id', $id) - ->execute() - ->fetchObject(); + $record = \Drupal::database() + ->select($table, 'f') + ->fields('f') + ->condition('f.entity_id', $id) + ->condition('f.revision_id', $id) + ->execute() + ->fetchObject(); - if ($record->langcode != $langcode) { - $status = FALSE; - break; - } + if ($record->langcode != $langcode) { + $status = FALSE; + break; } } diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php index 0f8eaef..0c15085 100644 --- a/core/modules/taxonomy/src/Entity/Term.php +++ b/core/modules/taxonomy/src/Entity/Term.php @@ -169,7 +169,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { // Save new terms with no parents by default. ->setDefaultValue(0) ->setSetting('unsigned', TRUE) - ->addConstraint('TermParent', array()); + ->addConstraint('TermParent', array()) + ->setCustomStorage(TRUE); $fields['changed'] = BaseFieldDefinition::create('changed') ->setLabel(t('Changed')) diff --git a/core/modules/taxonomy/taxonomy.views.inc b/core/modules/taxonomy/taxonomy.views.inc index 383075f..42e0374 100644 --- a/core/modules/taxonomy/taxonomy.views.inc +++ b/core/modules/taxonomy/taxonomy.views.inc @@ -5,7 +5,6 @@ * Provides views data for taxonomy.module. */ -use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\field\FieldStorageConfigInterface; /** @@ -92,9 +91,12 @@ function taxonomy_field_views_data(FieldStorageConfigInterface $field_storage) { */ function taxonomy_field_views_data_views_data_alter(array &$data, FieldStorageConfigInterface $field_storage) { $field_name = $field_storage->getName(); - $entity_type_id = $field_storage->entity_type; - $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id); + $entity_type_id = $field_storage->getTargetEntityTypeId(); + $entity_manager = \Drupal::entityManager(); + $entity_type = $entity_manager->getDefinition($entity_type_id); $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type_id; + /** @var \Drupal\Core\Entity\Sql\DefaultTableMappingInterface $table_mapping */ + $table_mapping = $entity_manager->getStorage($entity_type_id)->getTableMapping(); list($label) = field_views_field_label($entity_type_id, $field_name); @@ -104,7 +106,7 @@ function taxonomy_field_views_data_views_data_alter(array &$data, FieldStorageCo 'id' => 'entity_reverse', 'field_name' => $field_name, 'entity_type' => $entity_type_id, - 'field table' => ContentEntityDatabaseStorage::_fieldTableName($field_storage), + 'field table' => $table_mapping->getDedicatedDataTableName($field_storage), 'field field' => $field_name . '_target_id', 'base' => $entity_type->getBaseTable(), 'base field' => $entity_type->getKey('id'), diff --git a/core/modules/views/views.api.php b/core/modules/views/views.api.php index e5c8c50..a54d562 100644 --- a/core/modules/views/views.api.php +++ b/core/modules/views/views.api.php @@ -474,6 +474,7 @@ function hook_field_views_data_alter(array &$data, \Drupal\field\FieldStorageCon $field_name = $field_storage->getName(); $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id); $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type_id; + $table_mapping = \Drupal::entityManager()->getStorage($entity_type_id)->getTableMapping(); list($label) = field_views_field_label($entity_type_id, $field_name); @@ -483,7 +484,7 @@ function hook_field_views_data_alter(array &$data, \Drupal\field\FieldStorageCon 'id' => 'entity_reverse', 'field_name' => $field_name, 'entity_type' => $entity_type_id, - 'field table' => ContentEntityDatabaseStorage::_fieldTableName($field_storage), + 'field table' => $table_mapping->getDedicatedDataTableName($field_storage), 'field field' => $field_name . '_target_id', 'base' => $entity_type->getBaseTable(), 'base field' => $entity_type->getKey('id'), @@ -531,6 +532,7 @@ function hook_field_views_data_views_data_alter(array &$data, \Drupal\field\Fiel $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id); $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type_id; list($label) = field_views_field_label($entity_type_id, $field_name); + $table_mapping = \Drupal::entityManager()->getStorage($entity_type_id)->getTableMapping(); // Views data for this field is in $data[$data_key]. $data[$data_key][$pseudo_field_name]['relationship'] = array( @@ -539,7 +541,7 @@ function hook_field_views_data_views_data_alter(array &$data, \Drupal\field\Fiel 'id' => 'entity_reverse', 'field_name' => $field_name, 'entity_type' => $entity_type_id, - 'field table' => ContentEntityDatabaseStorage::_fieldTableName($field), + 'field table' => $table_mapping->getDedicatedDataTableName($field), 'field field' => $field_name . '_target_id', 'base' => $entity_type->getBaseTable(), 'base field' => $entity_type->getKey('id'),