diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php index 1f37641..5c33f1e 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php @@ -29,8 +29,7 @@ * This class can be used as-is by most simple entity types. Entity types * requiring special handling can extend the class. */ -class ContentEntityDatabaseStorage extends ContentEntityStorageBase - implements FieldableEntityStorageInterface, ContentEntitySchemaHandlerInterface, SqlStorageInterface { +class ContentEntityDatabaseStorage extends ContentEntityStorageBase implements ContentEntitySchemaHandlerInterface, SqlStorageInterface { /** * The base table layout: no revision or multilingual support for base fields. @@ -93,6 +92,15 @@ class ContentEntityDatabaseStorage extends ContentEntityStorageBase protected $revisionKey = FALSE; /** + * Name of entity's default_langcode database table field. + * + * Has the value FALSE if this entity does not support multilingual storage. + * + * @var string|bool + */ + protected $defaultLangcodeKey = FALSE; + + /** * The base table of the entity, if the entity has storage. * * @var string @@ -187,34 +195,105 @@ public function __construct(EntityTypeInterface $entity_type, Connection $databa $this->entityManager = $entity_manager; $this->fieldDefinitions = $entity_manager->getBaseFieldDefinitions($entity_type->id()); - // Check if the entity type supports IDs. - if ($this->entityType->hasKey('id')) { - $this->idKey = $this->entityType->getKey('id'); - } + $this->initTableLayout(); + } + + /** + * Initializes table and key names based on the current layout type. + */ + protected function initTableLayout() { + $this->idKey = $this->entityType->getKey('id') ?: 'id'; + $this->uuidKey = $this->entityType->getKey('uuid') ?: 'uuid'; + + // The bundle key is optional. + $this->bundleKey = $this->entityType->getKey('bundle'); - // Check if the entity type supports UUIDs. - $this->uuidKey = $this->entityType->getKey('uuid'); + // @todo Table names do not belong to the entity type definition, they are + // storage implementation details. Rip them out. + $this->baseTable = $this->entityType->getBaseTable() ?: $this->entityTypeId; - // Some entity types are not stored in the database at all. - if ($base_table = $this->entityType->getBaseTable()) { - $this->baseTable = $base_table; + // Retrieve the current table layout type based on the entity type + // definition. + $layout_type = $this->getLayoutType(); + + if ($layout_type & static::LAYOUT_REVISION) { + $this->revisionKey = $this->entityType->getKey('revision') ?: 'revision_id'; + $this->revisionTable = $this->entityType->getRevisionTable() ?: $this->entityTypeId . '_revision'; + } + + if ($layout_type & static::LAYOUT_MULTILINGUAL) { + $this->dataTable = $this->entityType->getDataTable() ?: $this->entityTypeId . '_field_data'; + $this->langcodeKey = $this->entityType->getKey('langcode') ?: 'langcode'; + $this->defaultLangcodeKey = $this->entityType->getKey('default_langcode') ?: 'default_langcode'; } - // Check if the entity type supports revisions. - if ($this->entityType->hasKey('revision')) { - $this->revisionKey = $this->entityType->getKey('revision'); - $this->revisionTable = $this->entityType->getRevisionTable(); + if ($layout_type == static::LAYOUT_MULTILINGUAL_REVISION) { + $this->revisionDataTable = $this->entityType->getRevisionDataTable() ?: $this->entityTypeId . '_field_revision'; } + } - // Check if the entity type has a dedicated table for fields. - if ($data_table = $this->entityType->getDataTable()) { - $this->dataTable = $data_table; - // Entity types having both revision and translation support should always - // define a revision data table. - if ($this->revisionTable && $revision_data_table = $this->entityType->getRevisionDataTable()) { - $this->revisionDataTable = $revision_data_table; + /** + * Returns the current layout type. + * + * @return int + * An integer value identifiying the current layour type. + * + * @see static::LAYOUT_BASE + * @see static::LAYOUT_REVISION + * @see static::LAYOUT_MULTILINGUAL + * @see static::LAYOUT_MULTILINGUAL_REVISION + */ + protected function getLayoutType() { + if (!isset($this->layoutType)) { + $this->layoutType = static::LAYOUT_BASE; + if ($this->entityType->hasKey('revision')) { + $this->layoutType |= static::LAYOUT_REVISION; + } + if ($this->entityType->isTranslatable()) { + $this->layoutType |= static::LAYOUT_MULTILINGUAL; } } + return $this->layoutType; + } + + /** + * Returns the base table name. + * + * @return string + * The table name. + */ + public function getBaseTable() { + return $this->baseTable; + } + + /** + * Returns the revision table name. + * + * @return string|bool + * The table name or FALSE if it is not available. + */ + public function getRevisionTable() { + return $this->revisionTable; + } + + /** + * Returns the data table name. + * + * @return string|bool + * The table name or FALSE if it is not available. + */ + public function getDataTable() { + return $this->dataTable; + } + + /** + * Returns the revision data table name. + * + * @return string|bool + * The table name or FALSE if it is not available. + */ + public function getRevisionDataTable() { + return $this->revisionDataTable; } /** @@ -255,22 +334,7 @@ public function getTableMapping() { } } else { - $entity_keys = array_filter(array( - 'id' => $this->entityType->getKey('id'), - 'revision' => $this->entityType->getKey('revision'), - 'bundle' => $this->entityType->getKey('bundle'), - 'uuid' => $this->entityType->getKey('uuid'), - )); - // @todo This will become unnecessary once https://drupal.org/node/2143729 - // has landed. - if ($this->entityType->getDataTable()) { - $entity_keys['langcode'] = 'langcode'; - $entity_keys['default_langcode'] = 'default_langcode'; - }; - - $key_fields = $entity_keys; - unset($key_fields['default_langcode']); - $key_fields = array_values($key_fields); + $key_fields = array_filter(array($this->idKey, $this->revisionKey, $this->bundleKey, $this->uuidKey, $this->langcodeKey)); $storable_definitions = array_filter($this->fieldDefinitions, function (FieldDefinitionInterface $field_definition) { return !$field_definition->isComputed() && !$field_definition->hasCustomStorage(); @@ -279,49 +343,48 @@ public function getTableMapping() { // @todo Provide automatic definitions for revision metadata fields. // Rename 'log' to 'revision_log'. - $revision_metadata_fields = array('revision_timestamp', 'revision_uid', 'log'); + $revision_metadata_fields = array_intersect(array('revision_timestamp', 'revision_uid', 'log'), $storable_fields); $revisionable_filter_callback = function (FieldDefinitionInterface $definition) { return $definition->isRevisionable(); }; - $this->getLayoutType(); - switch (TRUE) { + switch ($this->getLayoutType()) { // The base layout stores all the base field values in the base table. - case $this->layoutType == static::LAYOUT_BASE: + case static::LAYOUT_BASE: $table_mapping[$this->baseTable] = $this->processFields(array_merge($key_fields, array_diff($storable_fields, $key_fields))); break; - // The base layout stores all the base field values in the base table. - // Revisionable fields are also stored in the revision table. - case $this->layoutType == static::LAYOUT_REVISION: + // The base layout stores all the base field values in the base table. + // Revisionable fields are also stored in the revision table. + case static::LAYOUT_REVISION: $table_mapping[$this->baseTable] = $this->processFields(array_merge($key_fields, array_diff($storable_fields, $key_fields, $revision_metadata_fields))); - $revision_key_fields = array($entity_keys['id'], $entity_keys['revision']); + $revision_key_fields = array($this->idKey, $this->revisionKey); $revisionable_fields = array_keys(array_filter($storable_definitions, $revisionable_filter_callback)); $table_mapping[$this->revisionTable] = $this->processFields(array_merge($revision_key_fields, $revisionable_fields)); break; - // Multilingual layouts store key field values in the base table. The - // other base field values are stored in the data table, no matter - // whether they are translatable or not. The data table holds also a - // denormalized copy of the bundle field value to allow for more - // performant queries. - case $this->layoutType == static::LAYOUT_MULTILINGUAL: + // Multilingual layouts store key field values in the base table. The + // other base field values are stored in the data table, no matter + // whether they are translatable or not. The data table holds also a + // denormalized copy of the bundle field value to allow for more + // performant queries. + case static::LAYOUT_MULTILINGUAL: $table_mapping[$this->baseTable] = $this->processFields($key_fields); - $data_key_fields = array_diff($key_fields, array('uuid')); + $data_key_fields = array_diff($key_fields, array($this->uuidKey)); $data_fields = array_diff($storable_fields, $key_fields); $table_mapping[$this->dataTable] = $this->processFields(array_merge($data_key_fields, $data_fields)); break; - // The revisionable multilingual layout stores key field values in the - // base table, except for language, which is stored in the revision - // table along with revision metadata. The revision data table holds - // data field values for all the available revisions without - // denormalizations. - case $this->layoutType == static::LAYOUT_MULTILINGUAL_REVISION: - $table_mapping[$this->baseTable] = $this->processFields(array_diff($key_fields, array($entity_keys['langcode']))); - $data_key_fields = array_diff($key_fields, array('uuid')); + // The revisionable multilingual layout stores key field values in the + // base table, except for language, which is stored in the revision + // table along with revision metadata. The revision data table holds + // data field values for all the available revisions without + // denormalizations. + case static::LAYOUT_MULTILINGUAL_REVISION: + $table_mapping[$this->baseTable] = $this->processFields(array_diff($key_fields, array($this->langcodeKey))); + $data_key_fields = array_diff($key_fields, array($this->uuidKey)); $data_fields = array_diff($storable_fields, $key_fields, $revision_metadata_fields); $table_mapping[$this->dataTable] = $this->processFields(array_merge($data_key_fields, $data_fields)); - $table_mapping[$this->revisionTable] = $this->processFields(array_merge(array($entity_keys['id'], $entity_keys['revision'], $entity_keys['langcode']), $revision_metadata_fields)); - $revision_data_key_fields = array_diff($key_fields, array($entity_keys['bundle'], $entity_keys['uuid'])); + $table_mapping[$this->revisionTable] = $this->processFields(array_merge(array($this->idKey, $this->revisionKey, $this->langcodeKey), $revision_metadata_fields)); + $revision_data_key_fields = array_diff($key_fields, array($this->bundleKey, $this->uuidKey)); $revisionable_fields = array_keys(array_filter($storable_definitions, $revisionable_filter_callback)); $revision_data_fields = array_diff($revisionable_fields, $revision_metadata_fields, $revision_data_key_fields); $table_mapping[$this->revisionDataTable] = $this->processFields(array_merge($revision_data_key_fields, $revision_data_fields)); @@ -336,30 +399,6 @@ public function getTableMapping() { } /** - * Returns the current layout type. - * - * @return int - * An integer value identifiying the current layour type. - * - * @see static::LAYOUT_BASE - * @see static::LAYOUT_REVISION - * @see static::LAYOUT_MULTILINGUAL - * @see static::LAYOUT_MULTILINGUAL_REVISION - */ - protected function getLayoutType() { - if (!isset($this->layoutType)) { - $this->layoutType = static::LAYOUT_BASE; - if ($this->entityType->hasKey('revision')) { - $this->layoutType |= static::LAYOUT_REVISION; - } - if ($this->entityType->isTranslatable()) { - $this->layoutType |= static::LAYOUT_MULTILINGUAL; - } - } - return $this->layoutType; - } - - /** * Returns a mapping between field and column names. * * @param array $field_names @@ -829,7 +868,7 @@ public function save(EntityInterface $entity) { $this->savePropertyData($entity); } if ($this->revisionDataTable) { - $this->savePropertyData($entity, 'revision_data_table'); + $this->savePropertyData($entity, $this->revisionDataTable); } $entity->setNewRevision(FALSE); $this->invokeFieldMethod('update', $entity); @@ -865,7 +904,7 @@ public function save(EntityInterface $entity) { $this->savePropertyData($entity); } if ($this->revisionDataTable) { - $this->savePropertyData($entity, 'revision_data_table'); + $this->savePropertyData($entity, $this->revisionDataTable); } $entity->enforceIsNew(FALSE); @@ -895,13 +934,14 @@ public function save(EntityInterface $entity) { * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity object. - * @param string $table_key - * (optional) The entity key identifying the target table. Defaults to - * 'data_table'. + * @param string $table_name + * (optional) The table name to save to. Defaults to the data table. */ - protected function savePropertyData(EntityInterface $entity, $table_key = 'data_table') { - $table_name = $this->entityType->get($table_key); - $revision = $table_key != 'data_table'; + protected function savePropertyData(EntityInterface $entity, $table_name = NULL) { + if (!isset($table_name)) { + $table_name = $this->dataTable; + } + $revision = $table_name != $this->dataTable; if (!$revision || !$entity->isNewRevision()) { $key = $revision ? $this->revisionKey : $this->idKey; @@ -916,7 +956,7 @@ protected function savePropertyData(EntityInterface $entity, $table_key = 'data_ foreach ($entity->getTranslationLanguages() as $langcode => $language) { $translation = $entity->getTranslation($langcode); - $record = $this->mapToDataStorageRecord($translation, $table_key); + $record = $this->mapToDataStorageRecord($translation, $table_name); $values = (array) $record; $query ->fields(array_keys($values)) @@ -931,21 +971,21 @@ protected function savePropertyData(EntityInterface $entity, $table_key = 'data_ * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity object. - * @param string $table_key - * (optional) The entity key identifying the target table. Defaults to - * 'base_table'. + * @param string $table_name + * (optional) The table name to map records to. Defaults to the base table. * * @return \stdClass * The record to store. */ - protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'base_table') { + protected function mapToStorageRecord(EntityInterface $entity, $table_name = NULL) { + if (!isset($table_name)) { + $table_name = $this->baseTable; + } + $record = new \stdClass(); $values = array(); $definitions = $entity->getFieldDefinitions(); - - $table_name = $this->entityType->get($table_key); $schema = $this->schemaBuilder()->getSchema()[$table_name]; - $is_new = $entity->isNew(); $multi_column_fields = array(); @@ -1007,15 +1047,17 @@ protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'bas * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity object. - * @param string $table_key - * (optional) The entity key identifying the target table. Defaults to - * 'data_table'. + * @param string $table_name + * (optional) The table name to map records to. Defaults to the data table. * * @return \stdClass * The record to store. */ - protected function mapToDataStorageRecord(EntityInterface $entity, $table_key = 'data_table') { - $record = $this->mapToStorageRecord($entity, $table_key); + protected function mapToDataStorageRecord(EntityInterface $entity, $table_name = NULL) { + if (!isset($table_name)) { + $table_name = $this->dataTable; + } + $record = $this->mapToStorageRecord($entity, $table_name); $record->langcode = $entity->language()->id; $record->default_langcode = intval($record->langcode == $entity->getUntranslated()->language()->id); return $record; @@ -1031,7 +1073,7 @@ protected function mapToDataStorageRecord(EntityInterface $entity, $table_key = * The revision id. */ protected function saveRevision(EntityInterface $entity) { - $record = $this->mapToStorageRecord($entity, 'revision_table'); + $record = $this->mapToStorageRecord($entity, $this->revisionTable); // When saving a new revision, set any existing revision ID to NULL so as to // ensure that a new revision will actually be created. diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php index 0c2bfe4..469047c 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php @@ -23,6 +23,13 @@ protected $bundleKey = FALSE; /** + * The entity langcode key + * + * @var string|bool + */ + protected $langcodeKey = FALSE; + + /** * Name of the entity class. * * @var string diff --git a/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php index 46ab0b7..b1ecf72 100644 --- a/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php +++ b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php @@ -7,8 +7,8 @@ namespace Drupal\Core\Entity\Schema; use Drupal\Core\Entity\ContentEntityType; +use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Entity\SqlStorageInterface; use Drupal\Core\Field\FieldDefinitionInterface; /** @@ -31,11 +31,11 @@ class ContentEntitySchemaHandler implements ContentEntitySchemaHandlerInterface protected $fieldDefinitions; /** - * A table mapper for the given entity type. + * The storage object for the given entity type. * - * @var \Drupal\Core\Entity\SqlStorageInterface + * @var \Drupal\Core\Entity\ContentEntityDatabaseStorage */ - protected $mapper; + protected $storage; /** * A static cache of the generated schema array. @@ -52,10 +52,10 @@ class ContentEntitySchemaHandler implements ContentEntitySchemaHandlerInterface * @param \Drupal\Core\Entity\ContentEntityType $entity_type * The entity type. */ - public function __construct(EntityManagerInterface $entity_manager, ContentEntityType $entity_type, SqlStorageInterface $mapper) { + public function __construct(EntityManagerInterface $entity_manager, ContentEntityType $entity_type, ContentEntityDatabaseStorage $storage) { $this->entityType = $entity_type; $this->fieldDefinitions = $entity_manager->getBaseFieldDefinitions($entity_type->id()); - $this->mapper = $mapper; + $this->storage = $storage; } /** @@ -97,7 +97,7 @@ public function getSchema() { } // Add the schema from field definitions. - foreach ($this->mapper->getTableMapping() as $table => $field_names) { + foreach ($this->storage->getTableMapping() as $table => $field_names) { foreach ($field_names as $field_name => $column_names) { $this->addFieldSchema($schema[$table], $field_name); } @@ -130,10 +130,10 @@ public function getSchema() { */ protected function getTables() { return array_filter(array( - 'base_table' => $this->entityType->getBaseTable(), - 'revision_table' => $this->entityType->getRevisionTable(), - 'data_table' => $this->entityType->getDataTable(), - 'revision_data_table' => $this->entityType->getRevisionDataTable(), + 'base_table' => $this->storage->getBaseTable(), + 'revision_table' => $this->storage->getRevisionTable(), + 'data_table' => $this->storage->getDataTable(), + 'revision_data_table' => $this->storage->getRevisionDataTable(), )); } diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php index 68414d3..ec3d65f 100644 --- a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php +++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php @@ -42,7 +42,7 @@ * "admin-form" = "custom_block.type_edit" * }, * fieldable = TRUE, - * translatable = TRUE, + * translatable = FALSE, * entity_keys = { * "id" = "id", * "revision" = "revision_id", diff --git a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php index 69adba8..3656b3e 100644 --- a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php +++ b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php @@ -37,7 +37,7 @@ * base_table = "comment", * uri_callback = "comment_uri", * fieldable = TRUE, - * translatable = TRUE, + * translatable = FALSE, * entity_keys = { * "id" = "cid", * "bundle" = "field_id", diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php index 255200a..f4bbde9 100644 --- a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php +++ b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php @@ -31,7 +31,7 @@ * }, * base_table = "shortcut", * data_table = "shortcut_field_data", - * translatable = TRUE, + * translatable = FALSE, * entity_keys = { * "id" = "id", * "uuid" = "uuid", diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php index 4550acb..17414d2 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php @@ -35,7 +35,7 @@ * base_table = "taxonomy_term_data", * uri_callback = "taxonomy_term_uri", * fieldable = TRUE, - * translatable = TRUE, + * translatable = FALSE, * entity_keys = { * "id" = "tid", * "bundle" = "vid", diff --git a/core/modules/user/lib/Drupal/user/Entity/User.php b/core/modules/user/lib/Drupal/user/Entity/User.php index 3c13a94..1fd5709 100644 --- a/core/modules/user/lib/Drupal/user/Entity/User.php +++ b/core/modules/user/lib/Drupal/user/Entity/User.php @@ -38,7 +38,7 @@ * uri_callback = "user_uri", * label_callback = "user_format_name", * fieldable = TRUE, - * translatable = TRUE, + * translatable = FALSE, * entity_keys = { * "id" = "uid", * "uuid" = "uuid"