diff -u b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php --- b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php @@ -242,17 +242,16 @@ /** * {@inheritdoc} */ - public function getTableMapping(array $storage_definitions = NULL) { - $table_mapping = $this->tableMapping; + public function getTableMapping() { + if (!isset($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); + $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(); }); + $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); @@ -280,16 +279,16 @@ $translatable = $this->entityType->getDataTable() && $this->entityType->isTranslatable(); if (!$revisionable && !$translatable) { // The base layout stores all the base field values in the base table. - $table_mapping->setFieldNames($this->baseTable, $all_fields); + $this->tableMapping->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. - $table_mapping->setFieldNames($this->baseTable, array_diff($all_fields, $revision_metadata_fields)); + $this->tableMapping->setFieldNames($this->baseTable, array_diff($all_fields, $revision_metadata_fields)); $revision_key_fields = array($this->idKey, $this->revisionKey); - $table_mapping->setFieldNames($this->revisionTable, array_merge($revision_key_fields, $revisionable_fields)); + $this->tableMapping->setFieldNames($this->revisionTable, array_merge($revision_key_fields, $revisionable_fields)); } elseif (!$revisionable && $translatable) { // Multilingual layouts store key field values in the base table. The @@ -298,7 +297,7 @@ // 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. - $table_mapping + $this->tableMapping ->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 @@ -314,13 +313,13 @@ // 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. - $table_mapping->setFieldNames($this->baseTable, array_values(array_diff($key_fields, array($this->langcodeKey)))); + $this->tableMapping->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)); - $table_mapping + $this->tableMapping ->setFieldNames($this->dataTable, $data_fields) // Add the denormalized 'default_langcode' field to the mapping. Its // value is identical to the query expression @@ -329,43 +328,20 @@ ->setExtraColumns($this->dataTable, array('default_langcode')); $revision_base_fields = array_merge(array($this->idKey, $this->revisionKey, $this->langcodeKey), $revision_metadata_fields); - $table_mapping->setFieldNames($this->revisionTable, $revision_base_fields); + $this->tableMapping->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, array($this->langcodeKey)); - $table_mapping + $revision_data_fields = array_diff($revisionable_fields, $revision_metadata_fields); + $this->tableMapping ->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 $table_mapping; + return $this->tableMapping; } /** @@ -612,7 +588,7 @@ $table_mapping = $this->getTableMapping(); $translations = array(); if ($this->revisionDataTable) { - $data_fields = array_diff($table_mapping->getFieldNames($this->revisionDataTable), $table_mapping->getFieldNames($this->baseTable)); + $data_fields = array_diff_key($table_mapping->getFieldNames($this->revisionDataTable), $table_mapping->getFieldNames($this->baseTable)); } else { $data_fields = $table_mapping->getFieldNames($this->dataTable); @@ -1188,7 +1164,7 @@ $definitions[$bundle] = $this->entityManager->getFieldDefinitions($this->entityTypeId, $bundle); foreach ($definitions[$bundle] as $field_name => $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); - if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) { + if ($this->usesDedicatedTable($storage_definition)) { $storage_definitions[$field_name] = $storage_definition; } } @@ -1263,7 +1239,7 @@ foreach ($this->entityManager->getFieldDefinitions($entity_type, $bundle) as $field_name => $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); - if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) { + if (!$this->usesDedicatedTable($storage_definition)) { continue; } $table_name = $table_mapping->getDedicatedDataTableName($storage_definition); @@ -1278,12 +1254,10 @@ ->condition('entity_id', $id) ->execute(); } - if ($this->entityType->isRevisionable()) { - $this->database->delete($revision_name) - ->condition('entity_id', $id) - ->condition('revision_id', $vid) - ->execute(); - } + $this->database->delete($revision_name) + ->condition('entity_id', $id) + ->condition('revision_id', $vid) + ->execute(); } // Prepare the multi-insert query. @@ -1293,9 +1267,7 @@ $columns[] = $table_mapping->getFieldColumnName($storage_definition, $column); } $query = $this->database->insert($table_name)->fields($columns); - if ($this->entityType->isRevisionable()) { - $revision_query = $this->database->insert($revision_name)->fields($columns); - } + $revision_query = $this->database->insert($revision_name)->fields($columns); $langcodes = $field_definition->isTranslatable() ? $translation_langcodes : array($default_langcode); foreach ($langcodes as $langcode) { @@ -1318,9 +1290,7 @@ $record[$column_name] = !empty($attributes['serialize']) ? serialize($item->$column) : $item->$column; } $query->values($record); - if ($this->entityType->isRevisionable()) { - $revision_query->values($record); - } + $revision_query->values($record); if ($storage_definition->getCardinality() != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && ++$delta_count == $storage_definition->getCardinality()) { break; @@ -1335,9 +1305,7 @@ if ($entity->isDefaultRevision()) { $query->execute(); } - if ($this->entityType->isRevisionable()) { - $revision_query->execute(); - } + $revision_query->execute(); } } } @@ -1352,7 +1320,7 @@ $table_mapping = $this->getTableMapping(); foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); - if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) { + if (!$this->usesDedicatedTable($storage_definition)) { continue; } $table_name = $table_mapping->getDedicatedDataTableName($storage_definition); @@ -1360,11 +1328,9 @@ $this->database->delete($table_name) ->condition('entity_id', $entity->id()) ->execute(); - if ($this->entityType->isRevisionable()) { - $this->database->delete($revision_name) - ->condition('entity_id', $entity->id()) - ->execute(); - } + $this->database->delete($revision_name) + ->condition('entity_id', $entity->id()) + ->execute(); } } @@ -1376,11 +1342,11 @@ */ protected function deleteFieldItemsRevision(EntityInterface $entity) { $vid = $entity->getRevisionId(); - if (isset($vid) && $this->entityType->isRevisionable()) { + if (isset($vid)) { $table_mapping = $this->getTableMapping(); foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); - if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) { + if (!$this->usesDedicatedTable($storage_definition)) { continue; } $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition); @@ -1393,6 +1359,21 @@ } /** + * 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) { @@ -1494,19 +1475,12 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) { $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(); - } - } + // 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(); // Move the table to a unique name while the table contents are being // deleted. @@ -1523,20 +1497,16 @@ $table_mapping = $this->getTableMapping(); $storage_definition = $field_definition->getFieldStorageDefinition(); // Mark field data as deleted. - 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(); - } - } + $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(); + $this->database->update($revision_name) + ->fields(array('deleted' => 1)) + ->condition('bundle', $field_definition->getBundle()) + ->execute(); } /** @@ -1555,7 +1525,7 @@ foreach ($field_definitions as $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); - if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) { + if ($this->usesDedicatedTable($storage_definition)) { $is_deleted = $this->storageDefinitionIsDeleted($storage_definition); $table_name = $table_mapping->getDedicatedDataTableName($storage_definition, $is_deleted); $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, $is_deleted); @@ -1563,12 +1533,10 @@ ->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(); - } + $this->database->update($revision_name) + ->fields(array('bundle' => $bundle_new)) + ->condition('bundle', $bundle) + ->execute(); } } } @@ -1670,52 +1638,23 @@ public function countFieldData($storage_definition, $as_bool = FALSE) { $table_mapping = $this->getTableMapping(); - 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(); + $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)); } - 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(); - } + $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(); return $as_bool ? (bool) $count : (int) $count; } @@ -1753,8 +1692,8 @@ * * @see hook_schema() */ - protected function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_definition, array $schema = NULL, $deleted = FALSE) { - $table_mapping = $this->getTableMapping(); + public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_definition, array $schema = NULL, $deleted = FALSE) { + $table_mapping = new DefaultTableMapping(array()); $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()}."; diff -u b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php --- b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php +++ b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php @@ -15,20 +15,13 @@ class DefaultTableMapping implements DefaultTableMappingInterface { /** - * The field storage definitions of this mapping. + * A list of field storage definitions that are available for 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 @@ -83,13 +76,11 @@ * Constructs a DefaultTableMapping. * * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $storage_definitions - * 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. + * A list of field storage definitions that should be available for the + * field columns of this table mapping. */ - public function __construct(array $storage_definitions, array $base_field_definitions) { + public function __construct(array $storage_definitions) { $this->fieldStorageDefinitions = $storage_definitions; - $this->baseFieldDefinitions = $base_field_definitions; } /** @@ -110,16 +101,7 @@ $this->allColumns[$table_name] = array_merge($this->allColumns[$table_name], array_values($this->getColumnNames($field_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)); - } + $this->allColumns[$table_name] = array_merge($this->allColumns[$table_name], $this->getExtraColumns($table_name)); } return $this->allColumns[$table_name]; } @@ -200,32 +182,6 @@ /** * {@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'); } diff -u b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMappingInterface.php b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMappingInterface.php --- b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMappingInterface.php +++ b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMappingInterface.php @@ -15,36 +15,6 @@ interface DefaultTableMappingInterface extends TableMappingInterface { /** - * Returns a list of dedicated table names for this mapping. - * - * @return string[] - * An array of table names. - */ - public function getDedicatedTableNames(); - - /** - * Checks whether the given field can be stored in a shared table. - * - * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition - * The field storage definition. - * - * @return bool - * TRUE if the field can be stored in a dedicated table, FALSE otherwise. - */ - public function allowsSharedTableStorage(FieldStorageDefinitionInterface $storage_definition); - - /** - * Checks whether the given field has to be stored in a dedicated table. - * - * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition - * The field storage definition. - * - * @return bool - * TRUE if the field can be stored in a dedicated table, FALSE otherwise. - */ - public function requiresDedicatedTableStorage(FieldStorageDefinitionInterface $storage_definition); - - /** * A list of columns that can not be used as field type columns. * * @return array