diff --git a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php index 222145f..8cdde53 100644 --- a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php @@ -1455,4 +1455,233 @@ static public function _fieldColumnName(FieldConfigInterface $field, $column) { return in_array($column, FieldConfig::getReservedColumns()) ? $column : $field->getName() . '_' . $column; } + /** + * Gets the schema for this entity type. + * + * @return array|null + * Returns the schema definitions for the entity type's tables. If the + * entity type does not have a schema, returns NULL. + * + * @throws \LogicException + */ + public function getSchema() { + $schema = array(); + + $entity_type = $this->getEntityType(); + $entity_type_id = $entity_type->id(); + $entity_keys = array_filter($entity_type->getKeys()); + $entity_keys = array_intersect_key($entity_keys, array_flip(array('id', 'revision', 'bundle', 'uuid'))); + + $tables = array_filter(array( + 'base table' => $entity_type->getBaseTable(), + 'data table' => $entity_type->getDataTable(), + 'revision table' => $entity_type->getRevisionTable(), + 'revision data table' => $entity_type->getRevisionDataTable(), + )); + + // If this entity type does not support storage, no schema information can + // be collected. + if (empty($tables)) { + return; + } + + // Rule out faulty configurations. + if (!isset($tables['base table'])) { + throw new \LogicException("The entity type '$entity_type_id' does not provide a base table."); + } + elseif (isset($tables['revision data table']) && !isset($tables['revision table'])) { + throw new \LogicException("The entity type '$entity_type_id' does not provide a revision table."); + } + elseif (isset($tables['data table']) && isset($tables['revision table']) && !isset($tables['revision data table'])) { + throw new \LogicException("The entity type '$entity_type_id' does not provide a revision data table."); + } + if (isset($tables['revision table']) && !isset($entity_keys['revision'])) { + throw new \LogicException("The entity type '$entity_type_id' does not provide a revision entity key."); + } + // @todo Add a check for the language entity key when + // https://drupal.org/node/2143729 has landed + + // Prepare a mapping of which fields should be stored on which table. This + // information depends on which table types are provided. + if (!isset($tables['data table']) && !isset($tables['revision table'])) { + // In the simplest case all fields are stored in the base table. + $field_table_mapping = array('base table' => array_values($entity_keys)); + $other_fields_tables = array('base table'); + } + elseif (isset($tables['data table']) && !isset($tables['revision table'])) { + // @todo Remove this when https://drupal.org/node/2143729 has landed + $entity_keys['langcode'] = 'langcode'; + + // For translatable, non-revisionable entities the entity key fields + // except for ID and language code are only stored in the base table. All + // other fields are only stored in the data table. The ID is stored in + // both the base table and the data table and the language code is only + // stored in the data table. Additionally, whether or not a language + // variant belongs to the default language code of an entity is tracked in + // the data table as the default language code field and the respective + // default language code is stored as the language code field in the base + // table. + $field_table_mapping = array( + 'base table' => array_values($entity_keys), + 'data table' => array($entity_keys['id'], 'langcode', 'default_langcode'), + ); + $other_fields_tables = array('data table'); + } + elseif (!isset($tables['data table']) && isset($tables['revision table'])) { + // For non-translatable, revisionable entities the entity key fields + // except for ID and revision ID are only stored in the base table. The ID + // is stored in both the base table and the revision table and the + // revision ID is only stored in the revision table. Additionally, the + // default revision ID of an entity is tracked as the revision ID field in + // the base table. + // Other fields are stored in the in the revision table and other fields + // of the default revision are stored in the the base table. + $field_table_mapping = array( + 'base table' => array_values($entity_keys), + 'revision table' => array($entity_keys['id'], $entity_keys['revision']), + ); + $other_fields_tables = array('base table', 'revision table'); + } + else { + // @todo Remove this when https://drupal.org/node/2143729 has landed + $entity_keys['langcode'] = 'langcode'; + + // For translatable, revisionable entities the entity key fields except + // for ID, language code and revision ID are only stored on the base + // table. The ID is stored on the base table, the data table, the revision + // table, and the revision data table. The language code is stored both on + // the data table and the revision data table. Additionally, whether or + // not a language variant belongs to the default language code of an + // entity is tracked in the data table as the default language code field + // and the respective default language code is stored as the language code + // field in the base table. Similarly, whether or not a language variant + // belongs to the default language code of an entity revision is tracked + // in the revision data table as the default language code field and the + // respective default language code is stored as the language code field + // in the revision table. Furthermore, the revision ID is stored both in + // the revision table and the revision data table. Additionally, the + // default revision ID of an entity is tracked as the revision ID field in + // both the base table and the data table. Other fields are stored in the + // revision data table and other fields of the default revision are stored + // in the data table. + $field_table_mapping = array( + 'base table' => array_values($entity_keys), + 'data table' => array($entity_keys['id'], $entity_keys['revision'], 'langcode', 'default_langcode'), + 'revision table' => array($entity_keys['id'], $entity_keys['revision'], 'langcode'), + 'revision data table' => array($entity_keys['id'], $entity_keys['revision'], 'langcode', 'default_langcode'), + ); + $other_fields_tables = array('data table', 'revision data table'); + } + + // Add primary keys and common foreign keys to the tables. + $schema[$tables['base table']]['primary key'] = array($entity_keys['id']); + $schema[$tables['base table']]['foreign keys'] = array(); + if (isset($tables['data table'])) { + $schema[$tables['data table']]['primary key'] = array($entity_keys['id'], 'langcode'); + $schema[$tables['data table']]['foreign keys'][$entity_keys['id']] = array( + 'table' => $tables['base table'], + 'columns' => array($entity_keys['id'] => $entity_keys['id']), + ); + } + if (isset($tables['revision table'])) { + $schema[$tables['revision table']]['primary key'] = array($entity_keys['revision']); + $schema[$tables['revision table']]['foreign keys'][$entity_keys['id']] = array( + 'table' => $tables['base table'], + 'columns' => array($entity_keys['id'] => $entity_keys['id']), + ); + } + if (isset($tables['revision data table'])) { + $schema[$tables['revision data table']]['primary key'] = array($entity_keys['revision'], 'langcode'); + $schema[$tables['revision data table']]['foreign keys'][$entity_keys['id']] = array( + 'table' => $tables['base table'], + 'columns' => array($entity_keys['id'] => $entity_keys['id']), + ); + $schema[$tables['revision data table']]['foreign keys'][$entity_keys['revision']] = array( + 'table' => $tables['revision table'], + 'columns' => array($entity_keys['revision'] => $entity_keys['revision']), + ); + } + + $class = $this->entityClass; + // Invoke ContentEntityInterface::baseFieldDefinitions() directly. Avoid + // info or alter hooks. + foreach($class::baseFieldDefinitions($entity_type) as $field_name => $field_definition) { + /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */ + + $field_schema = $field_definition->getSchema(); + $field_schema['description'] = $field_definition->getDescription(); + + $single_column_field = count($field_schema['columns']) == 1; + $main_property_name = $field_definition->getMainPropertyName(); + + if (in_array($field_name, $entity_keys)) { + foreach ($field_table_mapping as $table_type => $field_names) { + if (in_array($field_name, $field_names)) { + if (!$single_column_field) { + throw new \LogicException("The $field_name field of the $entity_type_id entity type has multiple columns."); + } + $schema[$tables[$table_type]]['fields'][$field_name] = $field_schema['columns'][$main_property_name]; + $schema[$tables[$table_type]]['fields'][$field_name]['description'] = $field_definition->getDescription(); + + if (!empty($field_schema['unique keys'])) { + $schema[$tables[$table_type]]['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[$tables[$table_type]]['indexes'][$field_name] = array($field_name); + } + if (!empty($field_schema['foreign keys'])) { + $schema[$tables[$table_type]]['foreign keys'] += $field_schema['foreign keys']; + } + } + } + } + else { + foreach ($other_fields_tables as $table_type) { + if ($single_column_field) { + $schema[$tables[$table_type]]['fields'][$field_name] = $field_schema['columns'][$main_property_name]; + $schema[$tables[$table_type]]['fields'][$field_name]['description'] = $field_definition->getDescription(); + + if (!empty($field_schema['unique keys'])) { + $schema[$tables[$table_type]]['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[$tables[$table_type]]['indexes'][$field_name] = array($field_name); + } + if (!empty($field_schema['foreign keys'])) { + $schema[$tables[$table_type]]['foreign keys'] += $field_schema['foreign keys']; + } + } + else { + foreach ($field_schema['columns'] as $column_name => $column_schema) { + $schema[$tables[$table_type]]['fields'][$field_name . '__' . $column_name] = $column_schema; + } + + // @todo Unique keys, indexes and foreign keys. + } + } + } + } + + // Change the ID field in the base table to 'serial' if it is 'int'. + if ($schema[$tables['base table']]['fields'][$entity_keys['id']]['type'] == 'int') { + $schema[$tables['base table']]['fields'][$entity_keys['id']]['type'] = 'serial'; + } + // Change the revision ID field in the revision table 'serial' if it is + // 'int'. + if (isset($tables['revision table']) && $schema[$tables['revision table']]['fields'][$entity_keys['revision']]['type'] == 'int') { + $schema[$tables['revision table']]['fields'][$entity_keys['revision']]['type'] = 'serial'; + } + + // Add table descriptions. + foreach ($tables as $table_type => $table) { + $schema[$table]['description'] = "The $table_type table for $entity_type_id entities."; + } + + return $schema; + } + } diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php index 72acdf2..88330b9 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php @@ -133,7 +133,8 @@ public static function baseFieldDefinitions($entity_type) { $fields['type'] = FieldDefinition::create('string') ->setLabel(t('Type')) ->setDescription(t('The bundle of the test entity.')) - ->setRequired(TRUE); + ->setRequired(TRUE) + ->setSetting('max_length', 32); $fields['user_id'] = FieldDefinition::create('entity_reference') ->setLabel(t('User ID')) diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php index 481711f..e881b9a 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php @@ -15,7 +15,6 @@ * label = @Translation("Entity test label callback"), * field_cache = FALSE, * base_table = "entity_test", - * revision_table = "entity_test_revision", * label_callback = "entity_test_label_callback", * fieldable = TRUE, * entity_keys = {