diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php index 5769425..a1943b2 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php @@ -243,7 +243,7 @@ protected function initTableLayout() { * @see static::LAYOUT_MULTILINGUAL * @see static::LAYOUT_MULTILINGUAL_REVISION */ - protected function getLayoutType() { + public function getLayoutType() { if (!isset($this->layoutType)) { $this->layoutType = static::LAYOUT_BASE; if ($this->entityType->hasKey('revision')) { @@ -333,8 +333,9 @@ public function getTableMapping() { if (drupal_get_schema($this->baseTable)) { $tables = array_filter(array($this->baseTable, $this->revisionTable, $this->dataTable, $this->revisionDataTable)); foreach ($tables as $table) { - foreach (array_keys(drupal_get_schema($table)['fields']) as $field_name) { - $table_mapping[$table][$field_name] = array($field_name); + foreach (array_keys(drupal_get_schema($table)['fields']) as $column_name) { + $field_name = strstr($column_name, '__', TRUE) ?: $column_name; + $table_mapping[$table][$field_name][] = $column_name; } } } @@ -1018,7 +1019,9 @@ protected function mapToStorageRecord(ContentEntityInterface $entity, $table_nam if (isset($value) || !$is_new) { $info = isset($schema['fields'][$name]) ? $schema['fields'][$name] : $definition->getSchema()['columns'][$column]; list(, $storage_column) = each($storage_columns); - $record->$storage_column = drupal_schema_get_field_value($info, $value); + if (!empty($storage_column)) { + $record->$storage_column = drupal_schema_get_field_value($info, $value); + } } } } diff --git a/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php index b1ecf72..1fb9b53 100644 --- a/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php +++ b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php @@ -10,6 +10,7 @@ use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Entity\EntityTypeInterface; /** * An entity schema builder that supports revisionable, translatable entities. @@ -144,42 +145,103 @@ protected function getTables() { * The table schema to add the field schema to, passed by reference. * @param string $field_name * The name of the field. - * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition + * @param \Drupal\Core\Field\FieldDefinitionInterface $definition * The field definition to return the schema for. */ protected function addFieldSchema(array &$schema, $field_name) { - $field_definition = $this->fieldDefinitions[$field_name]; - $field_schema = $field_definition->getSchema(); - if (count($field_schema['columns']) == 1) { - - $schema['fields'][$field_name] = $field_schema['columns'][$field_definition->getMainPropertyName()]; - $schema['fields'][$field_name]['description'] = $field_definition->getDescription(); + $definition = $this->fieldDefinitions[$field_name]; + $field_schema = $definition->getSchema(); + foreach ($field_schema['columns'] as $column_name => $column_schema) { + $schema_field_name = $this->getFieldColumnName($definition, $column_name); + $schema['fields'][$schema_field_name] = $column_schema; + $schema['fields'][$schema_field_name]['description'] = $definition->getDescription(); // Only entity keys are required. - $schema['fields'][$field_name]['not null'] = (bool) $this->entityType->getKey($field_name); + $schema['fields'][$schema_field_name]['not null'] = (bool) $this->entityType->getKey($field_name); - if (!empty($field_schema['unique keys'])) { - $schema['unique keys'][$field_name] = array($field_name); - } if (!empty($field_schema['indexes'])) { - // @todo Support indexes specified as an array of column name and - // length. - $schema['indexes'][$field_name] = array($field_name); + $indexes = $this->getFieldSchemaData($definition, 'indexes'); + $schema['indexes'] = !empty($schema['indexes']) ? array_merge($schema['indexes'], $indexes) : $indexes; } + + if (!empty($field_schema['unique keys'])) { + $unique_keys = $this->getFieldSchemaData($definition, 'unique keys'); + $schema['unique keys'] = !empty($schema['unique keys']) ? array_merge($schema['unique keys'], $unique_keys) : $unique_keys; + } + if (!empty($field_schema['foreign keys'])) { - $schema += array('foreign keys' => array()); - $schema['foreign keys'] += $field_schema['foreign keys']; + $foreign_keys = $this->getFieldForeignKeys($definition); + $schema['foreign keys'] = !empty($schema['foreign keys']) ? array_merge($schema['foreign keys'], $foreign_keys) : $foreign_keys; } } - else { - foreach ($field_schema['columns'] as $column_name => $column_schema) { - // Only entity keys are required. - $column_schema['not null'] = (bool) $this->entityType->getKey($field_name); - $schema['fields'][$field_name . '__' . $column_name] = $column_schema; + } + + /** + * Returns field schema data for the given key. + * + * @param \Drupal\Core\Field\FieldDefinitionInterface $definition + * The field definition. + * @param string $key + * The schema key, e.g. 'indexes'. + * + * @return array + * The schema definition for the specified key. + */ + protected function getFieldSchemaData(FieldDefinitionInterface $definition, $key) { + $data = array(); + $schema = $definition->getSchema(); + + foreach ($schema[$key] as $key => $columns) { + $real_name = 'field__' . $this->getFieldColumnName($definition, $key); + foreach ($columns as $column) { + // Indexes can be specified as either a column name or an array with + // column name and length. Allow for either case. + $data[$real_name][] = is_array($column) ? + array($this->getFieldColumnName($definition, $column[0]), $column[1]) : + $this->getFieldColumnName($definition, $column); } } - // @todo Descriptions, unique keys, indexes and foreign keys. + return $data; + } + + /** + * Returns field foreign keys. + * + * @param \Drupal\Core\Field\FieldDefinitionInterface $definition + * The field definition. + * + * @return array + * The schema definition for the foreign keys. + */ + protected function getFieldForeignKeys(FieldDefinitionInterface $definition) { + $foreign_keys = array(); + $schema = $definition->getSchema(); + + foreach ($schema['foreign keys'] as $specifier => $specification) { + $real_name = $this->getFieldIndexName($definition, $specifier); + $foreign_keys[$real_name]['table'] = $specification['table']; + foreach ($specification['columns'] as $column => $referenced) { + $sql_storage_column = $this->getFieldColumnName($definition, $column); + $foreign_keys[$real_name]['columns'][$sql_storage_column] = $referenced; + } + } + + return $foreign_keys; + } + + /** + * Returns the name to be used for the given field column. + * + * @param \Drupal\Core\Field\FieldDefinitionInterface $definition + * The field definition. + * + * @return string + * The column name. + */ + protected function getFieldColumnName(FieldDefinitionInterface $definition, $column) { + $name = $definition->getName(); + return count($definition->getSchema()['columns']) == 1 ? $name : $name . '__' . $column; } /** @@ -204,10 +266,21 @@ protected function getDefaultLangcodeSchema() { * A partial schema array for the base table. */ protected function initializeBaseTable() { - return array( + $key = $this->entityType->getKey('uuid'); + $schema = array( 'description' => "The base table for {$this->entityType->id()} entities.", 'primary key' => array($this->entityType->getKey('id')), + 'unique keys' => array( + $this->getEntityIndexName($key) => array($key), + ), ); + + if ($this->storage->getLayoutType() & ContentEntityDatabaseStorage::LAYOUT_REVISION) { + $key = $this->entityType->getKey('revision'); + $schema['indexes'][$this->getEntityIndexName($key)] = array($key); + } + + return $schema; } /** @@ -218,7 +291,7 @@ protected function initializeBaseTable() { */ protected function initializeRevisionTable() { $id_key = $this->entityType->getKey('id'); - return array( + $schema = array( 'description' => "The revision table for {$this->entityType->id()} entities.", 'primary key' => array($this->entityType->getKey('revision')), 'foreign keys' => array( @@ -228,6 +301,19 @@ protected function initializeRevisionTable() { ), ), ); + + $key = $this->entityType->getKey('id'); + $schema['indexes'][$this->getEntityIndexName($key)] = array($key); + + $key = $this->entityType->getKey('revision'); + $schema['indexes'][$this->getEntityIndexName($key)] = array($key); + + if (isset($this->fieldDefinitions['revision_uid'])) { + $key = 'revision_uid'; + $schema['indexes'][$this->getEntityIndexName($key)] = array($key); + } + + return $schema; } /** @@ -238,7 +324,7 @@ protected function initializeRevisionTable() { */ protected function initializeDataTable() { $id_key = $this->entityType->getKey('id'); - return array( + $schema = array( 'description' => "The data table for {$this->entityType->id()} entities.", // @todo Use the language entity key when https://drupal.org/node/2143729 // is in. @@ -250,6 +336,13 @@ protected function initializeDataTable() { ), ), ); + + if ($this->storage->getLayoutType() & ContentEntityDatabaseStorage::LAYOUT_REVISION) { + $key = $this->entityType->getKey('revision'); + $schema['indexes'][$this->getEntityIndexName($key)] = array($key); + } + + return $schema; } /** @@ -261,7 +354,7 @@ protected function initializeDataTable() { protected function initializeRevisionDataTable() { $id_key = $this->entityType->getKey('id'); $revision_key = $this->entityType->getKey('revision'); - return array( + $schema = array( 'description' => "The revision data table for {$this->entityType->id()} entities.", // @todo Use the language entity key when https://drupal.org/node/2143729 // is in. @@ -277,6 +370,11 @@ protected function initializeRevisionDataTable() { ) ), ); + + $key = $this->entityType->getKey('id'); + $schema['indexes'][$this->getEntityIndexName($key)] = array($key); + + return $schema; } /** @@ -289,12 +387,7 @@ protected function initializeRevisionDataTable() { * A partial schema array for the base table. */ protected function processBaseTable(array &$schema) { - $id_key = $this->entityType->getKey('id'); - // Change the ID field in the base table to 'serial' if it is 'int'. - if ($schema['fields'][$id_key]['type'] == 'int') { - $schema['fields'][$id_key]['type'] = 'serial'; - unset($schema['fields'][$id_key]['default']); - } + $this->processIdentifierSchema($schema, $this->entityType->getKey('id')); } /** @@ -307,13 +400,7 @@ protected function processBaseTable(array &$schema) { * A partial schema array for the base table. */ protected function processRevisionTable(array &$schema) { - $revision_key = $this->entityType->getKey('revision'); - // Change the revision ID field in the revision table 'serial' if it is - // 'int'. - if ($schema['fields'][$revision_key]['type'] == 'int') { - $schema['fields'][$revision_key]['type'] = 'serial'; - unset($schema['fields'][$revision_key]['default']); - } + $this->processIdentifierSchema($schema, $this->entityType->getKey('revision')); } /** @@ -342,4 +429,30 @@ protected function processRevisionDataTable(array &$schema) { $schema['fields']['default_langcode'] = $this->getDefaultLangcodeSchema(); } + /** + * Processes the specified entity key. + * + * @param string $key + * The entity key name. + */ + protected function processIdentifierSchema(&$schema, $key) { + if ($schema['fields'][$key]['type'] == 'int') { + $schema['fields'][$key]['type'] = 'serial'; + } + unset($schema['fields'][$key]['default']); + } + + /** + * Returns the name to be used for the given entity index. + * + * @param string $index + * The index column name. + * + * @return string + * The index name. + */ + protected function getEntityIndexName($index) { + return $this->entityType->id() . '__' . $index; + } + }