diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index aa158a2..bf19f83 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -969,7 +969,7 @@ function install_base_system(&$install_state) { // Enable the user module so that sessions can be recorded during the // upcoming bootstrap step. - \Drupal::moduleHandler()->install(array('user'), FALSE); + \Drupal::moduleHandler()->install(array('user')); // Save the list of other modules to install for the upcoming tasks. // State can be set to the database now that system.module is installed. diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php index cc97849..9f0585e 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php @@ -8,7 +8,11 @@ namespace Drupal\Core\Entity; use Drupal\Core\Database\Connection; +use Drupal\Core\Database\Database; use Drupal\Core\Entity\Query\QueryInterface; +use Drupal\Core\Entity\Schema\ContentEntitySchemaHandler; +use Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface; +use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Language\Language; use Drupal\field\FieldInfo; use Drupal\field\FieldConfigUpdateForbiddenException; @@ -25,7 +29,28 @@ * 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 { +class ContentEntityDatabaseStorage extends ContentEntityStorageBase implements ContentEntityDatabaseStorageInterface, ContentEntitySchemaHandlerInterface { + + /** + * The base field definitions for this entity type. + * + * @var \Drupal\Core\Field\FieldDefinitionInterface[] + */ + protected $fieldDefinitions; + + /** + * The table layout associated to the entity type. + * + * @var int + */ + protected $layoutType; + + /** + * A mapping of schema fields that will be stored per entity table. + * + * @var array + */ + protected $tableMapping; /** * Name of entity's revision database table field, if it supports revisions. @@ -37,6 +62,22 @@ 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 + */ + protected $baseTable; + + /** * The table that stores revisions, if the entity supports revisions. * * @var string @@ -79,12 +120,27 @@ class ContentEntityDatabaseStorage extends ContentEntityStorageBase { protected $fieldInfo; /** + * The entity manager. + * + * @var \Drupal\Core\Entity\EntityManagerInterface + */ + protected $entityManager; + + /** + * The entity schema builder. + * + * @var \Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface + */ + protected $schemaHandler; + + /** * {@inheritdoc} */ public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { return new static( $entity_type, $container->get('database'), + $container->get('entity.manager'), $container->get('field.info') ); } @@ -96,38 +152,248 @@ public static function createInstance(ContainerInterface $container, EntityTypeI * The entity type definition. * @param \Drupal\Core\Database\Connection $database * The database connection to be used. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. * @param \Drupal\field\FieldInfo $field_info - * The field info service. + * (optional) The field info service. Defaults to NULL as storage can be + * instantiated before Field module is installed. */ - public function __construct(EntityTypeInterface $entity_type, Connection $database, FieldInfo $field_info) { + public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, FieldInfo $field_info = NULL) { parent::__construct($entity_type); $this->database = $database; $this->fieldInfo = $field_info; + $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'); + + // @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; + + // 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'; } - // Check if the entity type supports UUIDs. - $this->uuidKey = $this->entityType->getKey('uuid'); + 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; + /** + * {@inheritdoc} + */ + public function getLayoutType() { + if (!isset($this->layoutType)) { + $this->layoutType = static::LAYOUT_BASE; + if ($this->entityType->hasKey('revision')) { + $this->layoutType |= static::LAYOUT_REVISION; + } + // @todo Remove the data table check once all entity types are using + // entity query and we have a views data controller. See: + // - https://drupal.org/node/2068325 + // - https://drupal.org/node/1740492 + $data_table = $this->entityType->getDataTable(); + if ($data_table && $this->entityType->isTranslatable()) { + $this->layoutType |= static::LAYOUT_MULTILINGUAL; } } + return $this->layoutType; + } + + /** + * {@inheritdoc} + */ + public function getBaseTable() { + return $this->baseTable; + } + + /** + * {@inheritdoc} + */ + public function getRevisionTable() { + return $this->revisionTable; + } + + /** + * {@inheritdoc} + */ + public function getDataTable() { + return $this->dataTable; + } + + /** + * {@inheritdoc} + */ + public function getRevisionDataTable() { + return $this->revisionDataTable; + } + + /** + * {@inheritdoc} + */ + public function getSchema() { + $schema = $this->buildSchema(); + $context = array('entity_type' => $this->entityType); + // TODO Document this hook. + $this->moduleHandler()->alter('entity_schema', $schema, $context); + return $schema; + } + + /** + * Builds the database schema for the related entity type. + * + * @return + * A schema API array. + */ + protected function buildSchema() { + return $this->schemaHandler()->getSchema(); + } + + /** + * Gets the schema handler for this storage controller. + * + * @return \Drupal\Core\Entity\Schema\ContentEntitySchemaHandler + * The schema handler. + */ + protected function schemaHandler() { + if (!isset($this->schemaHandler)) { + $this->schemaHandler = new ContentEntitySchemaHandler($this->entityManager, $this->entityType, $this); + } + return $this->schemaHandler; + } + + /** + * {@inheritdoc} + */ + public function getTableMapping() { + if (!isset($this->tableMapping)) { + $table_mapping = array(); + + $key_fields = array_filter(array($this->idKey, $this->revisionKey, $this->bundleKey, $this->uuidKey, $this->langcodeKey)); + + // Storable fields are single-value base fields that are not defined as + // computed and that do not specify a custom format. + // @todo Add support for multiple-value base fields. + $storable_definitions = array_filter($this->fieldDefinitions, function (FieldDefinitionInterface $field_definition) { + return !$field_definition->isComputed() && !$field_definition->hasCustomStorage() && !$field_definition->isMultiple(); + }); + $storable_fields = array_keys($storable_definitions); + + // @todo Provide automatic definitions for revision metadata fields. + // Rename 'log' to 'revision_log'. + $revision_metadata_fields = array_intersect(array('revision_timestamp', 'revision_uid', 'log'), $storable_fields); + $revisionable_filter_callback = function (FieldDefinitionInterface $definition) { return $definition->isRevisionable(); }; + + switch ($this->getLayoutType()) { + // The base layout stores all the base field values in the base table. + 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 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($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 static::LAYOUT_MULTILINGUAL: + $table_mapping[$this->baseTable] = $this->processFields($key_fields); + $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)); + // Add the denormalized 'default_langcode' field to the mapping. As it + // does not correspond to a field definition we add it with an empty + // key. + $table_mapping[$this->dataTable][''] = array('default_langcode'); + 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 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)); + // Add the denormalized 'default_langcode' field to the mapping. As it + // does not correspond to a field definition we add it with an empty + // key. + $table_mapping[$this->dataTable][''] = array('default_langcode'); + $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)); + // See above. + $table_mapping[$this->revisionDataTable][''] = array('default_langcode'); + break; + } + + $this->tableMapping = $table_mapping; + } + + return $this->tableMapping; + } + + /** + * Returns a mapping between field and column names. + * + * @param array $field_names + * An array of names of fields to map. + * + * @return array + * An associative array of arrays of column names keyed by field name. + */ + protected function processFields($field_names) { + $mapping = array(); + foreach ($field_names as $field_name) { + $columns = isset($this->fieldDefinitions[$field_name]) ? array_keys($this->fieldDefinitions[$field_name]->getColumns()) : array(); + // @todo Remove the entity reference check once we can handle schema + // changes or the Entity reference module stops messing with field + // schema. See: + // - https://drupal.org/node/1498720 + // - https://drupal.org/node/2209981 + foreach ($columns as $index => $column) { + if (!$index || $this->fieldDefinitions[$field_name]->getType() != 'entity_reference') { + $mapping[$field_name][] = $this->schemaHandler()->getFieldColumnName($this->fieldDefinitions[$field_name], $column); + } + } + } + return $mapping; } /** @@ -265,15 +531,18 @@ protected function attachPropertyData(array &$entities) { } $data = $query->execute(); - $field_definitions = \Drupal::entityManager()->getBaseFieldDefinitions($this->entityTypeId); + $field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityTypeId); + + $table_mapping = $this->getTableMapping(); $translations = array(); if ($this->revisionDataTable) { - $data_column_names = array_flip(array_diff(drupal_schema_fields_sql($this->entityType->getRevisionDataTable()), drupal_schema_fields_sql($this->entityType->getBaseTable()))); + $data_column_names = call_user_func_array('array_merge', array_diff_key($table_mapping[$this->revisionDataTable], $table_mapping[$this->baseTable])); } else { - $data_column_names = array_flip(drupal_schema_fields_sql($this->entityType->getDataTable())); + $data_column_names = call_user_func_array('array_merge', $table_mapping[$this->dataTable]); } + $data_column_names = array_combine($data_column_names, $data_column_names); foreach ($data as $values) { $id = $values[$this->idKey]; @@ -403,11 +672,12 @@ protected function buildQuery($ids, $revision_id = FALSE) { } // Add fields from the {entity} table. - $entity_fields = drupal_schema_fields_sql($this->entityType->getBaseTable()); + $table_mapping = $this->getTableMapping(); + $entity_fields = call_user_func_array('array_merge', $table_mapping[$this->baseTable]); if ($this->revisionTable) { // Add all fields from the {entity_revision} table. - $entity_revision_fields = drupal_schema_fields_sql($this->entityType->getRevisionTable()); + $entity_revision_fields = call_user_func_array('array_merge', $table_mapping[$this->revisionTable]); $entity_revision_fields = array_combine($entity_revision_fields, $entity_revision_fields); // The ID field is provided by entity, so remove it. unset($entity_revision_fields[$this->idKey]); @@ -552,7 +822,12 @@ public function save(EntityInterface $entity) { if (!$entity->isNew()) { if ($entity->isDefaultRevision()) { - $return = drupal_write_record($this->entityType->getBaseTable(), $record, $this->idKey); + $this->database + ->update($this->baseTable) + ->fields((array) $record) + ->condition($this->idKey, $record->{$this->idKey}) + ->execute(); + $return = SAVED_UPDATED; } else { // @todo, should a different value be returned when saving an entity @@ -560,13 +835,13 @@ public function save(EntityInterface $entity) { $return = FALSE; } if ($this->revisionTable) { - $record->{$this->revisionKey} = $this->saveRevision($entity); + $entity->{$this->revisionKey}->value = $this->saveRevision($entity); } if ($this->dataTable) { $this->savePropertyData($entity); } if ($this->revisionDataTable) { - $this->savePropertyData($entity, 'revision_data_table'); + $this->savePropertyData($entity, $this->revisionDataTable); } if ($this->revisionTable) { $entity->setNewRevision(FALSE); @@ -584,7 +859,17 @@ public function save(EntityInterface $entity) { // Ensure the entity is still seen as new after assigning it an id, // while storing its data. $entity->enforceIsNew(); - $return = drupal_write_record($this->entityType->getBaseTable(), $record); + $insert_id = $this->database + ->insert($this->baseTable, array('return' => Database::RETURN_INSERT_ID)) + ->fields((array) $record) + ->execute(); + // Even if this is a new entity, the ID key might have been set in which + // case we should not override the provided ID. An empty value for the + // id is interpreted as NULL and thus overriden. + if (empty($record->{$this->idKey})) { + $record->{$this->idKey} = $insert_id; + } + $return = SAVED_NEW; $entity->{$this->idKey}->value = (string) $record->{$this->idKey}; if ($this->revisionTable) { $entity->setNewRevision(); @@ -594,7 +879,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); @@ -628,13 +913,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; @@ -649,7 +935,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)) @@ -662,53 +948,53 @@ protected function savePropertyData(EntityInterface $entity, $table_key = 'data_ /** * Maps from an entity object to the storage record. * - * @param \Drupal\Core\Entity\EntityInterface $entity + * @param \Drupal\Core\Entity\ContentEntityInterface $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(ContentEntityInterface $entity, $table_name = NULL) { + if (!isset($table_name)) { + $table_name = $this->baseTable; + } + $record = new \stdClass(); - $values = array(); - $definitions = $entity->getFieldDefinitions(); - $schema = drupal_get_schema($this->entityType->get($table_key)); $is_new = $entity->isNew(); + $table_mapping = $this->getTableMapping(); + + foreach ($table_mapping[$table_name] as $name => $storage_columns) { + if (!empty($this->fieldDefinitions[$name])) { + $definition = $this->fieldDefinitions[$name]; + foreach ($definition->getColumns() as $column => $column_info) { + $value = isset($entity->$name->$column) ? $entity->$name->$column : NULL; + + // We allow for storage controllers to have schema fields which are + // not entity fields, which can be useful for normalization purposes. + // The 'default_langcode' field, for example, is simply a + // denormalization of checking the language code in the data table + // against the language code in the base table and similarly the + // 'isDefaultRevision' field (which we add as a query expression in + // self::buildQuery()) is denormalization of checking the revision ID + // in the revision table against the revision ID in the base table. + if (!empty($column_info['serialize'])) { + $value = serialize($value); + } - $multi_column_fields = array(); - foreach (drupal_schema_fields_sql($this->entityType->get($table_key)) as $name) { - // Check for fields which store data in multiple columns and process them - // separately. - if ($field = strstr($name, '__', TRUE)) { - $multi_column_fields[$field] = TRUE; - continue; - } - $values[$name] = isset($definitions[$name]) && isset($entity->$name->value) ? $entity->$name->value : NULL; - } - - // Handle fields that store multiple properties and match each property name - // to its schema column name. - foreach (array_keys($multi_column_fields) as $field_name) { - $field_items = $entity->get($field_name); - $field_value = $field_items->getValue(); - foreach (array_keys($field_items->getFieldDefinition()->getColumns()) as $field_schema_column) { - if (isset($schema['fields'][$field_name . '__' . $field_schema_column])) { - $values[$field_name . '__' . $field_schema_column] = isset($field_value[0][$field_schema_column]) ? $field_value[0][$field_schema_column] : NULL; + // If we are creating a new entity, we must not populate the record + // with NULL values otherwise defaults would not be applied. + if (isset($value) || !$is_new) { + list(, $storage_column) = each($storage_columns); + if (!empty($storage_column)) { + $record->$storage_column = drupal_schema_get_field_value($definition->getSchema()['columns'][$column], $value); + } + } } } } - foreach ($values as $field_name => $value) { - // If we are creating a new entity, we must not populate the record with - // NULL values otherwise defaults would not be applied. - if (isset($value) || !$is_new) { - $record->$field_name = drupal_schema_get_field_value($schema['fields'][$field_name], $value); - } - } - return $record; } @@ -717,15 +1003,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; @@ -741,12 +1029,20 @@ 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); $entity->preSaveRevision($this, $record); if ($entity->isNewRevision()) { - drupal_write_record($this->revisionTable, $record); + $insert_id = $this->database + ->insert($this->revisionTable, array('return' => Database::RETURN_INSERT_ID)) + ->fields((array) $record) + ->execute(); + // Even if this is a new revsision, the revision ID key might have been + // set in which case we should not override the provided revision ID. + if (!isset($record->{$this->revisionKey})) { + $record->{$this->revisionKey} = $insert_id; + } if ($entity->isDefaultRevision()) { $this->database->update($this->entityType->getBaseTable()) ->fields(array($this->revisionKey => $record->{$this->revisionKey})) @@ -755,7 +1051,11 @@ protected function saveRevision(EntityInterface $entity) { } } else { - drupal_write_record($this->revisionTable, $record, $this->revisionKey); + $this->database + ->update($this->revisionTable) + ->fields((array) $record) + ->condition($this->revisionKey, $record->{$this->revisionKey}) + ->execute(); } // Make sure to update the new revision key for the entity. diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorageInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorageInterface.php new file mode 100644 index 0000000..1f31ec9 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorageInterface.php @@ -0,0 +1,103 @@ +sqlQuery = $sql_query; + $this->entityManager = \Drupal::entityManager(); } /** @@ -57,7 +66,6 @@ public function __construct(SelectInterface $sql_query) { */ public function addField($field, $type, $langcode) { $entity_type_id = $this->sqlQuery->getMetaData('entity_type'); - $entity_manager = \Drupal::entityManager(); $field_info = FieldInfo::fieldInfo(); $age = $this->sqlQuery->getMetaData('age'); // This variable ensures grouping works correctly. For example: @@ -74,7 +82,7 @@ public function addField($field, $type, $langcode) { // This will contain the definitions of the last specifier seen by the // system. $propertyDefinitions = array(); - $entity_type = $entity_manager->getDefinition($entity_type_id); + $entity_type = $this->entityManager->getDefinition($entity_type_id); // Use the lightweight and fast field map for checking whether a specifier // is a field or not. While calling field_info_field() on every specifier // delivers the same information, if no specifiers are using the field API @@ -141,7 +149,7 @@ public function addField($field, $type, $langcode) { if ($bundle_key = $entity_type->getKey('bundle')) { $values[$bundle_key] = reset($field_map[$entity_type_id][$field_name]['bundles']); } - $entity = $entity_manager + $entity = $this->entityManager ->getStorage($entity_type_id) ->create($values); $propertyDefinitions = $entity->$field_name->getFieldDefinition()->getPropertyDefinitions(); @@ -174,10 +182,10 @@ public function addField($field, $type, $langcode) { $entity_tables = array(); if ($data_table = $entity_type->getDataTable()) { $this->sqlQuery->addMetaData('simple_query', FALSE); - $entity_tables[$data_table] = drupal_get_schema($data_table); + $entity_tables[$data_table] = $this->getTableMapping($data_table, $entity_type_id); } $entity_base_table = $entity_type->getBaseTable(); - $entity_tables[$entity_base_table] = drupal_get_schema($entity_base_table); + $entity_tables[$entity_base_table] = $this->getTableMapping($entity_base_table, $entity_type_id); $sql_column = $specifier; $table = $this->ensureEntityTable($index_prefix, $specifier, $type, $langcode, $base_table, $entity_id_field, $entity_tables); } @@ -195,7 +203,7 @@ public function addField($field, $type, $langcode) { $bundles = entity_get_bundles($entity_type_id); $values[$bundle_key] = key($bundles); } - $entity = $entity_manager + $entity = $this->entityManager ->getStorage($entity_type_id) ->create($values); $propertyDefinitions = $entity->$specifier->getFieldDefinition()->getPropertyDefinitions(); @@ -206,7 +214,7 @@ public function addField($field, $type, $langcode) { if (isset($propertyDefinitions[$relationship_specifier]) && $entity->get($specifier)->first()->get('entity') instanceof EntityReference) { // If it is, use the entity type. $entity_type_id = $propertyDefinitions[$relationship_specifier]->getTargetDefinition()->getEntityTypeId(); - $entity_type = $entity_manager->getDefinition($entity_type_id); + $entity_type = $this->entityManager->getDefinition($entity_type_id); // Add the new entity base table using the table and sql column. $join_condition= '%alias.' . $entity_type->getKey('id') . " = $table.$sql_column"; $base_table = $this->sqlQuery->leftJoin($entity_type->getBaseTable(), NULL, $join_condition); @@ -230,8 +238,8 @@ public function addField($field, $type, $langcode) { * @throws \Drupal\Core\Entity\Query\QueryException */ protected function ensureEntityTable($index_prefix, $property, $type, $langcode, $base_table, $id_field, $entity_tables) { - foreach ($entity_tables as $table => $schema) { - if (isset($schema['fields'][$property])) { + foreach ($entity_tables as $table => $mapping) { + if (isset($mapping[$property])) { if (!isset($this->entityTables[$index_prefix . $table])) { $this->entityTables[$index_prefix . $table] = $this->addJoin($type, $table, "%alias.$id_field = $base_table.$id_field", $langcode); } @@ -272,4 +280,28 @@ protected function addJoin($type, $table, $join_condition, $langcode) { return $this->sqlQuery->addJoin($type, $table, NULL, $join_condition, $arguments); } + /** + * Returns the schema for the given table. + * + * @param string $table + * The table name. + * + * @return array|bool + * The table field mapping for the given table or FALSE if not available. + */ + protected function getTableMapping($table, $entity_type_id) { + $storage = $this->entityManager->getStorage($entity_type_id); + // @todo Stop calling drupal_get_schema() once menu links are converted + // to the Entity Field API. See https://drupal.org/node/1842858. + $schema = drupal_get_schema($table); + if (!$schema && $storage instanceof SqlStorageInterface) { + $mapping = $storage->getTableMapping(); + $mapping = isset($mapping[$table]) ? call_user_func_array('array_merge', $mapping[$table]) : FALSE; + } + else { + $mapping = array_keys($schema['fields']); + } + return array_flip($mapping); + } + } diff --git a/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php new file mode 100644 index 0000000..bd96c6c --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php @@ -0,0 +1,474 @@ +entityType = $entity_type; + $this->fieldDefinitions = $entity_manager->getBaseFieldDefinitions($entity_type->id()); + $this->storage = $storage; + } + + /** + * {@inheritdoc} + */ + public function getSchema() { + // Prepare basic information about the entity type. + $tables = $this->getTables(); + // If this entity type does not support storage, no schema information can + // be collected. + if (empty($tables)) { + $this->schema[$this->entityType->id()] = NULL; + } + + if (!isset($this->schema[$this->entityType->id()])) { + // Initialize the table schema. + $schema[$tables['base_table']] = $this->initializeBaseTable(); + if (isset($tables['revision_table'])) { + $schema[$tables['revision_table']] = $this->initializeRevisionTable(); + } + if (isset($tables['data_table'])) { + $schema[$tables['data_table']] = $this->initializeDataTable(); + } + if (isset($tables['revision_data_table'])) { + $schema[$tables['revision_data_table']] = $this->initializeRevisionDataTable(); + } + + // Add the schema from field definitions. + foreach ($this->storage->getTableMapping() as $table => $field_names) { + foreach ($field_names as $field_name => $column_names) { + // If there are table fields which do not correspond to entity field + // definitions, those are stored with the '' key. + if ($field_name !== '') { + $this->addFieldSchema($schema[$table], $field_name, $column_names); + } + else { + foreach ($column_names as $column_name) { + // Support hardcoded non-entity-field table fields. + if ($column_name == 'default_langcode') { + $this->addDefaultLangcodeSchema($schema[$table]); + } + } + } + } + } + + // Process tables after having gathered field information. + $this->processBaseTable($schema[$tables['base_table']]); + if (isset($tables['revision_table'])) { + $this->processRevisionTable($schema[$tables['revision_table']]); + } + if (isset($tables['data_table'])) { + $this->processDataTable($schema[$tables['data_table']]); + } + if (isset($tables['revision_data_table'])) { + $this->processRevisionDataTable($schema[$tables['revision_data_table']]); + } + + $this->schema[$this->entityType->id()] = $schema; + } + + return $this->schema[$this->entityType->id()]; + } + + /** + * Gets a list of entity type tables. + * + * @return array + * A list of entity type tables, keyed by table key. + */ + protected function getTables() { + return array_filter(array( + 'base_table' => $this->storage->getBaseTable(), + 'revision_table' => $this->storage->getRevisionTable(), + 'data_table' => $this->storage->getDataTable(), + 'revision_data_table' => $this->storage->getRevisionDataTable(), + )); + } + + /** + * Returns the schema for a single field definition. + * + * @param array $schema + * The table schema to add the field schema to, passed by reference. + * @param string $field_name + * The name of the field. + */ + protected function addFieldSchema(array &$schema, $field_name) { + $definition = $this->fieldDefinitions[$field_name]; + $field_schema = $definition->getSchema(); + $index = 0; + + foreach ($field_schema['columns'] as $column_name => $column_schema) { + // @todo Remove the entity reference check once we can handle schema + // changes or the Entity reference module stops messing with field + // schema. See: + // - https://drupal.org/node/1498720 + // - https://drupal.org/node/2209981 + if ($definition->getType() == 'entity_reference' && $index++ > 0) { + continue; + } + else { + $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'][$schema_field_name]['not null'] = (bool) $this->entityType->getKey($field_name); + + if (!empty($field_schema['indexes'])) { + $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'])) { + $foreign_keys = $this->getFieldForeignKeys($definition); + $schema['foreign keys'] = !empty($schema['foreign keys']) ? array_merge($schema['foreign keys'], $foreign_keys) : $foreign_keys; + } + } + } + + /** + * 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); + } + } + + 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. + */ + public function getFieldColumnName(FieldDefinitionInterface $definition, $column) { + $name = $definition->getName(); + // @todo Remove the entity reference check once we can handle schema + // changes or the Entity reference module stops messing with field + // schema. See: + // - https://drupal.org/node/1498720 + // - https://drupal.org/node/2209981 + return count($definition->getSchema()['columns']) == 1 || $definition->getType() == 'entity_reference' ? $name : $name . '__' . $column; + } + + /** + * Returns the schema for the 'default_langcode' metadata field. + * + * @param array $schema + * The table schema to add the field schema to, passed by reference. + * + * @return array + * A schema field array for the 'default_langcode' metadata field. + */ + protected function addDefaultLangcodeSchema(&$schema) { + $schema['fields']['default_langcode'] = array( + 'description' => 'Boolean indicating whether field values are in the default entity language.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 1, + ); + } + + /** + * Initializes common information for a base table. + * + * @return array + * A partial schema array for the base table. + */ + protected function initializeBaseTable() { + $schema = array( + 'description' => "The base table for {$this->entityType->id()} entities.", + 'primary key' => array($this->entityType->getKey('id')), + ); + if ($key = $this->entityType->getKey('uuid')) { + $schema['unique keys'] = array( + $this->getEntityIndexName($key) => array($key), + ); + } + + if ($this->storage->getLayoutType() & ContentEntityDatabaseStorage::LAYOUT_REVISION) { + $key = $this->entityType->getKey('revision'); + $schema['unique keys'][$this->getEntityIndexName($key)] = array($key); + } + + return $schema; + } + + /** + * Initializes common information for a revision table. + * + * @return array + * A partial schema array for the revision table. + */ + protected function initializeRevisionTable() { + $id_key = $this->entityType->getKey('id'); + $revision_key = $this->entityType->getKey('revision'); + + $schema = array( + 'description' => "The revision table for {$this->entityType->id()} entities.", + 'primary key' => array($revision_key), + 'foreign keys' => array( + $id_key => array( + 'table' => $this->entityType->getBaseTable(), + 'columns' => array($id_key => $id_key), + ), + ), + ); + + $schema['indexes'][$this->getEntityIndexName($id_key)] = array($id_key); + + $schema['indexes'][$this->getEntityIndexName($revision_key)] = array($revision_key); + + if (isset($this->fieldDefinitions['revision_uid'])) { + $schema['indexes'][$this->getEntityIndexName('revision_uid')] = array('revision_uid'); + } + + return $schema; + } + + /** + * Initializes common information for a data table. + * + * @return array + * A partial schema array for the data table. + */ + protected function initializeDataTable() { + $id_key = $this->entityType->getKey('id'); + $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. + 'primary key' => array($id_key, 'langcode'), + 'foreign keys' => array( + $id_key => array( + 'table' => $this->entityType->getBaseTable(), + 'columns' => array($id_key => $id_key), + ), + ), + ); + + if ($this->storage->getLayoutType() & ContentEntityDatabaseStorage::LAYOUT_REVISION) { + $key = $this->entityType->getKey('revision'); + $schema['indexes'][$this->getEntityIndexName($key)] = array($key); + } + + return $schema; + } + + /** + * Initializes common information for a revision data table. + * + * @return array + * A partial schema array for the revision data table. + */ + protected function initializeRevisionDataTable() { + $id_key = $this->entityType->getKey('id'); + $revision_key = $this->entityType->getKey('revision'); + + $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. + 'primary key' => array($revision_key, 'langcode'), + 'foreign keys' => array( + $id_key => array( + 'table' => $this->entityType->getBaseTable(), + 'columns' => array($id_key => $id_key), + ), + $revision_key => array( + 'table' => $this->entityType->getRevisionTable(), + 'columns' => array($revision_key => $revision_key), + ) + ), + ); + + $schema['indexes'][$this->getEntityIndexName($id_key)] = array($id_key); + + return $schema; + } + + /** + * Processes the gathered schema for a base table. + * + * @param array $schema + * The table schema, passed by reference. + * + * @return array + * A partial schema array for the base table. + */ + protected function processBaseTable(array &$schema) { + $this->processIdentifierSchema($schema, $this->entityType->getKey('id')); + } + + /** + * Processes the gathered schema for a base table. + * + * @param array $schema + * The table schema, passed by reference. + * + * @return array + * A partial schema array for the base table. + */ + protected function processRevisionTable(array &$schema) { + $this->processIdentifierSchema($schema, $this->entityType->getKey('revision')); + } + + /** + * Processes the gathered schema for a base table. + * + * @param array $schema + * The table schema, passed by reference. + * + * @return array + * A partial schema array for the base table. + */ + protected function processDataTable(array &$schema) { + } + + /** + * Processes the gathered schema for a base table. + * + * @param array $schema + * The table schema, passed by reference. + * + * @return array + * A partial schema array for the base table. + */ + protected function processRevisionDataTable(array &$schema) { + } + + /** + * 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; + } + +} diff --git a/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandlerInterface.php b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandlerInterface.php new file mode 100644 index 0000000..5b958d8 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandlerInterface.php @@ -0,0 +1,21 @@ +getSettings(); $target_type_info = \Drupal::entityManager()->getDefinition($settings['target_type']); + if (!$target_type_info) { + throw new \Exception(String::format('Target type @entity_type does not exist.', array('@target_type' => $settings['target_type']))); + } + if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) { // @todo: Lookup the entity type's ID data type and use it here. // https://drupal.org/node/2107249 @@ -98,6 +103,9 @@ public static function mainPropertyName() { public static function schema(FieldDefinitionInterface $field_definition) { $target_type = $field_definition->getSetting('target_type'); $target_type_info = \Drupal::entityManager()->getDefinition($target_type); + if (!$target_type_info) { + throw new \Exception(String::format('Target type @entity_type does not exist.', array('@entity_type' => $target_type))); + } if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) { $columns = array( diff --git a/core/modules/aggregator/aggregator.install b/core/modules/aggregator/aggregator.install index e4bef0e..2b755e1 100644 --- a/core/modules/aggregator/aggregator.install +++ b/core/modules/aggregator/aggregator.install @@ -22,179 +22,3 @@ function aggregator_requirements($phase) { } return $requirements; } - -/** - * Implements hook_schema(). - */ -function aggregator_schema() { - $schema['aggregator_feed'] = array( - 'description' => 'Stores feeds to be parsed by the aggregator.', - 'fields' => array( - 'fid' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique feed ID.', - ), - 'uuid' => array( - 'description' => 'Unique Key: Universally unique identifier for this entity.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - 'title' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Title of the feed.', - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this feed.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'url' => array( - 'type' => 'text', - 'not null' => TRUE, - 'description' => 'URL to the feed.', - ), - 'refresh' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'How often to check for new feed items, in seconds.', - ), - 'checked' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Last time feed was checked for new items, as Unix timestamp.', - ), - 'queued' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Time when this feed was queued for refresh, 0 if not queued.', - ), - 'link' => array( - 'type' => 'text', - 'not null' => TRUE, - 'description' => 'The parent website of the feed; comes from the element in the feed.', - ), - 'description' => array( - 'type' => 'text', - 'not null' => TRUE, - 'size' => 'big', - 'description' => "The parent website's description; comes from the element in the feed.", - ), - 'image' => array( - 'type' => 'text', - 'not null' => TRUE, - 'size' => 'big', - 'description' => 'An image representing the feed.', - ), - 'hash' => array( - 'type' => 'varchar', - 'length' => 64, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Calculated hash of the feed data, used for validating cache.', - ), - 'etag' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Entity tag HTTP response header, used for validating cache.', - ), - 'modified' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'When the feed was last modified, as a Unix timestamp.', - ), - ), - 'primary key' => array('fid'), - 'indexes' => array( - 'url' => array(array('url', 255)), - 'queued' => array('queued'), - ), - 'unique keys' => array( - 'title' => array('title'), - ), - ); - - $schema['aggregator_item'] = array( - 'description' => 'Stores the individual items imported from feeds.', - 'fields' => array( - 'iid' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique ID for feed item.', - ), - 'fid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {aggregator_feed}.fid to which this item belongs.', - ), - 'title' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Title of the feed item.', - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this feed item.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'link' => array( - 'type' => 'text', - 'not null' => TRUE, - 'description' => 'Link to the feed item.', - ), - 'author' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Author of the feed item.', - ), - 'description' => array( - 'type' => 'text', - 'not null' => TRUE, - 'size' => 'big', - 'description' => 'Body of the feed item.', - ), - 'timestamp' => array( - 'type' => 'int', - 'not null' => FALSE, - 'description' => 'Posted date of the feed item, as a Unix timestamp.', - ), - 'guid' => array( - 'type' => 'text', - 'not null' => TRUE, - 'description' => 'Unique identifier for the feed item.', - ) - ), - 'primary key' => array('iid'), - 'indexes' => array( - 'fid' => array('fid'), - 'timestamp' => array('timestamp'), - ), - 'foreign keys' => array( - 'aggregator_feed' => array( - 'table' => 'aggregator_feed', - 'columns' => array('fid' => 'fid'), - ), - ), - ); - - return $schema; -} diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php index daa3fba..4093037 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php @@ -183,11 +183,13 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['checked'] = FieldDefinition::create('timestamp') ->setLabel(t('Checked')) - ->setDescription(t('Last time feed was checked for new items, as Unix timestamp.')); + ->setDescription(t('Last time feed was checked for new items, as Unix timestamp.')) + ->setSetting('default_value', 0); $fields['queued'] = FieldDefinition::create('timestamp') ->setLabel(t('Queued')) - ->setDescription(t('Time when this feed was queued for refresh, 0 if not queued.')); + ->setDescription(t('Time when this feed was queued for refresh, 0 if not queued.')) + ->setSetting('default_value', 0); $fields['link'] = FieldDefinition::create('uri') ->setLabel(t('Link')) diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Item.php b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Item.php index f12ed40..804d42a 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Item.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Item.php @@ -78,8 +78,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setLabel(t('Author')) ->setDescription(t('The author of the feed item.')); - // @todo Convert to a text field in https://drupal.org/node/2149845. - $fields['description'] = FieldDefinition::create('string') + $fields['description'] = FieldDefinition::create('string_long') ->setLabel(t('Description')) ->setDescription(t('The body of the feed item.')); @@ -88,7 +87,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setDescription(t('Posted date of the feed item, as a Unix timestamp.')); // @todo Convert to a real UUID field in https://drupal.org/node/2149851. - $fields['guid'] = FieldDefinition::create('string') + $fields['guid'] = FieldDefinition::create('string_long') ->setLabel(t('GUID')) ->setDescription(t('Unique identifier for the feed item.')); diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/Views/IntegrationTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/Views/IntegrationTest.php index 3f4d7b5..bd8f17c 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/Views/IntegrationTest.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/Views/IntegrationTest.php @@ -21,7 +21,7 @@ class IntegrationTest extends ViewUnitTestBase { * * @var array */ - public static $modules = array('aggregator', 'aggregator_test_views', 'system', 'entity', 'field'); + public static $modules = array('aggregator', 'aggregator_test_views', 'system', 'entity', 'field', 'options'); /** * Views used by this test. @@ -55,7 +55,8 @@ public static function getInfo() { protected function setUp() { parent::setUp(); - $this->installSchema('aggregator', array('aggregator_item', 'aggregator_feed')); + $this->installEntitySchema('aggregator_item'); + $this->installEntitySchema('aggregator_feed'); ViewTestData::createTestViews(get_class($this), array('aggregator_test_views')); diff --git a/core/modules/block/custom_block/custom_block.install b/core/modules/block/custom_block/custom_block.install index 6c27dd9..f7042b3 100644 --- a/core/modules/block/custom_block/custom_block.install +++ b/core/modules/block/custom_block/custom_block.install @@ -8,123 +8,6 @@ use Drupal\Core\Entity\EntityTypeInterface; /** - * Implements hook_schema(). - */ -function custom_block_schema() { - $schema = array(); - $schema['custom_block'] = array( - 'description' => 'Stores contents of custom-made blocks.', - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => "The block's {custom_block}.id.", - ), - 'uuid' => array( - 'description' => 'Unique Key: Universally unique identifier for this entity.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - 'info' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Block description.', - ), - // Defaults to NULL in order to avoid a brief period of potential - // deadlocks on the index. - 'revision_id' => array( - 'description' => 'The current {block_custom_revision}.revision_id version identifier.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => NULL, - ), - 'type' => array( - 'description' => 'The type of this custom block.', - 'type' => 'varchar', - 'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH, - 'not null' => TRUE, - 'default' => '', - ), - 'changed' => array( - 'description' => 'The Unix timestamp when the custom block was most recently saved.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this node.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - ), - 'primary key' => array('id'), - 'indexes' => array( - 'block_custom_type' => array('type'), - ), - 'unique keys' => array( - 'revision_id' => array('revision_id'), - 'uuid' => array('uuid'), - 'info' => array('info'), - ), - 'foreign keys' => array( - 'custom_block_revision' => array( - 'table' => 'custom_block_revision', - 'columns' => array('revision_id' => 'revision_id'), - ), - ), - ); - - $schema['custom_block_revision'] = array( - 'description' => 'Stores contents of custom-made blocks.', - 'fields' => array( - 'id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => "The block's {custom_block}.id.", - ), - // Defaults to NULL in order to avoid a brief period of potential - // deadlocks on the index. - 'revision_id' => array( - 'description' => 'The current version identifier.', - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'log' => array( - 'description' => 'The log entry explaining the changes in this version.', - 'type' => 'text', - 'not null' => TRUE, - 'size' => 'big', - ), - 'info' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Block description.', - ), - 'changed' => array( - 'description' => 'The Unix timestamp when the version was most recently saved.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('revision_id'), - ); - return $schema; -} - -/** * Implements hook_schema_0(). */ function custom_block_schema_0() { diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install index 40fb1a0..1929b7b 100644 --- a/core/modules/comment/comment.install +++ b/core/modules/comment/comment.install @@ -28,229 +28,3 @@ function comment_install() { // @see \Drupal\comment\CommentStorage::updateEntityStatistics(). \Drupal::state()->set('comment.maintain_entity_statistics', TRUE); } - -/** - * Implements hook_schema(). - */ -function comment_schema() { - $schema['comment'] = array( - 'description' => 'Stores comments and associated data.', - 'fields' => array( - 'cid' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique comment ID.', - ), - 'uuid' => array( - 'description' => 'Unique Key: Universally unique identifier for this entity.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - 'pid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {comment}.cid to which this comment is a reply. If set to 0, this comment is not a reply to an existing comment.', - ), - 'entity_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The entity_id of the entity to which this comment is a reply.', - ), - 'entity_type' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'default' => 'node', - 'length' => 255, - 'description' => 'The entity_type of the entity to which this comment is a reply.', - ), - 'field_id' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'default' => 'node.comment', - 'length' => 255, - 'description' => 'The field_id of the field that was used to add this comment.', - ), - 'uid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {users}.uid who authored the comment. If set to 0, this comment was created by an anonymous user.', - ), - 'subject' => array( - 'type' => 'varchar', - 'length' => 64, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The comment title.', - ), - 'hostname' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => "The author's host name.", - ), - 'created' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The time that the comment was created, as a Unix timestamp.', - ), - 'changed' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The time that the comment was last edited, as a Unix timestamp.', - ), - 'status' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 1, - 'size' => 'tiny', - 'description' => 'The published status of a comment. (0 = Not Published, 1 = Published)', - ), - 'thread' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'description' => "The alphadecimal representation of the comment's place in a thread, consisting of a base 36 string prefixed by an integer indicating its length.", - ), - 'name' => array( - 'type' => 'varchar', - 'length' => 60, - 'not null' => FALSE, - 'description' => "The comment author's name. Uses {users}.name if the user is logged in, otherwise uses the value typed into the comment form.", - ), - 'mail' => array( - 'type' => 'varchar', - 'length' => 64, - 'not null' => FALSE, - 'description' => "The comment author's e-mail address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on.", - ), - 'homepage' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => FALSE, - 'description' => "The comment author's home page address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on.", - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this comment.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - ), - 'indexes' => array( - 'comment_status_pid' => array('pid', 'status'), - 'comment_num_new' => array( - 'entity_id', - array('entity_type', 32), - array('field_id', 32), - 'status', - 'created', - 'cid', - 'thread', - ), - 'comment_uid' => array('uid'), - 'comment_entity_langcode' => array( - 'entity_id', - array('entity_type', 32), - array('field_id', 32), - 'langcode', - ), - 'comment_created' => array('created'), - ), - 'primary key' => array('cid'), - 'unique keys' => array( - 'uuid' => array('uuid'), - ), - 'foreign keys' => array( - 'comment_author' => array( - 'table' => 'users', - 'columns' => array('uid' => 'uid'), - ), - ), - ); - - $schema['comment_entity_statistics'] = array( - 'description' => 'Maintains statistics of entity and comments posts to show "new" and "updated" flags.', - 'fields' => array( - 'entity_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The entity_id of the entity for which the statistics are compiled.', - ), - 'entity_type' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'default' => 'node', - 'length' => 255, - 'description' => 'The entity_type of the entity to which this comment is a reply.', - ), - 'field_id' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'default' => 'node__comment', - 'length' => 255, - 'description' => 'The field_id of the field that was used to add this comment.', - ), - 'cid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {comment}.cid of the last comment.', - ), - 'last_comment_timestamp' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The Unix timestamp of the last comment that was posted within this node, from {comment}.changed.', - ), - 'last_comment_name' => array( - 'type' => 'varchar', - 'length' => 60, - 'not null' => FALSE, - 'description' => 'The name of the latest author to post a comment on this node, from {comment}.name.', - ), - 'last_comment_uid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The user ID of the latest author to post a comment on this node, from {comment}.uid.', - ), - 'comment_count' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The total number of comments on this entity.', - ), - ), - 'primary key' => array('entity_id', array('entity_type', 32), array('field_id', 32)), - 'indexes' => array( - 'last_comment_timestamp' => array('last_comment_timestamp'), - 'comment_count' => array('comment_count'), - 'last_comment_uid' => array('last_comment_uid'), - ), - 'foreign keys' => array( - 'last_comment_author' => array( - 'table' => 'users', - 'columns' => array( - 'last_comment_uid' => 'uid', - ), - ), - ), - ); - - return $schema; -} diff --git a/core/modules/comment/lib/Drupal/comment/CommentStorage.php b/core/modules/comment/lib/Drupal/comment/CommentStorage.php index d05b2c3..02a411f 100644 --- a/core/modules/comment/lib/Drupal/comment/CommentStorage.php +++ b/core/modules/comment/lib/Drupal/comment/CommentStorage.php @@ -9,6 +9,7 @@ use Drupal\Core\Database\Connection; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\field\FieldInfo; @@ -36,13 +37,15 @@ class CommentStorage extends ContentEntityDatabaseStorage implements CommentStor * An array of entity info for the entity type. * @param \Drupal\Core\Database\Connection $database * The database connection to be used. - * @param \Drupal\field\FieldInfo $field_info - * The field info service. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. * @param \Drupal\comment\CommentStatisticsInterface $comment_statistics * The comment statistics service. + * @param \Drupal\field\FieldInfo $field_info + * The field info service. */ - public function __construct(EntityTypeInterface $entity_info, Connection $database, FieldInfo $field_info, CommentStatisticsInterface $comment_statistics) { - parent::__construct($entity_info, $database, $field_info); + public function __construct(EntityTypeInterface $entity_info, Connection $database, EntityManagerInterface $entity_manager, CommentStatisticsInterface $comment_statistics, FieldInfo $field_info) { + parent::__construct($entity_info, $database, $entity_manager, $field_info); $this->statistics = $comment_statistics; } @@ -53,8 +56,9 @@ public static function createInstance(ContainerInterface $container, EntityTypeI return new static( $entity_info, $container->get('database'), - $container->get('field.info'), - $container->get('comment.statistics') + $container->get('entity.manager'), + $container->get('comment.statistics'), + $container->get('field.info') ); } @@ -127,4 +131,86 @@ public function getChildCids(array $comments) { ->fetchCol(); } + /** + * {@inheritdoc} + */ + protected function buildSchema() { + $schema = parent::buildSchema(); + + $schema['comment_entity_statistics'] = array( + 'description' => 'Maintains statistics of entity and comments posts to show "new" and "updated" flags.', + 'fields' => array( + 'entity_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The entity_id of the entity for which the statistics are compiled.', + ), + 'entity_type' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'default' => 'node', + 'length' => 255, + 'description' => 'The entity_type of the entity to which this comment is a reply.', + ), + 'field_id' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'default' => 'node__comment', + 'length' => 255, + 'description' => 'The field_id of the field that was used to add this comment.', + ), + 'cid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The {comment}.cid of the last comment.', + ), + 'last_comment_timestamp' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The Unix timestamp of the last comment that was posted within this node, from {comment}.changed.', + ), + 'last_comment_name' => array( + 'type' => 'varchar', + 'length' => 60, + 'not null' => FALSE, + 'description' => 'The name of the latest author to post a comment on this node, from {comment}.name.', + ), + 'last_comment_uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The user ID of the latest author to post a comment on this node, from {comment}.uid.', + ), + 'comment_count' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The total number of comments on this entity.', + ), + ), + 'primary key' => array('entity_id', array('entity_type', 32), array('field_id', 32)), + 'indexes' => array( + 'last_comment_timestamp' => array('last_comment_timestamp'), + 'comment_count' => array('comment_count'), + 'last_comment_uid' => array('last_comment_uid'), + ), + 'foreign keys' => array( + 'last_comment_author' => array( + 'table' => 'users', + 'columns' => array( + 'last_comment_uid' => 'uid', + ), + ), + ), + ); + + return $schema; + } + } diff --git a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php index 6dd943f..81d18bd 100644 --- a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php +++ b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php @@ -280,7 +280,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['status'] = FieldDefinition::create('boolean') ->setLabel(t('Publishing status')) - ->setDescription(t('A boolean indicating whether the comment is published.')); + ->setDescription(t('A boolean indicating whether the comment is published.')) + ->setSetting('default_value', TRUE); $fields['thread'] = FieldDefinition::create('string') ->setLabel(t('Thread place')) diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentValidationTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentValidationTest.php index 881c965..ae613db 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentValidationTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentValidationTest.php @@ -38,7 +38,7 @@ public static function getInfo() { */ public function setUp() { parent::setUp(); - $this->installSchema('node', array('node', 'node_field_data', 'node_field_revision', 'node_revision')); + $this->installEntitySchema('node'); } /** diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportRecreateTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportRecreateTest.php index 3f86c9b..4f4d5ca 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportRecreateTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportRecreateTest.php @@ -29,7 +29,7 @@ class ConfigImportRecreateTest extends DrupalUnitTestBase { * * @var array */ - public static $modules = array('system', 'entity', 'field', 'text', 'node'); + public static $modules = array('system', 'entity', 'field', 'text', 'node', 'user'); public static function getInfo() { return array( @@ -43,7 +43,7 @@ public function setUp() { parent::setUp(); $this->installSchema('system', 'config_snapshot'); - $this->installSchema('node', 'node'); + $this->installEntitySchema('node'); $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging')); diff --git a/core/modules/content_translation/content_translation.pages.inc b/core/modules/content_translation/content_translation.pages.inc index 16864bf..4d2fefc 100644 --- a/core/modules/content_translation/content_translation.pages.inc +++ b/core/modules/content_translation/content_translation.pages.inc @@ -37,8 +37,8 @@ function content_translation_overview(EntityInterface $entity) { // Determine whether the current entity is translatable. $translatable = FALSE; - foreach (field_info_instances($entity->getEntityTypeId(), $entity->bundle()) as $instance) { - if ($instance->isTranslatable()) { + foreach (\Drupal::entityManager()->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) { + if ($field_definition->isTranslatable()) { $translatable = TRUE; break; } diff --git a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php index 753745f..0ae29c8 100644 --- a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php +++ b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php @@ -27,7 +27,7 @@ class EditTestBase extends DrupalUnitTestBase { protected function setUp() { parent::setUp(); - $this->installSchema('entity_test', array('entity_test', 'entity_test_rev')); + $this->installEntitySchema('entity_test'); $this->installConfig(array('field', 'filter')); } diff --git a/core/modules/editor/lib/Drupal/editor/Tests/EditorFileUsageTest.php b/core/modules/editor/lib/Drupal/editor/Tests/EditorFileUsageTest.php index 3be50ec..40af4bc 100644 --- a/core/modules/editor/lib/Drupal/editor/Tests/EditorFileUsageTest.php +++ b/core/modules/editor/lib/Drupal/editor/Tests/EditorFileUsageTest.php @@ -31,8 +31,8 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('node', array('node', 'node_access', 'node_field_data', 'node_field_revision', 'node_revision')); - $this->installSchema('file', array('file_managed', 'file_usage')); + $this->installEntitySchema('node'); + $this->installEntitySchema('file'); // Add text formats. $filtered_html_format = entity_create('filter_format', array( diff --git a/core/modules/entity/entity.api.php b/core/modules/entity/entity.api.php new file mode 100644 index 0000000..94730f0 --- /dev/null +++ b/core/modules/entity/entity.api.php @@ -0,0 +1,49 @@ +create(array( + 'name' => t('General discussion'), + 'description' => '', + 'parent' => array(0), + 'vid' => 'forums', + 'forum_container' => 0, + ))->save(); + } +} + +/** + * Allows to react the uninstallation of entity schema. + * + * After modules have been uninstalled Entity module checks for any removed + * entity types and installs the entity schema if the entity type's storage + * supports automatic schema generation. This hook is invoked after the entity + * schema has been uninstalled. + * + * @param \Drupal\Core\Entity\EntityStorageInterface[]|\Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface[] $storages + * An array of entity storages of the entity types whose schemas have been + * uninstalled keyed by entity type ID. + */ +function hook_entity_schema_uninstalled(array $storages) { + if (isset($storages['node'])) { + \Drupal::database()->schema()->dropTable('mymodule_node_data'); + } +} diff --git a/core/modules/entity/entity.module b/core/modules/entity/entity.module index 1fa3852..978a43b 100644 --- a/core/modules/entity/entity.module +++ b/core/modules/entity/entity.module @@ -9,6 +9,7 @@ */ use Drupal\Core\Config\Entity\ConfigEntityStorage; +use Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface; /** * Implements hook_help(). @@ -99,6 +100,35 @@ function entity_entity_bundle_delete($entity_type_id, $bundle) { } /** + * Implements hook_modules_installed(). + */ +function entity_modules_installed($modules) { + // Install entity type tables. + $entity_manager = \Drupal::entityManager(); + $schema = \Drupal::database()->schema(); + $definitions = $entity_manager->getDefinitions(); + $storages = array(); + + foreach ($modules as $module) { + foreach ($definitions as $entity_type_id => $entity_type) { + if ($entity_type->getProvider() == $module) { + $storage = $entity_manager->getStorage($entity_type_id); + if ($storage instanceof ContentEntitySchemaHandlerInterface) { + foreach ($storage->getSchema() as $table_name => $table_schema) { + if (!$schema->tableExists($table_name)) { + $schema->createTable($table_name, $table_schema); + $storages[$entity_type_id] = $storage; + } + } + } + } + } + } + + \Drupal::moduleHandler()->invokeAll('entity_schema_installed', array($storages)); +} + +/** * Implements hook_module_preuninstall(). */ function entity_module_preuninstall($module) { @@ -112,3 +142,32 @@ function entity_module_preuninstall($module) { } } } + +/** + * Implements hook_modules_uninstalled(). + */ +function entity_modules_uninstalled($modules) { + $entity_manager = \Drupal::entityManager(); + $schema = \Drupal::database()->schema(); + $definitions = $entity_manager->getDefinitions(); + $storages = array(); + + foreach ($modules as $module) { + foreach ($definitions as $entity_type_id => $entity_type) { + if ($entity_type->getProvider() == $module) { + // Remove entity tables. + $storage = $entity_manager->getStorage($entity_type_id); + if ($storage instanceof ContentEntitySchemaHandlerInterface) { + foreach ($storage->getSchema() as $table_name => $table_schema) { + if ($schema->tableExists($table_name)) { + $schema->dropTable($table_name); + $storages[$entity_type_id] = $storage; + } + } + } + } + } + } + + \Drupal::moduleHandler()->invokeAll('entity_schema_uninstalled', array($storages)); +} diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php index 31ea4e8..de443b4 100644 --- a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php +++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php @@ -261,7 +261,7 @@ public function testBaseFieldComponent() { */ public function testRenameDeleteBundle() { $this->enableModules(array('field_test', 'node', 'system', 'text')); - $this->installSchema('node', array('node')); + $this->installEntitySchema('node'); // Create a node bundle, display and form display object. entity_create('node_type', array('type' => 'article'))->save(); diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php b/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php index e681856..372f2f8 100644 --- a/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/ConfigurableEntityReferenceItem.php @@ -14,6 +14,7 @@ use Drupal\Core\TypedData\AllowedValuesInterface; use Drupal\Core\TypedData\DataDefinition; use Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraint; +use Drupal\field\FieldConfigInterface; /** * Alternative plugin implementation of the 'entity_reference' field type. @@ -142,7 +143,7 @@ public static function schema(FieldDefinitionInterface $field_definition) { $target_type = $field_definition->getSetting('target_type'); $target_type_info = \Drupal::entityManager()->getDefinition($target_type); - if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) { + if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface') && $field_definition instanceof FieldConfigInterface) { $schema['columns']['revision_id'] = array( 'description' => 'The revision ID of the target entity.', 'type' => 'int', diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php index 5105a20..9d67882 100644 --- a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php @@ -11,7 +11,6 @@ use Drupal\Core\Database\Query\SelectInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\FieldDefinitionInterface; -use Drupal\Component\Utility\NestedArray; use Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface; /** @@ -53,10 +52,11 @@ public function __construct(FieldDefinitionInterface $field_definition, EntityIn * {@inheritdoc} */ public static function settingsForm(FieldDefinitionInterface $field_definition) { - $target_type = $field_definition->getSetting('target_type'); + $entity_manager = \Drupal::entityManager(); + $entity_type_id = $field_definition->getSetting('target_type'); $selection_handler_settings = $field_definition->getSetting('handler_settings') ?: array(); - $entity_type = \Drupal::entityManager()->getDefinition($target_type); - $bundles = entity_get_bundles($target_type); + $entity_type = $entity_manager->getDefinition($entity_type_id); + $bundles = $entity_manager->getBundleInfo($entity_type_id); // Merge-in default values. $selection_handler_settings += array( @@ -75,10 +75,10 @@ public static function settingsForm(FieldDefinitionInterface $field_definition) $target_bundles_title = t('Bundles'); // Default core entity types with sensible labels. - if ($target_type == 'node') { + if ($entity_type_id == 'node') { $target_bundles_title = t('Content types'); } - elseif ($target_type == 'taxonomy_term') { + elseif ($entity_type_id == 'taxonomy_term') { $target_bundles_title = t('Vocabularies'); } @@ -100,18 +100,26 @@ public static function settingsForm(FieldDefinitionInterface $field_definition) ); } - $target_type_info = \Drupal::entityManager()->getDefinition($target_type); - if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) { - // @todo Use Entity::getFieldDefinitions() when all entity types are - // converted to the new Field API. - $fields = drupal_schema_fields_sql($entity_type->getBaseTable()); - $fields = array_combine($fields, $fields); - foreach (field_info_instances($target_type) as $bundle_instances) { - foreach ($bundle_instances as $instance_name => $instance) { - foreach ($instance->getField()->getColumns() as $column_name => $column_info) { - $fields[$instance_name . '.' . $column_name] = t('@label (@column)', array('@label' => $instance->getLabel(), '@column' => $column_name)); + if ($entity_type->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) { + $fields = array(); + foreach (array_keys($bundles) as $bundle) { + $bundle_fields = array_filter($entity_manager->getFieldDefinitions($entity_type_id, $bundle), function ($field_definition) { + return !$field_definition->isComputed(); + }); + foreach ($bundle_fields as $instance_name => $field_definition) { + /* @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */ + $columns = $field_definition->getColumns(); + // If there is more than one column, display them all, otherwise just + // display the field label. + // @todo: Use property labels instead of the column name. + if (count($columns) > 1) { + foreach ($field_definition->getColumns() as $column_name => $column_info) { + $fields[$instance_name . '.' . $column_name] = t('@label (@column)', array('@label' => $field_definition->getLabel(), '@column' => $column_name)); + } + } + else { + $fields[$instance_name] = t('@label', array('@label' => $field_definition->getLabel())); } - } } diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAdminTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAdminTest.php index 37cf7bb..545bc1b 100644 --- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAdminTest.php +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceAdminTest.php @@ -13,6 +13,17 @@ * Tests the Entity Reference Admin UI. */ class EntityReferenceAdminTest extends WebTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('node', 'field_ui', 'entity_reference'); + + /** + * {@inheritdoc} + */ public static function getInfo() { return array( 'name' => 'Entity Reference admin UI', @@ -21,47 +32,22 @@ public static function getInfo() { ); } - public static $modules = array('node', 'field_ui', 'entity_reference'); - public function setUp() { parent::setUp(); // Create test user. - $this->admin_user = $this->drupalCreateUser(array('access content', 'administer node fields')); - $this->drupalLogin($this->admin_user); + $admin_user = $this->drupalCreateUser(array('access content', 'administer node fields')); + $this->drupalLogin($admin_user); - // Create content type, with underscores. + // Create a content type, with underscores. $type_name = strtolower($this->randomName(8)) . '_test'; $type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name)); $this->type = $type->type; } - protected function assertFieldSelectOptions($name, $expected_options) { - $xpath = $this->buildXPathQuery('//select[@name=:name]', array(':name' => $name)); - $fields = $this->xpath($xpath); - if ($fields) { - $field = $fields[0]; - $options = $this->getAllOptionsList($field); - return $this->assertIdentical($options, $expected_options); - } - else { - return $this->fail(t('Unable to find field @name', array('@name' => $name))); - } - } - /** - * Extract all the options of a select element. + * Tests the Entity Reference Admin UI. */ - protected function getAllOptionsList($element) { - $options = array(); - // Add all options items. - foreach ($element->option as $option) { - $options[] = (string) $option['value']; - } - // TODO: support optgroup. - return $options; - } - public function testFieldAdminHandler() { $bundle_path = 'admin/structure/types/manage/' . $this->type; @@ -76,9 +62,7 @@ public function testFieldAdminHandler() { $this->assertFieldByName('field[settings][target_type]', 'node'); // Check that all entity types can be referenced. - foreach (\Drupal::entityManager()->getDefinitions() as $entity_type_id => $entity_type) { - $this->assertFieldByXPath("//select[@name='field[settings][target_type]']/option[@value='" . $entity_type_id . "']"); - } + $this->assertFieldSelectOptions('field[settings][target_type]', array_keys(\Drupal::entityManager()->getDefinitions())); // Second step: 'Instance settings' form. $this->drupalPostForm(NULL, array(), t('Save field settings')); @@ -102,8 +86,17 @@ public function testFieldAdminHandler() { // Option 1: sort by field. $this->drupalPostAjaxForm(NULL, array('instance[settings][handler_settings][sort][field]' => 'nid'), 'instance[settings][handler_settings][sort][field]'); $this->assertFieldByName('instance[settings][handler_settings][sort][direction]', 'ASC'); + + // Test that a non-translatable base field is a sort option. + $this->assertFieldByXPath("//select[@name='instance[settings][handler_settings][sort][field]']/option[@value='nid']"); + // Test that a translatable base field is a sort option. + $this->assertFieldByXPath("//select[@name='instance[settings][handler_settings][sort][field]']/option[@value='title']"); + // Test that a configurable field is a sort option. + $this->assertFieldByXPath("//select[@name='instance[settings][handler_settings][sort][field]']/option[@value='body.value']"); + // Set back to no sort. $this->drupalPostAjaxForm(NULL, array('instance[settings][handler_settings][sort][field]' => '_none'), 'instance[settings][handler_settings][sort][field]'); + $this->assertNoFieldByName('instance[settings][handler_settings][sort][direction]'); // Third step: confirm. $this->drupalPostForm(NULL, array( @@ -113,4 +106,52 @@ public function testFieldAdminHandler() { // Check that the field appears in the overview form. $this->assertFieldByXPath('//table[@id="field-overview"]//tr[@id="field-test"]/td[1]', 'Test label', 'Field was created and appears in the overview page.'); } + + /** + * Checks if a select element contains the specified options. + * + * @param string $name + * The field name. + * @param array $expected_options + * An array of expected options. + * + * @return bool + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertFieldSelectOptions($name, array $expected_options) { + $xpath = $this->buildXPathQuery('//select[@name=:name]', array(':name' => $name)); + $fields = $this->xpath($xpath); + if ($fields) { + $field = $fields[0]; + $options = $this->getAllOptionsList($field); + return $this->assertIdentical($options, $expected_options); + } + else { + return $this->fail('Unable to find field ' . $name); + } + } + + /** + * Extracts all options from a select element. + * + * @param \SimpleXMLElement $element + * The select element field information. + * + * @return array + * An array of option values as strings. + */ + protected function getAllOptionsList(\SimpleXMLElement $element) { + $options = array(); + // Add all options items. + foreach ($element->option as $option) { + $options[] = (string) $option['value']; + } + + if (isset($element->optgroup)) { + $options += $this->getAllOptionsList($element->optgroup); + } + + return $options; + } + } diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php index f035f37..f032080 100644 --- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php @@ -75,7 +75,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision')); + $this->installEntitySchema('entity_test_rev'); // Setup a field and instance. entity_reference_create_instance( diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php index 8c33d47..0586e7c 100644 --- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php @@ -52,8 +52,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('taxonomy', 'taxonomy_term_data'); - $this->installSchema('taxonomy', 'taxonomy_term_hierarchy'); + $this->installEntitySchema('taxonomy_term'); $this->vocabulary = entity_create('taxonomy_vocabulary', array( 'name' => $this->randomName(), diff --git a/core/modules/field/field.views.inc b/core/modules/field/field.views.inc index c142163..05b65ef 100644 --- a/core/modules/field/field.views.inc +++ b/core/modules/field/field.views.inc @@ -9,6 +9,7 @@ use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\field\FieldConfigInterface; +use Drupal\field\FieldInstanceConfigInterface; /** * Implements hook_views_data(). @@ -81,8 +82,10 @@ function field_views_field_label($entity_type, $field_name) { $label_counter = array(); $all_labels = array(); // Count the amount of instances per label per field. - $instances = field_info_instances($entity_type); - foreach ($instances as $bundle => $bundle_instances) { + foreach (array_keys(\Drupal::entityManager()->getBundleInfo($entity_type)) as $bundle) { + $bundle_instances = array_filter(\Drupal::entityManager()->getFieldDefinitions($entity_type, $bundle), function ($field_definition) { + return $field_definition instanceof FieldInstanceConfigInterface; + }); if (isset($bundle_instances[$field_name])) { $instance = $bundle_instances[$field_name]; $label = $instance->getLabel(); diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php index 9e42126..7c46cd8 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php @@ -38,7 +38,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision')); + $this->installEntitySchema('entity_test_rev'); $this->createFieldWithInstance(); } diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php index 4b0e85e..74464dc 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\field\Tests\FieldAttachStorageTest. + * Contains \Drupal\field\Tests\FieldAttachStorageTest. */ namespace Drupal\field\Tests; @@ -36,7 +36,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision')); + $this->installEntitySchema('entity_test_rev'); } /** @@ -146,8 +146,10 @@ function testFieldAttachLoadMultiple() { foreach ($bundles as $index => $bundle) { $entities[$index] = entity_create($entity_type, array('id' => $index, 'revision_id' => $index, 'type' => $bundle)); $entity = clone($entities[$index]); - $instances = field_info_instances($entity_type, $bundle); - foreach ($instances as $field_name => $instance) { + foreach ($field_names as $field_name) { + if (!$entity->hasField($field_name)) { + continue; + } $values[$index][$field_name] = mt_rand(1, 127); $entity->$field_name->setValue(array('value' => $values[$index][$field_name])); } @@ -160,8 +162,10 @@ function testFieldAttachLoadMultiple() { $controller->resetCache(); $entities = $controller->loadMultiple(); foreach ($entities as $index => $entity) { - $instances = field_info_instances($entity_type, $bundles[$index]); - foreach ($instances as $field_name => $instance) { + foreach ($field_names as $field_name) { + if (!$entity->hasField($field_name)) { + continue; + } // The field value loaded matches the one inserted. $this->assertEqual($entity->{$field_name}->value, $values[$index][$field_name], format_string('Entity %index: expected value was found.', array('%index' => $index))); // The value added in hook_field_load() is found. diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php index 4999a83..5c9c32d 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php @@ -177,7 +177,6 @@ function testInstancePrepare() { function testInstanceDisabledEntityType() { // Disabling a module invokes user_modules_uninstalled() and calls // drupal_flush_all_caches(). Install the necessary schema to support this. - $this->installSchema('user', array('users_data')); $this->installSchema('system', array('router')); // For this test the field type and the entity type must be exposed by diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php index 5f77d92..be551be 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php @@ -36,9 +36,9 @@ */ function setUp() { parent::setUp(); - $this->installSchema('entity_test', 'entity_test'); + $this->installEntitySchema('entity_test'); + $this->installEntitySchema('user'); $this->installSchema('system', array('sequences', 'config_snapshot')); - $this->installSchema('user', array('users', 'users_roles')); // Set default storage backend and configure the theme system. $this->installConfig(array('field', 'system')); diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php index ba00c74..fb52e72 100644 --- a/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php @@ -33,7 +33,7 @@ class TranslationWebTest extends FieldTestBase { * * @var string */ - protected $entity_type = 'entity_test_rev'; + protected $entity_type = 'entity_test_mulrev'; /** * The field to use in this test. @@ -78,7 +78,7 @@ function setUp() { 'bundle' => $this->entity_type, ); entity_create('field_instance_config', $instance)->save(); - $this->instance = entity_load('field_instance_config', 'entity_test.' . $instance['bundle'] . '.' . $this->field_name); + $this->instance = entity_load('field_instance_config', $this->entity_type . '.' . $instance['bundle'] . '.' . $this->field_name); entity_get_form_display($this->entity_type, $this->entity_type, 'default') ->setComponent($this->field_name) diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php index 523f1b0..958d15a 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php @@ -15,6 +15,7 @@ use Drupal\field_ui\OverviewBase; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\field\Entity\FieldConfig; +use Drupal\field\FieldInstanceConfigInterface; /** * Field UI field overview form. @@ -79,7 +80,9 @@ public function buildForm(array $form, array &$form_state, $entity_type_id = NUL parent::buildForm($form, $form_state, $entity_type_id, $bundle); // Gather bundle information. - $instances = field_info_instances($this->entity_type, $this->bundle); + $instances = array_filter(\Drupal::entityManager()->getFieldDefinitions($this->entity_type, $this->bundle), function ($field_definition) { + return $field_definition instanceof FieldInstanceConfigInterface; + }); $field_types = $this->fieldTypeManager->getDefinitions(); // Field prefix. diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc index d07bf9b..f9aa9f9 100644 --- a/core/modules/file/file.field.inc +++ b/core/modules/file/file.field.inc @@ -6,8 +6,8 @@ */ use Drupal\Component\Utility\Html; +use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Render\Element; -use Drupal\field\FieldConfigInterface; /** * Returns HTML for an individual file upload widget. @@ -214,14 +214,14 @@ function theme_file_upload_help($variables) { /** * Determine whether a field references files stored in {file_managed}. * - * @param Drupal\field\FieldConfigInterface $field + * @param \Drupal\Core\Field\FieldDefinitionInterface $field * A field definition. * - * @return + * @return bool * The field column if the field references {file_managed}.fid, typically - * fid, FALSE if it doesn't. + * fid, FALSE if it does not. */ -function file_field_find_file_reference_column(FieldConfigInterface $field) { +function file_field_find_file_reference_column(FieldDefinitionInterface $field) { $schema = $field->getSchema(); foreach ($schema['foreign keys'] as $data) { if ($data['table'] == 'file_managed') { diff --git a/core/modules/file/file.install b/core/modules/file/file.install index fb5a4eb..6dc06a2 100644 --- a/core/modules/file/file.install +++ b/core/modules/file/file.install @@ -6,157 +6,6 @@ */ /** - * Implements hook_schema(). - */ -function file_schema() { - $schema['file_managed'] = array( - 'description' => 'Stores information for uploaded files.', - 'fields' => array( - 'fid' => array( - 'description' => 'File ID.', - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'uuid' => array( - 'description' => 'Unique Key: Universally unique identifier for this entity.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - 'uid' => array( - 'description' => 'The {users}.uid of the user who is associated with the file.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'filename' => array( - 'description' => 'Name of the file with no path components. This may differ from the basename of the URI if the file is renamed to avoid overwriting an existing file.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'uri' => array( - 'description' => 'The URI to access the file (either local or remote).', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'binary' => TRUE, - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this file.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'filemime' => array( - 'description' => "The file's MIME type.", - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'filesize' => array( - 'description' => 'The size of the file in bytes.', - 'type' => 'int', - 'size' => 'big', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'status' => array( - 'description' => 'A field indicating the status of the file. Two status are defined in core: temporary (0) and permanent (1). Temporary files older than DRUPAL_MAXIMUM_TEMP_FILE_AGE will be removed during a cron run.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - ), - 'created' => array( - 'description' => 'UNIX timestamp for when the file added.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'changed' => array( - 'description' => 'UNIX timestamp for when the file was last changed.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'indexes' => array( - 'uid' => array('uid'), - 'status' => array('status'), - 'changed' => array('changed'), - ), - 'unique keys' => array( - 'uuid' => array('uuid'), - 'uri' => array('uri'), - ), - 'primary key' => array('fid'), - 'foreign keys' => array( - 'file_owner' => array( - 'table' => 'users', - 'columns' => array('uid' => 'uid'), - ), - ), - ); - - $schema['file_usage'] = array( - 'description' => 'Track where a file is used.', - 'fields' => array( - 'fid' => array( - 'description' => 'File ID.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'module' => array( - 'description' => 'The name of the module that is using the file.', - 'type' => 'varchar', - 'length' => DRUPAL_EXTENSION_NAME_MAX_LENGTH, - 'not null' => TRUE, - 'default' => '', - ), - 'type' => array( - 'description' => 'The name of the object type in which the file is used.', - 'type' => 'varchar', - 'length' => 64, - 'not null' => TRUE, - 'default' => '', - ), - 'id' => array( - 'description' => 'The primary key of the object using the file.', - 'type' => 'varchar', - 'length' => 64, - 'not null' => TRUE, - 'default' => 0, - ), - 'count' => array( - 'description' => 'The number of times this file is used by this object.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('fid', 'type', 'id', 'module'), - 'indexes' => array( - 'type_id' => array('type', 'id'), - 'fid_count' => array('fid', 'count'), - 'fid_module' => array('fid', 'module'), - ), - ); - return $schema; -} - -/** * Implements hook_requirements(). * * Display information about getting upload progress bars working. diff --git a/core/modules/file/file.module b/core/modules/file/file.module index b707b57..4370d74 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -254,6 +254,7 @@ function file_move(File $source, $destination = NULL, $replace = FILE_EXISTS_REN $existing = reset($existing_files); $delete_source = TRUE; $file->fid = $existing->id(); + $file->uuid = $existing->uuid(); } } // If we are renaming around an existing file (rather than a directory), @@ -1880,13 +1881,12 @@ function file_get_file_references(File $file, $field = NULL, $age = EntityStorag if (!isset($file_fields[$entity_type_id][$bundle])) { $file_fields[$entity_type_id][$bundle] = array(); // This contains the possible field names. - $instances = field_info_instances($entity_type_id, $bundle); - foreach ($instances as $field_name => $instance) { - $field_type = $instance->getType(); + foreach ($entity->getFieldDefinitions() as $field_name => $field_definition) { + $field_type = $field_definition->getType(); // If this is the first time this field type is seen, check // whether it references files. if (!isset($field_columns[$field_type])) { - $field_columns[$field_type] = file_field_find_file_reference_column($instance->getField()); + $field_columns[$field_type] = file_field_find_file_reference_column($field_definition); } // If the field type does reference files then record it. if ($field_columns[$field_type]) { diff --git a/core/modules/file/lib/Drupal/file/FileStorage.php b/core/modules/file/lib/Drupal/file/FileStorage.php index c6f2ccc..f8fc4d7 100644 --- a/core/modules/file/lib/Drupal/file/FileStorage.php +++ b/core/modules/file/lib/Drupal/file/FileStorage.php @@ -38,4 +38,60 @@ public function retrieveTemporaryFiles() { ':changed' => REQUEST_TIME - DRUPAL_MAXIMUM_TEMP_FILE_AGE )); } + + /** + * {@inheritdoc} + */ + protected function buildSchema() { + $schema = parent::buildSchema(); + + $schema['file_usage'] = array( + 'description' => 'Track where a file is used.', + 'fields' => array( + 'fid' => array( + 'description' => 'File ID.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'module' => array( + 'description' => 'The name of the module that is using the file.', + 'type' => 'varchar', + 'length' => DRUPAL_EXTENSION_NAME_MAX_LENGTH, + 'not null' => TRUE, + 'default' => '', + ), + 'type' => array( + 'description' => 'The name of the object type in which the file is used.', + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), + 'id' => array( + 'description' => 'The primary key of the object using the file.', + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => 0, + ), + 'count' => array( + 'description' => 'The number of times this file is used by this object.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('fid', 'type', 'id', 'module'), + 'indexes' => array( + 'type_id' => array('type', 'id'), + 'fid_count' => array('fid', 'count'), + 'fid_module' => array('fid', 'module'), + ), + ); + + return $schema; + } + } diff --git a/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php b/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php index 0f40a14..af73f03 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php @@ -42,8 +42,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('file', 'file_managed'); - $this->installSchema('file', 'file_usage'); + $this->installEntitySchema('file'); entity_create('field_config', array( 'name' => 'file_test', diff --git a/core/modules/file/lib/Drupal/file/Tests/FileManagedTestBase.php b/core/modules/file/lib/Drupal/file/Tests/FileManagedTestBase.php index c777691..91edf6b 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileManagedTestBase.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileManagedTestBase.php @@ -150,22 +150,19 @@ function assertSameFile(FileInterface $file1, FileInterface $file2) { * File entity. */ function createFile($filepath = NULL, $contents = NULL, $scheme = NULL) { - $file = new \stdClass(); - $file->uri = $this->createUri($filepath, $contents, $scheme); - $file->filename = drupal_basename($file->uri); - $file->filemime = 'text/plain'; - $file->uid = 1; - $file->created = REQUEST_TIME; - $file->changed = REQUEST_TIME; - $file->filesize = filesize($file->uri); - $file->status = 0; + // Don't count hook invocations caused by creating the file. + \Drupal::state()->set('file_test.count_hook_invocations', FALSE); + $file = entity_create('file', array( + 'uri' => $this->createUri($filepath, $contents, $scheme), + 'uid' => 1, + )); + $file->save(); // Write the record directly rather than using the API so we don't invoke // the hooks. - $file = (array) $file; - $file['fid'] = db_insert('file_managed') - ->fields($file) - ->execute(); - return entity_create('file', $file); + $this->assertTrue($file->id() > 0, 'The file was added to the database.', 'Create test file'); + + \Drupal::state()->set('file_test.count_hook_invocations', TRUE); + return $file; } /** diff --git a/core/modules/file/lib/Drupal/file/Tests/FileManagedUnitTestBase.php b/core/modules/file/lib/Drupal/file/Tests/FileManagedUnitTestBase.php index c9ec86d..1930297 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FileManagedUnitTestBase.php +++ b/core/modules/file/lib/Drupal/file/Tests/FileManagedUnitTestBase.php @@ -29,8 +29,8 @@ function setUp() { file_test_reset(); $this->installConfig(array('system')); - $this->installSchema('file', array('file_managed', 'file_usage')); - $this->installSchema('user', array('users', 'users_roles')); + $this->installEntitySchema('file'); + $this->installEntitySchema('user'); // Make sure that a user with uid 1 exists, self::createFile() relies on // it. @@ -161,20 +161,19 @@ function assertSameFile(FileInterface $file1, FileInterface $file2) { * File entity. */ function createFile($filepath = NULL, $contents = NULL, $scheme = NULL) { - $file = new \stdClass(); - $file->uri = $this->createUri($filepath, $contents, $scheme); - $file->filename = drupal_basename($file->uri); - $file->filemime = 'text/plain'; - $file->uid = 1; - $file->created = REQUEST_TIME; - $file->changed = REQUEST_TIME; - $file->filesize = filesize($file->uri); - $file->status = 0; + // Don't count hook invocations caused by creating the file. + \Drupal::state()->set('file_test.count_hook_invocations', FALSE); + $file = entity_create('file', array( + 'uri' => $this->createUri($filepath, $contents, $scheme), + 'uid' => 1, + )); + $file->save(); // Write the record directly rather than using the API so we don't invoke // the hooks. - $this->assertNotIdentical(drupal_write_record('file_managed', $file), FALSE, 'The file was added to the database.', 'Create test file'); + $this->assertTrue($file->id() > 0, 'The file was added to the database.', 'Create test file'); - return entity_create('file', (array) $file); + \Drupal::state()->set('file_test.count_hook_invocations', TRUE); + return $file; } /** diff --git a/core/modules/file/lib/Drupal/file/Tests/SpaceUsedTest.php b/core/modules/file/lib/Drupal/file/Tests/SpaceUsedTest.php index a0c8e33..bfedef6 100644 --- a/core/modules/file/lib/Drupal/file/Tests/SpaceUsedTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/SpaceUsedTest.php @@ -23,20 +23,40 @@ function setUp() { parent::setUp(); // Create records for a couple of users with different sizes. - $file = array('uid' => 2, 'uri' => 'public://example1.txt', 'filesize' => 50, 'status' => FILE_STATUS_PERMANENT); - db_insert('file_managed')->fields($file)->execute(); - $file = array('uid' => 2, 'uri' => 'public://example2.txt', 'filesize' => 20, 'status' => FILE_STATUS_PERMANENT); - db_insert('file_managed')->fields($file)->execute(); - $file = array('uid' => 3, 'uri' => 'public://example3.txt', 'filesize' => 100, 'status' => FILE_STATUS_PERMANENT); - db_insert('file_managed')->fields($file)->execute(); - $file = array('uid' => 3, 'uri' => 'public://example4.txt', 'filesize' => 200, 'status' => FILE_STATUS_PERMANENT); - db_insert('file_managed')->fields($file)->execute(); + $this->createFileWithSize('public://example1.txt', 50, 2); + $this->createFileWithSize('public://example2.txt', 20, 2); + $this->createFileWithSize('public://example3.txt', 100, 3); + $this->createFileWithSize('public://example4.txt', 200, 3); // Now create some non-permanent files. - $file = array('uid' => 2, 'uri' => 'public://example5.txt', 'filesize' => 1, 'status' => 0); - db_insert('file_managed')->fields($file)->execute(); - $file = array('uid' => 3, 'uri' => 'public://example6.txt', 'filesize' => 3, 'status' => 0); - db_insert('file_managed')->fields($file)->execute(); + $this->createFileWithSize('public://example5.txt', 1, 2, 0); + $this->createFileWithSize('public://example6.txt', 3, 3, 0); + } + + /** + * Creates a file with a given size. + * + * @param string $uri + * URI of the file to create. + * @param int $size + * Size of the file. + * @param int $uid + * File owner ID. + * @param int $status + * Whether the file should be permanent or temporary. + * + * @return \Drupal\Core\Entity\EntityInterface + * The file entity. + */ + protected function createFileWithSize($uri, $size, $uid, $status = FILE_STATUS_PERMANENT) { + file_put_contents($uri, $this->randomName($size)); + $file = entity_create('file', array( + 'uri' => $uri, + 'uid' => $uid, + 'status' => $status, + )); + $file->save(); + return $file; } /** diff --git a/core/modules/file/tests/file_test/file_test.module b/core/modules/file/tests/file_test/file_test.module index e5a10a0..5fcd15f 100644 --- a/core/modules/file/tests/file_test/file_test.module +++ b/core/modules/file/tests/file_test/file_test.module @@ -108,9 +108,11 @@ function file_test_get_all_calls() { * @see file_test_reset() */ function _file_test_log_call($op, $args) { - $results = \Drupal::state()->get('file_test.results') ?: array(); - $results[$op][] = $args; - \Drupal::state()->set('file_test.results', $results); + if (\Drupal::state()->get('file_test.count_hook_invocations', TRUE)) { + $results = \Drupal::state()->get('file_test.results') ?: array(); + $results[$op][] = $args; + \Drupal::state()->set('file_test.results', $results); + } } /** diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php index 9d8f3ca..7423b70 100644 --- a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php +++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php @@ -30,7 +30,7 @@ function setUp() { // filter_permission() calls into url() to output a link in the description. $this->installSchema('system', 'url_alias'); - $this->installSchema('user', array('users_roles')); + $this->installEntitySchema('user'); // Install filter_test module, which ships with custom default format. $this->installConfig(array('user', 'filter_test')); diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install index 7f975e9..70b53b0 100644 --- a/core/modules/forum/forum.install +++ b/core/modules/forum/forum.install @@ -35,16 +35,6 @@ function forum_install() { ), ))->save(); - // Create a default forum so forum posts can be created. - $term = entity_create('taxonomy_term', array( - 'name' => t('General discussion'), - 'description' => '', - 'parent' => array(0), - 'vid' => 'forums', - 'forum_container' => 0, - )); - $term->save(); - // Create the instance on the bundle. entity_create('field_instance_config', array( 'field_name' => 'taxonomy_forums', @@ -68,7 +58,6 @@ function forum_install() { 'weight' => 10, )) ->save(); - entity_get_display('node', 'forum', 'teaser') ->setComponent('taxonomy_forums', array( 'type' => 'taxonomy_term_reference_link', @@ -89,6 +78,22 @@ function forum_install() { } /** + * Implements hook_entity_schema_installed(). + */ +function forum_entity_schema_installed(array $storages) { + if (isset($storages['taxonomy_term'])) { + // Create a default forum so forum posts can be created. + $storages['taxonomy_term']->create(array( + 'name' => t('General discussion'), + 'description' => '', + 'parent' => array(0), + 'vid' => 'forums', + 'forum_container' => 0, + ))->save(); + } +} + +/** * Implements hook_uninstall(). */ function forum_uninstall() { diff --git a/core/modules/hal/lib/Drupal/hal/Tests/EntityTest.php b/core/modules/hal/lib/Drupal/hal/Tests/EntityTest.php index 42bfc4f..15342ec 100644 --- a/core/modules/hal/lib/Drupal/hal/Tests/EntityTest.php +++ b/core/modules/hal/lib/Drupal/hal/Tests/EntityTest.php @@ -38,9 +38,8 @@ function setUp() { \Drupal::service('router.builder')->rebuild(); $this->installSchema('system', array('sequences')); - $this->installSchema('node', array('node', 'node_field_data', 'node_revision', 'node_field_revision')); - $this->installSchema('user', array('users_roles')); - $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy')); + $this->installEntitySchema('node'); + $this->installEntitySchema('taxonomy_term'); } /** diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php index 98d24ce..0dc3ec0 100644 --- a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php +++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php @@ -62,8 +62,8 @@ function setUp() { parent::setUp(); $this->installSchema('system', array('url_alias', 'router')); - $this->installSchema('user', array('users')); - $this->installSchema('entity_test', array('entity_test')); + $this->installEntitySchema('user'); + $this->installEntitySchema('entity_test'); $this->installConfig(array('field', 'language')); // Add English as a language. diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php index 7803a11..7f16d09 100644 --- a/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php +++ b/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php @@ -47,7 +47,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('file', array('file_managed', 'file_usage')); + $this->installEntitySchema('file'); entity_create('field_config', array( 'name' => 'image_test', diff --git a/core/modules/node/lib/Drupal/node/Entity/Node.php b/core/modules/node/lib/Drupal/node/Entity/Node.php index d5aea78..c74a778 100644 --- a/core/modules/node/lib/Drupal/node/Entity/Node.php +++ b/core/modules/node/lib/Drupal/node/Entity/Node.php @@ -24,6 +24,7 @@ * label = @Translation("Content"), * bundle_label = @Translation("Content type"), * controllers = { + * "storage" = "Drupal\node\NodeStorage", * "view_builder" = "Drupal\node\NodeViewBuilder", * "access" = "Drupal\node\NodeAccessController", * "form" = { @@ -75,6 +76,20 @@ public function getRevisionId() { return $this->get('vid')->value; } + public function preSave(EntityStorageInterface $storage) { + parent::preSave($storage); + + // If no owner has been set explicitly, make the current user the owner. + if (!$this->getOwner()) { + $this->setOwnerId(\Drupal::currentUser()->id()); + } + // If no revision author has been set explicitly, make the node owner the + // revision author. + if (!$this->getRevisionAuthor()) { + $this->setRevisionAuthorId($this->getOwnerId()); + } + } + /** * {@inheritdoc} */ @@ -387,7 +402,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setRevisionable(TRUE) ->setSettings(array( 'target_type' => 'user', - 'default_value' => 0, )) ->setTranslatable(TRUE); @@ -421,7 +435,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setRevisionable(TRUE) ->setTranslatable(TRUE); - $fields['revision_timestamp'] = FieldDefinition::create('timestamp') + $fields['revision_timestamp'] = FieldDefinition::create('created') ->setLabel(t('Revision timestamp')) ->setDescription(t('The time that the current revision was created.')) ->setQueryable(FALSE) @@ -449,10 +463,30 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) { $node_type = node_type_load($bundle); $fields = array(); + + // When deleting a node type the corresponding node displays are deleted as + // well. In order to be deleted, they need to be loaded first. Entity + // displays, however, fetch the field definitions of the respective entity + // type to fill in their defaults. Therefore this function ends up being + // called with a non-existing bundle. + // @todo Fix this! + if (!$node_type) { + return $fields; + } + if (isset($node_type->title_label)) { $fields['title'] = clone $base_field_definitions['title']; $fields['title']->setLabel($node_type->title_label); } + + $options = $node_type->getModuleSettings('node')['options']; + $fields['status'] = clone $base_field_definitions['status']; + $fields['status']->setSetting('default_value', !empty($options['status']) ? NODE_PUBLISHED : NODE_NOT_PUBLISHED); + $fields['promote'] = clone $base_field_definitions['promote']; + $fields['promote']->setSetting('default_value', !empty($options['promote']) ? NODE_PROMOTED : NODE_NOT_PROMOTED); + $fields['sticky'] = clone $base_field_definitions['sticky']; + $fields['sticky']->setSetting('default_value', !empty($options['sticky']) ? NODE_STICKY : NODE_NOT_STICKY); + return $fields; } diff --git a/core/modules/node/lib/Drupal/node/Entity/NodeType.php b/core/modules/node/lib/Drupal/node/Entity/NodeType.php index 8ca6f72..feec14e 100644 --- a/core/modules/node/lib/Drupal/node/Entity/NodeType.php +++ b/core/modules/node/lib/Drupal/node/Entity/NodeType.php @@ -226,4 +226,14 @@ public static function preCreate(EntityStorageInterface $storage, array &$values ), $values['settings']['node']); } + /** + * {@inheritdoc} + */ + protected function onUpdateBundleEntity() { + // The bundle field definitions of nodes depend on the node type settings. + // @see \Drupal\node\Entity\Node::bundleFieldDefinitions() + $this->entityManager()->clearCachedFieldDefinitions(); + parent::onUpdateBundleEntity(); + } + } diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php index da55f5f..b30c329 100644 --- a/core/modules/node/lib/Drupal/node/NodeFormController.php +++ b/core/modules/node/lib/Drupal/node/NodeFormController.php @@ -36,16 +36,7 @@ protected function prepareEntity() { $type = entity_load('node_type', $node->bundle()); $this->settings = $type->getModuleSettings('node'); - // If this is a new node, fill in the default values. - if ($node->isNew()) { - foreach (array('status', 'promote', 'sticky') as $key) { - // Multistep node forms might have filled in something already. - if ($node->$key->isEmpty()) { - $node->$key = (int) !empty($this->settings['options'][$key]); - } - } - } - else { + if (!$node->isNew()) { $node->date = format_date($node->getCreatedTime(), 'custom', 'Y-m-d H:i:s O'); // Remove the log message from the original node entity. $node->log = NULL; diff --git a/core/modules/node/lib/Drupal/node/NodeStorage.php b/core/modules/node/lib/Drupal/node/NodeStorage.php new file mode 100644 index 0000000..7f52a36 --- /dev/null +++ b/core/modules/node/lib/Drupal/node/NodeStorage.php @@ -0,0 +1,100 @@ + 'Identifies which realm/grant pairs a user must possess in order to view, update, or delete specific nodes.', + 'fields' => array( + 'nid' => array( + 'description' => 'The {node}.nid this record affects.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'langcode' => array( + 'description' => 'The {language}.langcode of this node.', + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + ), + 'fallback' => array( + 'description' => 'Boolean indicating whether this record should be used as a fallback if a language condition is not provided.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 1, + ), + 'gid' => array( + 'description' => "The grant ID a user must possess in the specified realm to gain this row's privileges on the node.", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'realm' => array( + 'description' => 'The realm in which the user must possess the grant ID. Each node access node can define one or more realms.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'grant_view' => array( + 'description' => 'Boolean indicating whether a user with the realm/grant pair can view this node.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'grant_update' => array( + 'description' => 'Boolean indicating whether a user with the realm/grant pair can edit this node.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'grant_delete' => array( + 'description' => 'Boolean indicating whether a user with the realm/grant pair can delete this node.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + ), + 'primary key' => array('nid', 'gid', 'realm', 'langcode'), + 'foreign keys' => array( + 'affected_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + ), + ); + + return $schema; + } + +} diff --git a/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php b/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php index a5c2e35..fc5aadb 100644 --- a/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php +++ b/core/modules/node/lib/Drupal/node/Plugin/views/wizard/Node.php @@ -265,7 +265,10 @@ protected function buildFilters(&$form, &$form_state) { $tag_fields = array(); foreach ($bundles as $bundle) { $display = entity_get_form_display($this->entityTypeId, $bundle, 'default'); - foreach (field_info_instances($this->entityTypeId, $bundle) as $field_name => $instance) { + $taxonomy_fields = array_filter(\Drupal::entityManager()->getFieldDefinitions($this->entityTypeId, $bundle), function ($field_definition) { + return $field_definition->getType() == 'taxonomy_term_reference'; + }); + foreach ($taxonomy_fields as $field_name => $instance) { $widget = $display->getComponent($field_name); // We define "tag-like" taxonomy fields as ones that use the // "Autocomplete term widget (tagging)" widget. diff --git a/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php b/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php index 065cdca..a52b9fa 100644 --- a/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php @@ -26,7 +26,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('node', array('node', 'node_field_data', 'node_field_revision', 'node_revision')); + $this->installEntitySchema('node'); // Create the node bundles required for testing. $type = entity_create('node_type', array('type' => 'page', 'name' => 'page')); diff --git a/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportCreateTest.php b/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportCreateTest.php index 1d59ca1..dcf4e7e 100644 --- a/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportCreateTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportCreateTest.php @@ -28,7 +28,7 @@ public function setUp() { parent::setUp(); $this->installSchema('system', array('config_snapshot')); - $this->installSchema('user', array('users')); + $this->installEntitySchema('user'); // Set default storage backend. $this->installConfig(array('field')); diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeFormButtonsTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeFormButtonsTest.php index 1aa7dcd..9a6831d 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeFormButtonsTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeFormButtonsTest.php @@ -108,7 +108,10 @@ function testNodeFormButtons() { // Set article content type default to unpublished. This will change the // the initial order of buttons and/or status of the node when creating // a node. - \Drupal::config('node.type.article')->set('settings.node.options.status', FALSE)->save(); + /** @var \Drupal\node\NodeTypeInterface $node_type */ + $node_type = $this->container->get('entity.manager')->getStorage('node_type')->load('article'); + $node_type->settings['node']['options']['status'] = FALSE; + $node_type->save(); // Verify the buttons on a node add form for an administrator. $this->drupalLogin($this->admin_user); diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeLastChangedTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeLastChangedTest.php index bf23498..aa1dbfa 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeLastChangedTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeLastChangedTest.php @@ -31,11 +31,8 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('node', 'node'); - $this->installSchema('node', 'node_revision'); - $this->installSchema('node', 'node_field_data'); - $this->installSchema('node', 'node_field_revision'); - $this->installSchema('user', array('users')); + $this->installEntitySchema('node'); + $this->installEntitySchema('user'); } /** diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTokenReplaceTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTokenReplaceTest.php index 717abcc..2b1e07f 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeTokenReplaceTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeTokenReplaceTest.php @@ -38,7 +38,7 @@ public static function getInfo() { */ public function setUp() { parent::setUp(); - $this->installSchema('node', array('node', 'node_field_revision', 'node_field_data', 'node_revision')); + $this->installEntitySchema('node'); $this->installConfig(array('filter')); $node_type = entity_create('node_type', array('type' => 'article', 'name' => 'Article')); diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php index 4012908..95eba8c 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php @@ -196,26 +196,6 @@ function testTranslateLinkContentAdminPage() { } /** - * Tests field translation form. - */ - function testFieldTranslationForm() { - $this->drupalLogin($this->administrator); - - $article = $this->drupalCreateNode(array('type' => 'article', 'langcode' => 'en')); - - // Visit translation page. - $this->drupalGet('node/' . $article->id() . '/translations'); - $this->assertRaw('Not translated'); - - // Delete the only translatable field. - field_info_field($this->entityTypeId, 'field_test_et_ui_test')->delete(); - - // Visit translation page. - $this->drupalGet('node/' . $article->id() . '/translations'); - $this->assertRaw('No translatable fields'); - } - - /** * Tests that no metadata is stored for a disabled bundle. */ public function testDisabledBundle() { diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php index e71d75b..94c7ca3 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php @@ -34,7 +34,7 @@ public static function getInfo() { */ public function setUp() { parent::setUp(); - $this->installSchema('node', array('node', 'node_field_data', 'node_field_revision', 'node_revision')); + $this->installEntitySchema('node'); // Create a node type for testing. $type = entity_create('node_type', array('type' => 'page', 'name' => 'page')); diff --git a/core/modules/node/node.install b/core/modules/node/node.install index 36f8345..86546c3 100644 --- a/core/modules/node/node.install +++ b/core/modules/node/node.install @@ -36,390 +36,6 @@ function node_requirements($phase) { } /** - * Implements hook_schema(). - */ -function node_schema() { - $schema['node'] = array( - 'description' => 'The base table for nodes.', - 'fields' => array( - 'nid' => array( - 'description' => 'The primary identifier for a node.', - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'uuid' => array( - 'description' => 'Unique Key: Universally unique identifier for this entity.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - // Defaults to NULL in order to avoid a brief period of potential - // deadlocks on the index. - 'vid' => array( - 'description' => 'The current {node_field_revision}.vid version identifier.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => NULL, - ), - 'type' => array( - 'description' => 'The type of this node.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - ), - 'indexes' => array( - 'node_type' => array(array('type', 4)), - ), - 'unique keys' => array( - 'vid' => array('vid'), - 'uuid' => array('uuid'), - ), - 'foreign keys' => array( - 'node_revision' => array( - 'table' => 'node_revision', - 'columns' => array('vid' => 'vid'), - ), - ), - 'primary key' => array('nid'), - ); - - $schema['node_revision'] = array( - 'description' => 'Stores information about each saved version of a {node}.', - 'fields' => array( - 'nid' => array( - 'description' => 'The {node} this version belongs to.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'vid' => array( - 'description' => 'The primary identifier for this version.', - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'revision_uid' => array( - 'description' => 'The {users}.uid that created this version.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'log' => array( - 'description' => 'The log entry explaining the changes in this version.', - 'type' => 'text', - 'not null' => FALSE, - 'size' => 'big', - ), - 'revision_timestamp' => array( - 'description' => 'The Unix timestamp when the version was created.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this version.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - ), - 'indexes' => array( - 'nid' => array('nid'), - 'revision_uid' => array('revision_uid'), - 'node_langcode' => array('langcode'), - ), - 'foreign keys' => array( - 'versioned_node' => array( - 'table' => 'node', - 'columns' => array('nid' => 'nid'), - ), - 'version_author' => array( - 'table' => 'users', - 'columns' => array('revision_uid' => 'uid'), - ), - ), - 'primary key' => array('vid'), - ); - - // Node field storage. - $schema['node_field_data'] = array( - 'description' => 'Data table for node base fields.', - 'fields' => array( - 'nid' => array( - 'description' => 'The primary identifier for a node.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'vid' => array( - 'description' => 'The current {node_field_revision}.vid version identifier.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'type' => array( - 'description' => 'The {node_type}.type of this node.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of these node property values.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'default_langcode' => array( - 'description' => 'Boolean indicating whether the property values are in the {language}.langcode of this node.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 1, - ), - 'title' => array( - 'description' => 'The title of this node, always treated as non-markup plain text.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'uid' => array( - 'description' => 'The {users}.uid that owns this node; initially, this is the user that created it.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'status' => array( - 'description' => 'Boolean indicating whether the node translation is published (visible to non-administrators).', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 1, - ), - 'created' => array( - 'description' => 'The Unix timestamp when the node translation was created.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'changed' => array( - 'description' => 'The Unix timestamp when the node translation was most recently saved.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'promote' => array( - 'description' => 'Boolean indicating whether the node translation should be displayed on the front page.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'sticky' => array( - 'description' => 'Boolean indicating whether the node translation should be displayed at the top of lists in which it appears.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'indexes' => array( - 'node_changed' => array('changed'), - 'node_created' => array('created'), - 'node_default_langcode' => array('default_langcode'), - 'node_langcode' => array('langcode'), - 'node_frontpage' => array('promote', 'status', 'sticky', 'created'), - 'node_status_type' => array('status', 'type', 'nid'), - 'node_title_type' => array('title', array('type', 4)), - 'node_type' => array(array('type', 4)), - 'vid' => array('vid'), - 'uid' => array('uid'), - ), - 'foreign keys' => array( - 'node_base' => array( - 'table' => 'node', - 'columns' => array('nid' => 'nid'), - ), - 'node_author' => array( - 'table' => 'users', - 'columns' => array('uid' => 'uid'), - ), - ), - 'primary key' => array('nid', 'langcode'), - ); - - $schema['node_field_revision'] = array( - 'description' => 'Revision table for node base fields.', - 'fields' => array( - 'nid' => array( - 'description' => 'The {node} this version belongs to.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'vid' => array( - 'description' => 'The primary identifier for this version.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this version.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'default_langcode' => array( - 'description' => 'Boolean indicating whether the property values of this version are in the {language}.langcode of this node.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 1, - ), - 'title' => array( - 'description' => 'The title of this version, always treated as non-markup plain text.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'uid' => array( - 'description' => 'The {users}.uid that created this node.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'status' => array( - 'description' => 'Boolean indicating whether the node (at the time of this revision) is published (visible to non-administrators).', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 1, - ), - 'created' => array( - 'description' => 'The Unix timestamp when the node was created.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'changed' => array( - 'description' => 'The Unix timestamp when the version was most recently saved.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'promote' => array( - 'description' => 'Boolean indicating whether the node (at the time of this revision) should be displayed on the front page.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'sticky' => array( - 'description' => 'Boolean indicating whether the node (at the time of this revision) should be displayed at the top of lists in which it appears.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'indexes' => array( - 'uid' => array('uid'), - 'node_default_langcode' => array('default_langcode'), - 'node_langcode' => array('langcode'), - ), - 'foreign keys' => array( - 'versioned_node' => array( - 'table' => 'node', - 'columns' => array('nid' => 'nid'), - ), - 'node_author' => array( - 'table' => 'users', - 'columns' => array('uid' => 'uid'), - ), - ), - 'primary key' => array('vid', 'langcode'), - ); - - $schema['node_access'] = array( - 'description' => 'Identifies which realm/grant pairs a user must possess in order to view, update, or delete specific nodes.', - 'fields' => array( - 'nid' => array( - 'description' => 'The {node}.nid this record affects.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this node.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'fallback' => array( - 'description' => 'Boolean indicating whether this record should be used as a fallback if a language condition is not provided.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 1, - ), - 'gid' => array( - 'description' => "The grant ID a user must possess in the specified realm to gain this row's privileges on the node.", - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'realm' => array( - 'description' => 'The realm in which the user must possess the grant ID. Each node access node can define one or more realms.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'grant_view' => array( - 'description' => 'Boolean indicating whether a user with the realm/grant pair can view this node.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - ), - 'grant_update' => array( - 'description' => 'Boolean indicating whether a user with the realm/grant pair can edit this node.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - ), - 'grant_delete' => array( - 'description' => 'Boolean indicating whether a user with the realm/grant pair can delete this node.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - ), - ), - 'primary key' => array('nid', 'gid', 'realm', 'langcode'), - 'foreign keys' => array( - 'affected_node' => array( - 'table' => 'node', - 'columns' => array('nid' => 'nid'), - ), - ), - ); - - return $schema; -} - -/** * Implements hook_install(). */ function node_install() { @@ -434,18 +50,25 @@ function node_install() { user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access content')); user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('access content')); } +} - // Populate the node access table. - db_insert('node_access') - ->fields(array( - 'nid' => 0, - 'gid' => 0, - 'realm' => 'all', - 'grant_view' => 1, - 'grant_update' => 0, - 'grant_delete' => 0, - )) - ->execute(); +/** + * Implements hook_entity_schema_installed(). + */ +function node_entity_schema_installed(array $storages) { + if (isset($storages['node'])) { + // Populate the node access table. + db_insert('node_access') + ->fields(array( + 'nid' => 0, + 'gid' => 0, + 'realm' => 'all', + 'grant_view' => 1, + 'grant_update' => 0, + 'grant_delete' => 0, + )) + ->execute(); + } } /** diff --git a/core/modules/node/node.module b/core/modules/node/node.module index d793975..b5effde 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -836,6 +836,10 @@ function node_user_cancel($edit, $account, $method) { ->fetchCol(); node_mass_update($nodes, array('uid' => 0), NULL, TRUE); // Anonymize old revisions. + db_update('node_revision') + ->fields(array('revision_uid' => 0)) + ->condition('revision_uid', $account->id()) + ->execute(); db_update('node_field_revision') ->fields(array('uid' => 0)) ->condition('uid', $account->id()) diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php index f152e0b..d3cacfd 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php @@ -163,7 +163,7 @@ public function testCommentRdfaMarkup() { $anonymous_user['name'] = $this->randomName(); $anonymous_user['mail'] = 'tester@simpletest.org'; $anonymous_user['homepage'] = 'http://example.org/'; - $comment2 = $this->saveComment($this->node->id(), NULL, $anonymous_user); + $comment2 = $this->saveComment($this->node->id(), 0, $anonymous_user); // Tests comment #2 as anonymous user. $parser = new \EasyRdf_Parser_Rdfa(); diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/Field/TaxonomyTermReferenceRdfaTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/Field/TaxonomyTermReferenceRdfaTest.php index 00ac9d7..d8bfbed 100644 --- a/core/modules/rdf/lib/Drupal/rdf/Tests/Field/TaxonomyTermReferenceRdfaTest.php +++ b/core/modules/rdf/lib/Drupal/rdf/Tests/Field/TaxonomyTermReferenceRdfaTest.php @@ -50,7 +50,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy')); + $this->installEntitySchema('taxonomy_term'); $vocabulary = entity_create('taxonomy_vocabulary', array( 'name' => $this->randomName(), diff --git a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php index adff21d..78a8629 100644 --- a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php +++ b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php @@ -58,7 +58,6 @@ public function testPatchUpdate() { $patch_entity = entity_create($entity_type, $patch_values); // We don't want to overwrite the UUID. unset($patch_entity->uuid); - $patch_entity->save(); $serialized = $serializer->serialize($patch_entity, $this->defaultFormat); // Update the entity over the REST API. diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/EntitySerializationTest.php b/core/modules/serialization/lib/Drupal/serialization/Tests/EntitySerializationTest.php index 865704d..1ad843a 100644 --- a/core/modules/serialization/lib/Drupal/serialization/Tests/EntitySerializationTest.php +++ b/core/modules/serialization/lib/Drupal/serialization/Tests/EntitySerializationTest.php @@ -80,18 +80,12 @@ public function testNormalize() { 'id' => array( array('value' => 1), ), - 'revision_id' => array( - array('value' => 1), - ), 'uuid' => array( array('value' => $this->entity->uuid()), ), 'langcode' => array( array('value' => Language::LANGCODE_NOT_SPECIFIED), ), - 'default_langcode' => array( - array('value' => NULL), - ), 'name' => array( array('value' => $this->values['name']), ), @@ -101,6 +95,9 @@ public function testNormalize() { 'user_id' => array( array('target_id' => $this->values['user_id']), ), + 'revision_id' => array( + array('value' => 1), + ), 'field_test_text' => array( array( 'value' => $this->values['field_test_text']['value'], @@ -141,13 +138,12 @@ public function testSerialize() { // order. $expected = array( 'id' => '' . $this->entity->id() . '', - 'revision_id' => '' . $this->entity->getRevisionId() . '', 'uuid' => '' . $this->entity->uuid() . '', 'langcode' => '' . Language::LANGCODE_NOT_SPECIFIED . '', - 'default_langcode' => '', 'name' => '' . $this->values['name'] . '', 'type' => 'entity_test_mulrev', 'user_id' => '' . $this->values['user_id'] . '', + 'revision_id' => '' . $this->entity->getRevisionId() . '', 'field_test_text' => '' . $this->values['field_test_text']['value'] . '' . $this->values['field_test_text']['format'] . '', ); // Sort it in the same order as normalised. diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php index 3b0cfd6..0c1dc03 100644 --- a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php +++ b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php @@ -21,8 +21,8 @@ protected function setUp() { parent::setUp(); - $this->installSchema('entity_test', array('entity_test_mulrev', 'entity_test_mulrev_revision', 'entity_test_mulrev_property_revision', 'entity_test_mulrev_property_data')); - $this->installSchema('user', array('users', 'users_roles')); + $this->installEntitySchema('entity_test_mulrev'); + $this->installEntitySchema('user'); $this->installSchema('system', array('url_alias')); $this->installConfig(array('field')); diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php index 00d0dd3..f361bcd 100644 --- a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php +++ b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php @@ -183,10 +183,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setLabel(t('Language code')) ->setDescription(t('The language code of the shortcut.')); - $fields['default_langcode'] = FieldDefinition::create('boolean') - ->setLabel(t('Default language')) - ->setDescription(t('Flag to indicate whether this is the default language.')); - $fields['path'] = FieldDefinition::create('string') ->setLabel(t('Path')) ->setDescription(t('The computed shortcut path.')) diff --git a/core/modules/shortcut/shortcut.install b/core/modules/shortcut/shortcut.install index b7ed49c..be6f8b2 100644 --- a/core/modules/shortcut/shortcut.install +++ b/core/modules/shortcut/shortcut.install @@ -12,96 +12,6 @@ * Implements hook_schema(). */ function shortcut_schema() { - $schema['shortcut'] = array( - 'description' => 'Stores shortcut items.', - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique shortcut ID.', - ), - 'uuid' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - 'description' => 'Unique Key: Universally unique identifier for this shortcut.', - ), - 'shortcut_set' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The bundle of the shortcut.', - ), - 'langcode' => array( - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The {language}.langcode of the original variant of this shortcut.', - ), - 'weight' => array( - 'description' => 'Weight among shortcuts in the same shortcut set.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'route_name' => array( - 'description' => 'The machine name of a defined Symfony Route this menu item represents.', - 'type' => 'varchar', - 'length' => 255, - ), - 'route_parameters' => array( - 'description' => 'Serialized array of route parameters of this shortcut.', - 'type' => 'blob', - 'size' => 'big', - 'not null' => FALSE, - 'serialize' => TRUE, - ), - ), - 'primary key' => array('id'), - 'unique keys' => array( - 'uuid' => array('uuid'), - ), - ); - - $schema['shortcut_field_data'] = array( - 'description' => 'Stores shortcut properties.', - 'fields' => array( - 'id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The {shortcut}.id of the shortcut.', - ), - 'langcode' => array( - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The {language}.langcode of this variant of this shortcut.', - ), - 'default_langcode' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 1, - 'description' => 'Boolean indicating whether the current variant is in the original entity language.', - ), - 'title' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => FALSE, - 'description' => 'The title of the shortcut.', - ), - ), - 'foreign keys' => array( - 'shortcut' => array( - 'table' => 'shortcut', - 'columns' => array('id' => 'id'), - ), - ), - 'primary key' => array('id', 'langcode'), - ); $schema['shortcut_set_users'] = array( 'description' => 'Maps users to shortcut sets.', diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php index 36adb8b..4b9d02e 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php @@ -7,8 +7,10 @@ namespace Drupal\simpletest; +use Drupal\Component\Utility\String; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DrupalKernel; +use Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface; use Drupal\Core\KeyValueStore\KeyValueMemoryFactory; use Drupal\Core\Language\Language; use Symfony\Component\DependencyInjection\Reference; @@ -332,6 +334,37 @@ protected function installSchema($module, $tables) { } /** + * Installs the tables for a specific entity type. + * + * @param string $entity_type_id + * The ID of the entity type. + */ + protected function installEntitySchema($entity_type_id) { + /** @var \Drupal\Core\Entity\EntityManagerInterface $entity_manager */ + $entity_manager = $this->container->get('entity.manager'); + /** @var \Drupal\Core\Database\Schema $schema_handler */ + $schema_handler = $this->container->get('database')->schema(); + + $storage = $entity_manager->getStorage($entity_type_id); + if ($storage instanceof ContentEntitySchemaHandlerInterface) { + $schema = $storage->getSchema(); + foreach ($schema as $table_name => $table_schema) { + $schema_handler->createTable($table_name, $table_schema); + } + + $this->pass(String::format('Installed entity type tables for the %entity_type entity type: %tables', array( + '%entity_type' => $entity_type_id, + '%tables' => '{' . implode('}, {', array_keys($schema)) . '}', + ))); + } + else { + throw new \RuntimeException(String::format('Entity type %entity_type does not support automatic schema installation.', array( + '%entity-type' => $entity_type_id, + ))); + } + } + + /** * Enables modules for this test. * * @param array $modules diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php index 6c13c8a..b810672 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php @@ -19,7 +19,7 @@ class DrupalUnitTestBaseTest extends DrupalUnitTestBase { * * @var array */ - public static $modules = array('entity', 'entity_test'); + public static $modules = array('entity', 'entity_test', 'field', 'simpletest_test'); public static function getInfo() { return array( @@ -33,17 +33,18 @@ public static function getInfo() { * Tests expected behavior of setUp(). */ function testSetUp() { - $modules = array('entity', 'entity_test'); - $table = 'entity_test'; + $tables = array('entity_test', 'simpletest_test'); // Verify that specified $modules have been loaded. $this->assertTrue(function_exists('entity_test_permission'), 'entity_test.module was loaded.'); // Verify that there is a fixed module list. - $this->assertIdentical(array_keys(\Drupal::moduleHandler()->getModuleList()), $modules); - $this->assertIdentical(\Drupal::moduleHandler()->getImplementations('permission'), $modules); + $this->assertIdentical(array_keys(\Drupal::moduleHandler()->getModuleList()), static::$modules); + $this->assertIdentical(\Drupal::moduleHandler()->getImplementations('permission'), array('entity', 'entity_test')); // Verify that no modules have been installed. - $this->assertFalse(db_table_exists($table), "'$table' database table not found."); + foreach($tables as $table){ + $this->assertFalse(db_table_exists($table), "'$table' database table not found."); + } } /** @@ -74,8 +75,8 @@ function testEnableModulesLoad() { * Tests expected installation behavior of enableModules(). */ function testEnableModulesInstall() { - $module = 'node'; - $table = 'node'; + $module = 'ban'; + $table = 'ban_ip'; // Verify that the module does not exist yet. $this->assertFalse(\Drupal::moduleHandler()->moduleExists($module), "$module module not found."); @@ -108,9 +109,9 @@ function testEnableModulesInstall() { */ function testEnableModulesInstallContainer() { // Install Node module. - $this->enableModules(array('field', 'node')); + $this->enableModules(array('field', 'node', 'user')); - $this->installSchema('node', array('node', 'node_field_data')); + $this->installEntitySchema('node'); // Perform an entity query against node. $query = \Drupal::entityQuery('node'); // Disable node access checks, since User module is not enabled. @@ -124,8 +125,8 @@ function testEnableModulesInstallContainer() { * Tests expected behavior of installSchema(). */ function testInstallSchema() { - $module = 'entity_test'; - $table = 'entity_test'; + $module = 'simpletest_test'; + $table = 'simpletest_test'; // Verify that we can install a table from the module schema. $this->installSchema($module, $table); $this->assertTrue(db_table_exists($table), "'$table' database table found."); diff --git a/core/modules/simpletest/tests/modules/simpletest_test/simpletest_test.info.yml b/core/modules/simpletest/tests/modules/simpletest_test/simpletest_test.info.yml new file mode 100644 index 0000000..6c98069 --- /dev/null +++ b/core/modules/simpletest/tests/modules/simpletest_test/simpletest_test.info.yml @@ -0,0 +1,7 @@ +name: Simpletest Test +type: module +description: 'Provides dummy hook implementations for use by SimpleTest tests.' +package: Testing +version: VERSION +core: 8.x +hidden: TRUE diff --git a/core/modules/simpletest/tests/modules/simpletest_test/simpletest_test.install b/core/modules/simpletest/tests/modules/simpletest_test/simpletest_test.install new file mode 100644 index 0000000..f9b16a0 --- /dev/null +++ b/core/modules/simpletest/tests/modules/simpletest_test/simpletest_test.install @@ -0,0 +1,50 @@ + 'Stores simpltest_test data.', + 'fields' => array( + 'sid' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Primary Key: Unique ID.', + ), + 'uuid' => array( + 'description' => 'Unique Key: Universally unique identifier for this entity.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + ), + 'dummy_textfield' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + 'description' => 'A Dummy text field.', + ), + 'dummy_integer' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'A dummy integer field.', + ), + ), + 'indexes' => array( + 'simpltest_test_index' => array('sid', 'dummy_integer'), + ), + 'primary key' => array('sid'), + 'unique keys' => array( + 'uuid' => array('uuid'), + ), + ); + + return $schema; +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Action/ActionUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Action/ActionUnitTest.php index 6f97da1..55ae046 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Action/ActionUnitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Action/ActionUnitTest.php @@ -45,7 +45,7 @@ protected function setUp() { parent::setUp(); $this->actionManager = $this->container->get('plugin.manager.action'); - $this->installSchema('user', array('users', 'users_roles')); + $this->installEntitySchema('user'); $this->installSchema('system', array('sequences')); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/WriteRecordTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/WriteRecordTest.php index ffe5fdb..da88eda 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/WriteRecordTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/WriteRecordTest.php @@ -7,19 +7,20 @@ namespace Drupal\system\Tests\Common; +use Drupal\simpletest\DrupalUnitTestBase; use Drupal\simpletest\WebTestBase; /** * Tests writing of data records with drupal_write_record(). */ -class WriteRecordTest extends WebTestBase { +class WriteRecordTest extends DrupalUnitTestBase { /** * Modules to enable. * * @var array */ - public static $modules = array('database_test', 'node'); + public static $modules = array('database_test'); public static function getInfo() { return array( @@ -33,6 +34,8 @@ public static function getInfo() { * Tests the drupal_write_record() API function. */ function testDrupalWriteRecord() { + $this->installSchema('database_test', array('test', 'test_null', 'test_serialized', 'test_composite_primary')); + // Insert a record with no columns populated. $record = array(); $insert_result = drupal_write_record('test', $record); @@ -135,15 +138,14 @@ function testDrupalWriteRecord() { $this->assertTrue($update_result == SAVED_UPDATED, 'Correct value returned when a valid update is run without changing any values.'); // Insert an object record for a table with a multi-field primary key. - $node_access = new \stdClass(); - $node_access->nid = mt_rand(); - $node_access->gid = mt_rand(); - $node_access->realm = $this->randomName(); - $insert_result = drupal_write_record('node_access', $node_access); + $composite_primary = new \stdClass(); + $composite_primary->name = $this->randomName(); + $composite_primary->age = mt_rand(); + $insert_result = drupal_write_record('test_composite_primary', $composite_primary); $this->assertTrue($insert_result == SAVED_NEW, 'Correct value returned when a record is inserted with drupal_write_record() for a table with a multi-field primary key.'); // Update the record. - $update_result = drupal_write_record('node_access', $node_access, array('nid', 'gid', 'realm')); + $update_result = drupal_write_record('test_composite_primary', $composite_primary, array('name', 'job')); $this->assertTrue($update_result == SAVED_UPDATED, 'Correct value returned when a record is updated with drupal_write_record() for a table with a multi-field primary key.'); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php index a9f0105..5d2364c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php @@ -25,16 +25,10 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('entity_test', array( - 'entity_test_mul', - 'entity_test_mul_property_data', - 'entity_test_rev', - 'entity_test_rev_revision', - 'entity_test_mulrev', - 'entity_test_mulrev_revision', - 'entity_test_mulrev_property_data', - 'entity_test_mulrev_property_revision' - )); + + $this->installEntitySchema('entity_test_rev'); + $this->installEntitySchema('entity_test_mul'); + $this->installEntitySchema('entity_test_mulrev'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php index 1d49a02..6e5c980 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php @@ -44,9 +44,9 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('user', array('users_data')); - $this->installSchema('node', array('node', 'node_revision', 'node_field_data', 'node_field_revision', 'node_access')); - $this->installSchema('comment', array('comment', 'comment_entity_statistics')); + + $this->installEntitySchema('node'); + $this->installEntitySchema('comment'); } /** @@ -219,7 +219,8 @@ public function testCommentHooks() { * Tests hook invocations for CRUD operations on files. */ public function testFileHooks() { - $this->installSchema('file', array('file_managed', 'file_usage')); + $this->installEntitySchema('file'); + $url = 'public://entity_crud_hook_test.file'; file_put_contents($url, 'Test test test'); $file = entity_create('file', array( @@ -346,7 +347,7 @@ public function testNodeHooks() { * Tests hook invocations for CRUD operations on taxonomy terms. */ public function testTaxonomyTermHooks() { - $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy')); + $this->installEntitySchema('taxonomy_term'); $vocabulary = entity_create('taxonomy_vocabulary', array( 'name' => 'Test vocabulary', @@ -415,7 +416,7 @@ public function testTaxonomyTermHooks() { * Tests hook invocations for CRUD operations on taxonomy vocabularies. */ public function testTaxonomyVocabularyHooks() { - $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy')); + $this->installEntitySchema('taxonomy_term'); $vocabulary = entity_create('taxonomy_vocabulary', array( 'name' => 'Test vocabulary', diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php index 4a05431..36b3655 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php @@ -40,18 +40,12 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('user', array('users_data')); - $this->installSchema('node', array('node', 'node_revision', 'node_field_data', 'node_field_revision', 'node_access')); - $this->installSchema('entity_test', array( - 'entity_test_mul', - 'entity_test_mul_property_data', - 'entity_test_rev', - 'entity_test_rev_revision', - 'entity_test_mulrev', - 'entity_test_mulrev_revision', - 'entity_test_mulrev_property_data', - 'entity_test_mulrev_property_revision' - )); + + $this->installEntitySchema('node'); + + $this->installEntitySchema('entity_test_rev'); + $this->installEntitySchema('entity_test_mul'); + $this->installEntitySchema('entity_test_mulrev'); // Create the test field. entity_test_install(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php index bd2ceb9..db45c92 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php @@ -50,16 +50,10 @@ function setUp() { $this->languageManager = $this->container->get('language_manager'); - $this->installSchema('entity_test', array( - 'entity_test_mul', - 'entity_test_mul_property_data', - 'entity_test_rev', - 'entity_test_rev_revision', - 'entity_test_mulrev', - 'entity_test_mulrev_revision', - 'entity_test_mulrev_property_data', - 'entity_test_mulrev_property_revision', - )); + $this->installEntitySchema('entity_test_rev'); + $this->installEntitySchema('entity_test_mul'); + $this->installEntitySchema('entity_test_mulrev'); + $this->installConfig(array('language')); // Create the test field. diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php index e5d8cf4..8d67666 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryRelationshipTest.php @@ -70,7 +70,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy')); + $this->installEntitySchema('taxonomy_term'); // We want a taxonomy term reference field. It needs a vocabulary, terms, // a field and an instance. First, create the vocabulary. diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php index 07ee0e9..b3cb9f7 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php @@ -57,7 +57,9 @@ public static function getInfo() { function setUp() { parent::setUp(); - $this->installSchema('entity_test', array('entity_test_mulrev', 'entity_test_mulrev_revision', 'entity_test_mulrev_property_data', 'entity_test_mulrev_property_revision')); + + $this->installEntitySchema('entity_test_mulrev'); + $this->installConfig(array('language')); $figures = drupal_strtolower($this->randomName()); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php index ec7134c..a0ec726 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php @@ -23,16 +23,9 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('entity_test', array( - 'entity_test_mul', - 'entity_test_mul_property_data', - 'entity_test_rev', - 'entity_test_rev_revision', - 'entity_test_mulrev', - 'entity_test_mulrev_revision', - 'entity_test_mulrev_property_data', - 'entity_test_mulrev_property_revision', - )); + $this->installEntitySchema('entity_test_rev'); + $this->installEntitySchema('entity_test_mul'); + $this->installEntitySchema('entity_test_mulrev'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php index 99fe76a..0c6aeab 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php @@ -42,9 +42,11 @@ public function setUp() { $this->entityManager = $this->container->get('entity.manager'); $this->state = $this->container->get('state'); - $this->installSchema('user', array('users', 'users_roles')); $this->installSchema('system', 'sequences'); - $this->installSchema('entity_test', 'entity_test'); + + $this->installEntitySchema('user'); + $this->installEntitySchema('entity_test'); + $this->installConfig(array('field')); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php index 09e1e54..dceab23 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php @@ -32,17 +32,10 @@ public static function getInfo() { */ public function setUp() { parent::setUp(); - $this->installSchema('user', array('users_data')); - $this->installSchema('entity_test', array( - 'entity_test_mul', - 'entity_test_mul_property_data', - 'entity_test_rev', - 'entity_test_rev_revision', - 'entity_test_mulrev', - 'entity_test_mulrev_revision', - 'entity_test_mulrev_property_data', - 'entity_test_mulrev_property_revision' - )); + + $this->installEntitySchema('entity_test_rev'); + $this->installEntitySchema('entity_test_mul'); + $this->installEntitySchema('entity_test_mulrev'); // Create the test field. entity_test_install(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php index 7395dfa..d72b297 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php @@ -44,8 +44,9 @@ protected function setUp() { // Install field configuration. $this->installConfig(array('field')); // The users table is needed for creating dummy user accounts. - $this->installSchema('user', array('users')); + $this->installEntitySchema('user'); // Register entity_test text field. + module_load_install('entity_test'); entity_test_install(); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php index 82e63b2..de4aa63 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php @@ -66,7 +66,8 @@ public static function getInfo() { function setUp() { parent::setUp(); - $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision')); + + $this->installEntitySchema('entity_test_rev'); $entity_type = 'entity_test_rev'; $this->field_name = strtolower($this->randomName()); diff --git a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php index 5893f7b..37c4af9 100644 --- a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php @@ -44,7 +44,7 @@ public static function getInfo() { public function setUp() { parent::setup(); - $this->installSchema('file', array('file_managed', "file_usage")); + $this->installEntitySchema('file'); $this->typedDataManager = $this->container->get('typed_data_manager'); } diff --git a/core/modules/system/tests/modules/database_test/database_test.install b/core/modules/system/tests/modules/database_test/database_test.install index 866d453..7c74c1c 100644 --- a/core/modules/system/tests/modules/database_test/database_test.install +++ b/core/modules/system/tests/modules/database_test/database_test.install @@ -245,5 +245,34 @@ function database_test_schema() { ), ); + $schema['test_composite_primary'] = array( + 'description' => 'Basic test table with a composite primary key', + 'fields' => array( + 'name' => array( + 'description' => "A person's name", + 'type' => 'varchar', + 'length' => 50, + 'not null' => TRUE, + 'default' => '', + 'binary' => TRUE, + ), + 'age' => array( + 'description' => "The person's age", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'job' => array( + 'description' => "The person's job", + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => 'Undefined', + ), + ), + 'primary key' => array('name', 'age'), + ); + return $schema; } diff --git a/core/modules/system/tests/modules/entity_test/entity_test.install b/core/modules/system/tests/modules/entity_test/entity_test.install index 9e48029..f2db594 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.install +++ b/core/modules/system/tests/modules/entity_test/entity_test.install @@ -36,406 +36,3 @@ function entity_test_install() { ->save(); } } - -/** - * Implements hook_schema(). - */ -function entity_test_schema() { - // Schema for simple entity. - $schema['entity_test'] = array( - 'description' => 'Stores entity_test items.', - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique entity-test item ID.', - ), - 'uuid' => array( - 'description' => 'Unique Key: Universally unique identifier for this entity.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - 'type' => array( - 'description' => 'The bundle of the test entity.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of the original variant of this test entity.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'name' => array( - 'description' => 'The name of the test entity.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'user_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => NULL, - 'description' => 'The {users}.uid of the associated user.', - ), - ), - 'primary key' => array('id'), - 'unique keys' => array( - 'uuid' => array('uuid'), - ), - ); - - // Schema for entity with revisions. - $schema['entity_test_rev'] = array( - 'description' => 'Stores entity_test_rev items.', - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique entity-test item ID.', - ), - 'revision_id' => array( - 'description' => 'The current {entity_test_rev_property_revision}.revision_id version identifier.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'uuid' => array( - 'description' => 'Unique Key: Universally unique identifier for this entity.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - 'type' => array( - 'description' => 'The bundle of the test entity.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'name' => array( - 'description' => 'The name of the test entity.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'user_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => NULL, - 'description' => 'The {users}.uid of the associated user.', - ), - ), - 'primary key' => array('id'), - 'unique keys' => array( - 'uuid' => array('uuid'), - ), - ); - $schema['entity_test_rev_revision'] = array( - 'description' => 'Stores entity_test_rev item property revisions.', - 'fields' => array( - 'id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The {entity_test_rev}.id of the test entity.', - ), - 'revision_id' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The primary identifier for this version.', - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this variant of this test entity.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'name' => array( - 'description' => 'The name of the test entity.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'user_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => NULL, - 'description' => 'The {users}.uid of the associated user.', - ), - ), - 'indexes' => array( - 'user_id' => array('user_id'), - ), - 'foreign keys' => array( - 'user_id' => array('users' => 'uid'), - 'id' => array('entity_test_rev' => 'id'), - ), - 'primary key' => array('revision_id'), - ); - - // Schema for entity with data table. - $schema['entity_test_mul'] = array( - 'description' => 'Stores entity_test_mul items.', - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique entity-test item ID.', - ), - 'uuid' => array( - 'description' => 'Unique Key: Universally unique identifier for this entity.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - 'type' => array( - 'description' => 'The bundle of the test entity.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of the original variant of this test entity.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - ), - 'primary key' => array('id'), - 'unique keys' => array( - 'uuid' => array('uuid'), - ), - ); - $schema['entity_test_mul_property_data'] = array( - 'description' => 'Stores entity_test_mul item properties.', - 'fields' => array( - 'id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The {entity_test_mul}.id of the test entity.', - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this variant of this test entity.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'default_langcode' => array( - 'description' => 'Boolean indicating whether the current variant is in the original entity language.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 1, - ), - 'name' => array( - 'description' => 'The name of the test entity.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => FALSE, - ), - 'user_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => NULL, - 'description' => 'The {users}.uid of the associated user.', - ), - ), - 'indexes' => array( - 'user_id' => array('user_id'), - ), - 'foreign keys' => array( - 'user_id' => array('users' => 'uid'), - 'id' => array('entity_test_mul' => 'id'), - ), - 'primary key' => array('id', 'langcode'), - ); - - // Schema for entity with data table and revisions. - $schema['entity_test_mulrev'] = array( - 'description' => 'Stores entity_test_mulrev items.', - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique entity-test item ID.', - ), - 'revision_id' => array( - 'description' => 'The current {entity_test_mulrev_property_revision}.revision_id version identifier.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'uuid' => array( - 'description' => 'Unique Key: Universally unique identifier for this entity.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - 'type' => array( - 'description' => 'The bundle of the test entity.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - ), - 'primary key' => array('id'), - 'unique keys' => array( - 'uuid' => array('uuid'), - ), - ); - $schema['entity_test_mulrev_revision'] = array( - 'description' => 'Stores entity_test_rev item property revisions.', - 'fields' => array( - 'id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The {entity_test_rev}.id of the test entity.', - ), - 'revision_id' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The primary identifier for this version.', - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this variant of this test entity.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - ), - 'foreign keys' => array( - 'id' => array('entity_test_rev' => 'id'), - ), - 'primary key' => array('revision_id'), - ); - $schema['entity_test_mulrev_property_data'] = array( - 'description' => 'Stores entity_test_mulrev item properties.', - 'fields' => array( - 'id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The {entity_test_mulrev}.id of the test entity.', - ), - 'revision_id' => array( - 'description' => 'The current {entity_test_mulrev_property_revision}.revision_id version identifier.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this variant of this test entity.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'default_langcode' => array( - 'description' => 'Boolean indicating whether the current variant is in the original entity language.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 1, - ), - 'name' => array( - 'description' => 'The name of the test entity.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'user_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => NULL, - 'description' => 'The {users}.uid of the associated user.', - ), - ), - 'indexes' => array( - 'user_id' => array('user_id'), - ), - 'foreign keys' => array( - 'user_id' => array('users' => 'uid'), - 'id' => array('entity_test_mulrev' => 'id'), - ), - 'primary key' => array('id', 'langcode'), - ); - $schema['entity_test_mulrev_property_revision'] = array( - 'description' => 'Stores entity_test_mulrev item property revisions.', - 'fields' => array( - 'id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The {entity_test_mulrev}.id of the test entity.', - ), - 'revision_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The primary identifier for this version.', - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this variant of this test entity.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'default_langcode' => array( - 'description' => 'Boolean indicating whether the current variant is in the original entity language.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 1, - ), - 'name' => array( - 'description' => 'The name of the test entity.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'user_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => NULL, - 'description' => 'The {users}.uid of the associated user.', - ), - ), - 'indexes' => array( - 'user_id' => array('user_id'), - ), - 'foreign keys' => array( - 'user_id' => array('users' => 'uid'), - 'id' => array('entity_test_mulrev' => 'id'), - ), - 'primary key' => array('revision_id', 'langcode'), - ); - - return $schema; -} diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestBaseFieldDisplay.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestBaseFieldDisplay.php index cddccc6..324e70a 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestBaseFieldDisplay.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestBaseFieldDisplay.php @@ -28,7 +28,6 @@ * entity_keys = { * "id" = "id", * "uuid" = "uuid", - * "revision" = "revision_id", * "bundle" = "type" * }, * links = { 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 = { diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php index c38e806..f906ccd 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php @@ -44,17 +44,4 @@ */ class EntityTestMul extends EntityTest { - /** - * {@inheritdoc} - */ - public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { - $fields = parent::baseFieldDefinitions($entity_type); - - $fields['default_langcode'] = FieldDefinition::create('boolean') - ->setLabel(t('Default language')) - ->setDescription(t('Flag to indicate whether this is the default language.')); - - return $fields; - } - } diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php index 071ed67..d64afea 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php @@ -44,23 +44,4 @@ */ class EntityTestMulRev extends EntityTestRev { - /** - * {@inheritdoc} - */ - public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { - $fields = parent::baseFieldDefinitions($entity_type); - - $fields['revision_id'] = FieldDefinition::create('integer') - ->setLabel(t('Revision ID')) - ->setDescription(t('The version id of the test entity.')) - ->setReadOnly(TRUE) - ->setSetting('unsigned', TRUE); - - $fields['default_langcode'] = FieldDefinition::create('boolean') - ->setLabel(t('Default language')) - ->setDescription(t('Flag to indicate whether this is the default language.')); - - return $fields; - } - } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_default/Tid.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_default/Tid.php index bbfefb9..33b35a8 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_default/Tid.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_default/Tid.php @@ -168,12 +168,11 @@ public function getArgument() { // Just check, if a node could be detected. if (($node = $this->request->attributes->has('node')) && $node instanceof NodeInterface) { $taxonomy = array(); - $instances = field_info_instances('node', $node->getType()); - foreach ($instances as $instance) { - $field = $instance->getField(); - if ($field->type == 'taxonomy_term_reference') { - foreach ($node->get($field->name) as $item) { - $taxonomy[$item->target_id] = $field->settings['allowed_values'][0]['vocabulary']; + foreach ($node->getFieldDefinitions() as $field) { + if ($field->getType() == 'taxonomy_term_reference') { + foreach ($node->get($field->getName()) as $item) { + $allowed_values = $field->getSetting('allowed_values'); + $taxonomy[$item->target_id] = $allowed_values[0]['vocabulary']; } } } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorage.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorage.php index b7b9201..8a6385a 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorage.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorage.php @@ -151,4 +151,90 @@ public function resetWeights($vid) { ->execute(); } + /** + * {@inheritdoc} + */ + public function buildSchema() { + $schema = parent::buildSchema(); + + $schema['taxonomy_term_hierarchy'] = array( + 'description' => 'Stores the hierarchical relationship between terms.', + 'fields' => array( + 'tid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'Primary Key: The {taxonomy_term_data}.tid of the term.', + ), + 'parent' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => "Primary Key: The {taxonomy_term_data}.tid of the term's parent. 0 indicates no parent.", + ), + ), + 'indexes' => array( + 'parent' => array('parent'), + ), + 'foreign keys' => array( + 'taxonomy_term_data' => array( + 'table' => 'taxonomy_term_data', + 'columns' => array('tid' => 'tid'), + ), + ), + 'primary key' => array('tid', 'parent'), + ); + + $schema['taxonomy_index'] = array( + 'description' => 'Maintains denormalized information about node/term relationships.', + 'fields' => array( + 'nid' => array( + 'description' => 'The {node}.nid this record tracks.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'tid' => array( + 'description' => 'The term ID.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'sticky' => array( + 'description' => 'Boolean indicating whether the node is sticky.', + 'type' => 'int', + 'not null' => FALSE, + 'default' => 0, + 'size' => 'tiny', + ), + 'created' => array( + 'description' => 'The Unix timestamp when the node was created.', + 'type' => 'int', + 'not null' => TRUE, + 'default'=> 0, + ), + ), + 'indexes' => array( + 'term_node' => array('tid', 'sticky', 'created'), + 'nid' => array('nid'), + ), + 'foreign keys' => array( + 'tracked_node' => array( + 'table' => 'node', + 'columns' => array('nid' => 'nid'), + ), + 'term' => array( + 'table' => 'taxonomy_term_data', + 'columns' => array('tid' => 'tid'), + ), + ), + ); + + return $schema; + } + } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php index 0a67140..78c877f 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTermReferenceItemTest.php @@ -42,8 +42,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('taxonomy', 'taxonomy_term_data'); - $this->installSchema('taxonomy', 'taxonomy_term_hierarchy'); + $this->installEntitySchema('taxonomy_term'); $vocabulary = entity_create('taxonomy_vocabulary', array( 'name' => $this->randomName(), diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermValidationTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermValidationTest.php index 737f662..f545403 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermValidationTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermValidationTest.php @@ -34,7 +34,7 @@ public static function getInfo() { */ public function setUp() { parent::setUp(); - $this->installSchema('taxonomy', array('taxonomy_term_data')); + $this->installEntitySchema('taxonomy_term'); } /** diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install deleted file mode 100644 index a3f7e12..0000000 --- a/core/modules/taxonomy/taxonomy.install +++ /dev/null @@ -1,164 +0,0 @@ - 'Stores term information.', - 'fields' => array( - 'tid' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'Primary Key: Unique term ID.', - ), - 'uuid' => array( - 'description' => 'Unique Key: Universally unique identifier for this entity.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - 'vid' => array( - 'type' => 'varchar', - 'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The ID of the vocabulary to which the term is assigned.', - ), - 'langcode' => array( - 'description' => 'The {language}.langcode of this term.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'name' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The term name.', - ), - 'description__value' => array( - 'type' => 'text', - 'not null' => FALSE, - 'size' => 'big', - 'description' => 'A description of the term.', - ), - 'description__format' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => FALSE, - 'description' => 'The filter format ID of the description.', - ), - 'weight' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The weight of this term in relation to other terms.', - ), - 'changed' => array( - 'description' => 'The Unix timestamp when the term was most recently saved.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('tid'), - 'unique keys' => array( - 'uuid' => array('uuid'), - ), - 'indexes' => array( - 'taxonomy_tree' => array('vid', 'weight', 'name'), - 'vid_name' => array('vid', 'name'), - 'name' => array('name'), - ), - ); - - $schema['taxonomy_term_hierarchy'] = array( - 'description' => 'Stores the hierarchical relationship between terms.', - 'fields' => array( - 'tid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Primary Key: The {taxonomy_term_data}.tid of the term.', - ), - 'parent' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => "Primary Key: The {taxonomy_term_data}.tid of the term's parent. 0 indicates no parent.", - ), - ), - 'indexes' => array( - 'parent' => array('parent'), - ), - 'foreign keys' => array( - 'taxonomy_term_data' => array( - 'table' => 'taxonomy_term_data', - 'columns' => array('tid' => 'tid'), - ), - ), - 'primary key' => array('tid', 'parent'), - ); - - $schema['taxonomy_index'] = array( - 'description' => 'Maintains denormalized information about node/term relationships.', - 'fields' => array( - 'nid' => array( - 'description' => 'The {node}.nid this record tracks.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'tid' => array( - 'description' => 'The term ID.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'sticky' => array( - 'description' => 'Boolean indicating whether the node is sticky.', - 'type' => 'int', - 'not null' => FALSE, - 'default' => 0, - 'size' => 'tiny', - ), - 'created' => array( - 'description' => 'The Unix timestamp when the node was created.', - 'type' => 'int', - 'not null' => TRUE, - 'default'=> 0, - ), - ), - 'indexes' => array( - 'term_node' => array('tid', 'sticky', 'created'), - 'nid' => array('nid'), - ), - 'foreign keys' => array( - 'tracked_node' => array( - 'table' => 'node', - 'columns' => array('nid' => 'nid'), - ), - 'term' => array( - 'table' => 'taxonomy_term_data', - 'columns' => array('tid' => 'tid'), - ), - ), - ); - - return $schema; -} diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index bc53e48..b808012 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -786,10 +786,9 @@ function taxonomy_build_node_index($node) { if ($status && $node->isDefaultRevision()) { // Collect a unique list of all the term IDs from all node fields. $tid_all = array(); - foreach (field_info_instances('node', $node->getType()) as $instance) { - $field = $instance->getField(); + foreach ($node->getFieldDefinitions() as $field) { $field_name = $field->getName(); - if ($field->module == 'taxonomy') { + if ($field->getType() == 'taxonomy_term_reference') { foreach ($node->getTranslationLanguages() as $language) { foreach ($node->getTranslation($language->id)->$field_name as $item) { if (!$item->isEmpty()) { diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php index 8e37920..633ee0b 100644 --- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php +++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php @@ -48,7 +48,7 @@ function setUp() { // Configure the theme system. $this->installConfig(array('system', 'field')); - $this->installSchema('entity_test', 'entity_test'); + $this->installEntitySchema('entity_test'); // @todo Add helper methods for all of the following. diff --git a/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php b/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php index 9b93095..30af0bf 100644 --- a/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php +++ b/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php @@ -50,7 +50,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision')); + $this->installEntitySchema('entity_test_rev'); // Create the necessary formats. $this->installConfig(array('filter')); diff --git a/core/modules/tracker/tracker.install b/core/modules/tracker/tracker.install index 5f3e5a0..2a72272 100644 --- a/core/modules/tracker/tracker.install +++ b/core/modules/tracker/tracker.install @@ -16,12 +16,18 @@ function tracker_uninstall() { * Implements hook_install(). */ function tracker_install() { - $max_nid = db_query('SELECT MAX(nid) FROM {node}')->fetchField(); - if ($max_nid != 0) { - \Drupal::state()->set('tracker.index_nid', $max_nid); - // To avoid timing out while attempting to do a complete indexing, we - // simply call our cron job to remove stale records and begin the process. - tracker_cron(); + // @todo Is there some way to avoid this check? + if (\Drupal::database()->schema()->tableExists('node')) { + $nids = \Drupal::entityQuery('node') + ->sort('nid', 'DESC') + ->range(0, 1) + ->execute(); + if (!empty($nids)) { + \Drupal::state()->set('tracker.index_nid', reset($nids)); + // To avoid timing out while attempting to do a complete indexing, we + // simply call our cron job to remove stale records and begin the process. + tracker_cron(); + } } } diff --git a/core/modules/user/lib/Drupal/user/AccountFormController.php b/core/modules/user/lib/Drupal/user/AccountFormController.php index ed4cc07..75b71dd 100644 --- a/core/modules/user/lib/Drupal/user/AccountFormController.php +++ b/core/modules/user/lib/Drupal/user/AccountFormController.php @@ -366,9 +366,12 @@ public function validate(array $form, array &$form_state) { // Move text value for user signature into 'signature'. $form_state['values']['signature'] = $form_state['values']['signature']['value']; - $user_schema = drupal_get_schema('users'); - if (drupal_strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) { - $this->setFormError('signature', $form_state, $this->t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length']))); + // @todo Make the user signature field use a widget to benefit from + // automatic typed data validation. + $field_definitions = $this->entityManager->getFieldDefinitions('user', $this->getEntity()->bundle()); + $max_length = $field_definitions['signature']->getSetting('max_length'); + if (drupal_strlen($form_state['values']['signature']) > $max_length) { + $this->setFormError('signature', $form_state, $this->t('The signature is too long: it must be %max characters or less.', array('%max' => $max_length))); } } } diff --git a/core/modules/user/lib/Drupal/user/Entity/User.php b/core/modules/user/lib/Drupal/user/Entity/User.php index 8bb2f51..b2cfb40 100644 --- a/core/modules/user/lib/Drupal/user/Entity/User.php +++ b/core/modules/user/lib/Drupal/user/Entity/User.php @@ -506,8 +506,9 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['status'] = FieldDefinition::create('boolean') ->setLabel(t('User status')) - ->setDescription(t('Whether the user is active (1) or blocked (0).')) - ->setSetting('default_value', 1); + ->setDescription(t('Whether the user is active or blocked.')) + // @todo This should be FALSE. + ->setSetting('default_value', TRUE); $fields['created'] = FieldDefinition::create('created') ->setLabel(t('Created')) @@ -531,6 +532,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { // @todo Convert this to entity_reference_field, see // https://drupal.org/node/2044859. $fields['roles'] = FieldDefinition::create('string') + ->setCustomStorage(TRUE) ->setLabel(t('Roles')) ->setCardinality(FieldDefinitionInterface::CARDINALITY_UNLIMITED) ->setDescription(t('The roles the user has.')); diff --git a/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php b/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php index fb58bc1..6219f89 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php @@ -31,13 +31,22 @@ public static function getInfo() { */ function testUserAdmin() { $user_a = $this->drupalCreateUser(); + $user_a->name = 'User A'; $user_a->mail = $this->randomName() . '@example.com'; $user_a->save(); $user_b = $this->drupalCreateUser(array('administer taxonomy')); + $user_b->name = 'User B'; + $user_b->save(); $user_c = $this->drupalCreateUser(array('administer taxonomy')); + $user_c->name = 'User C'; + $user_c->save(); // Create admin user to delete registered user. $admin_user = $this->drupalCreateUser(array('administer users')); + // Use a predictable name so that we can reliably order the user admin page + // by name. + $admin_user->name = 'Admin user'; + $admin_user->save(); $this->drupalLogin($admin_user); $this->drupalGet('admin/people'); $this->assertText($user_a->getUsername(), 'Found user A on admin users page'); @@ -83,8 +92,12 @@ function testUserAdmin() { $this->assertTrue($account->isActive(), 'User C not blocked'); $edit = array(); $edit['action'] = 'user_block_user_action'; - $edit['user_bulk_form[1]'] = TRUE; - $this->drupalPostForm('admin/people', $edit, t('Apply')); + $edit['user_bulk_form[4]'] = TRUE; + $this->drupalPostForm('admin/people', $edit, t('Apply'), array( + // Sort the table by username so that we know reliably which user will be + // targeted with the blocking action. + 'query' => array('order' => 'name', 'sort' => 'asc') + )); $account = user_load($user_c->id(), TRUE); $this->assertTrue($account->isBlocked(), 'User C blocked'); @@ -97,8 +110,12 @@ function testUserAdmin() { // Test unblocking of a user from /admin/people page and sending of activation mail $editunblock = array(); $editunblock['action'] = 'user_unblock_user_action'; - $editunblock['user_bulk_form[1]'] = TRUE; - $this->drupalPostForm('admin/people', $editunblock, t('Apply')); + $editunblock['user_bulk_form[4]'] = TRUE; + $this->drupalPostForm('admin/people', $editunblock, t('Apply'), array( + // Sort the table by username so that we know reliably which user will be + // targeted with the blocking action. + 'query' => array('order' => 'name', 'sort' => 'asc') + )); $account = user_load($user_c->id(), TRUE); $this->assertTrue($account->isActive(), 'User C unblocked'); $this->assertMail("to", $account->getEmail(), "Activation mail sent to user C"); diff --git a/core/modules/user/lib/Drupal/user/Tests/UserInstallTest.php b/core/modules/user/lib/Drupal/user/Tests/UserInstallTest.php index 4e54ff8..25c61a8 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserInstallTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserInstallTest.php @@ -37,7 +37,9 @@ public static function getInfo() { */ protected function setUp() { parent::setUp(); - $this->installSchema('user', array('users')); + $this->container->get('module_handler')->loadInclude('user', 'install'); + $this->installEntitySchema('user'); + user_entity_schema_installed(array('user' => $this->container->get('entity.manager')->getStorage('user'))); } @@ -45,7 +47,6 @@ protected function setUp() { * Test that the initial users have correct values. */ public function testUserInstall() { - user_install(); $anon = db_query('SELECT * FROM {users} WHERE uid = 0')->fetchObject(); $admin = db_query('SELECT * FROM {users} WHERE uid = 1')->fetchObject(); $this->assertFalse(empty($anon->uuid), 'Anon user has a UUID'); diff --git a/core/modules/user/lib/Drupal/user/Tests/UserValidationTest.php b/core/modules/user/lib/Drupal/user/Tests/UserValidationTest.php index 7beec6a..2b50084 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserValidationTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserValidationTest.php @@ -36,7 +36,7 @@ public static function getInfo() { */ public function setUp() { parent::setUp(); - $this->installSchema('user', array('users')); + $this->installEntitySchema('user'); $this->installSchema('system', array('sequences')); } diff --git a/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php b/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php index eafc620..16ba123 100644 --- a/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php +++ b/core/modules/user/lib/Drupal/user/Tests/Views/UserUnitTestBase.php @@ -48,7 +48,7 @@ protected function setUp() { ViewTestData::createTestViews(get_class($this), array('user_test_views')); - $this->installSchema('user', array('users', 'users_roles')); + $this->installEntitySchema('user'); $this->installSchema('system', 'sequences'); $entity_manager = $this->container->get('entity.manager'); diff --git a/core/modules/user/lib/Drupal/user/UserStorage.php b/core/modules/user/lib/Drupal/user/UserStorage.php index d3f4990..d70adbb 100644 --- a/core/modules/user/lib/Drupal/user/UserStorage.php +++ b/core/modules/user/lib/Drupal/user/UserStorage.php @@ -9,7 +9,9 @@ use Drupal\Component\Uuid\UuidInterface; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface; use Drupal\Core\Password\PasswordInterface; use Drupal\Core\Database\Connection; use Drupal\field\FieldInfo; @@ -46,17 +48,20 @@ class UserStorage extends ContentEntityDatabaseStorage implements UserStorageInt * The entity type definition. * @param \Drupal\Core\Database\Connection $database * The database connection to be used. - * @param \Drupal\field\FieldInfo $field_info - * The field info service. - * @param \Drupal\Component\Uuid\UuidInterface $uuid_service - * The UUID Service. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. + * @param \Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface $schema_builder + * The entity schema builder. * @param \Drupal\Core\Password\PasswordInterface $password * The password hashing service. * @param \Drupal\user\UserDataInterface $user_data * The user data service. + * @param \Drupal\field\FieldInfo $field_info + * (optional) The field info service. Defaults to NULL as user storage can + * be instantiated before Field module is installed. */ - public function __construct(EntityTypeInterface $entity_type, Connection $database, FieldInfo $field_info, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) { - parent::__construct($entity_type, $database, $field_info, $uuid_service); + public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, PasswordInterface $password, UserDataInterface $user_data, FieldInfo $field_info = NULL) { + parent::__construct($entity_type, $database, $entity_manager, $field_info); $this->password = $password; $this->userData = $user_data; @@ -69,10 +74,10 @@ public static function createInstance(ContainerInterface $container, EntityTypeI return new static( $entity_type, $container->get('database'), - $container->get('field.info'), - $container->get('uuid'), + $container->get('entity.manager'), $container->get('password'), - $container->get('user.data') + $container->get('user.data'), + $container->has('field.info') ? $container->get('field.info') : NULL ); } @@ -154,4 +159,96 @@ public function updateLastLoginTimestamp(UserInterface $account) { ->execute(); } + /** + * {@inheritdoc} + */ + public function buildSchema() { + $schema = parent::buildSchema(); + + // The "users" table does not user serial identifiers. + $schema['users']['fields']['uid']['type'] = 'int'; + + // @todo Theoretically this table belongs to UserData instead of + // UserStorage. Add a schema creation facility to the former similar to + // this one. + $schema['users_data'] = array( + 'description' => 'Stores module data as key/value pairs per user.', + 'fields' => array( + 'uid' => array( + 'description' => 'Primary key: {users}.uid for user.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'module' => array( + 'description' => 'The name of the module declaring the variable.', + 'type' => 'varchar', + 'length' => DRUPAL_EXTENSION_NAME_MAX_LENGTH, + 'not null' => TRUE, + 'default' => '', + ), + 'name' => array( + 'description' => 'The identifier of the data.', + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + ), + 'value' => array( + 'description' => 'The value.', + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + ), + 'serialized' => array( + 'description' => 'Whether value is serialized.', + 'type' => 'int', + 'size' => 'tiny', + 'unsigned' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('uid', 'module', 'name'), + 'indexes' => array( + 'module' => array('module'), + 'name' => array('name'), + ), + 'foreign keys' => array( + 'uid' => array('users' => 'uid'), + ), + ); + + $schema['users_roles'] = array( + 'description' => 'Maps users to roles.', + 'fields' => array( + 'uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'Primary Key: {users}.uid for user.', + ), + 'rid' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'description' => 'Primary Key: ID for the role.', + ), + ), + 'primary key' => array('uid', 'rid'), + 'indexes' => array( + 'rid' => array('rid'), + ), + 'foreign keys' => array( + 'user' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), + ), + ); + + return $schema; + } + } diff --git a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml index b16cb3e..8b62fc7 100644 --- a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml +++ b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml @@ -115,7 +115,20 @@ display: group_items: { } reduce_duplicates: '1' plugin_id: user_permissions - sorts: { } + sorts: + uid: + id: uid + table: users + field: uid + relationship: none + group_type: group + admin_label: '' + order: ASC + exposed: false + expose: + label: '' + plugin_id: standard + provider: views human_name: test_filter_permission module: views id: test_filter_permission diff --git a/core/modules/user/user.info.yml b/core/modules/user/user.info.yml index 45a421a..dbdb520 100644 --- a/core/modules/user/user.info.yml +++ b/core/modules/user/user.info.yml @@ -6,3 +6,5 @@ version: VERSION core: 8.x required: true configure: user.admin_index +dependencies: + - entity diff --git a/core/modules/user/user.install b/core/modules/user/user.install index 97515a2..e5b9161 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -5,243 +5,36 @@ * Install, update and uninstall functions for the user module. */ -use Drupal\Core\Language\Language; -use Drupal\field\Field; +use Drupal\Core\Entity\ContentEntityDatabaseStorage; /** - * Implements hook_schema(). + * Implements hook_entity_schema_installed(). */ -function user_schema() { - // The table name here is plural, despite Drupal table naming standards, - // because "user" is a reserved word in many databases. - $schema['users'] = array( - 'description' => 'Stores user data.', - 'fields' => array( - 'uid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'Primary Key: Unique user ID.', - 'default' => 0, - ), - 'uuid' => array( - 'description' => 'Unique Key: Universally unique identifier for this entity.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => FALSE, - ), - 'name' => array( - 'type' => 'varchar', - 'length' => 60, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Unique user name.', - ), - 'langcode' => array( - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - 'description' => "The {language}.langcode of the user's profile.", - ), - 'pass' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => "User's password (hashed).", - ), - 'mail' => array( - 'type' => 'varchar', - 'length' => 254, - 'not null' => FALSE, - 'default' => '', - 'description' => "User's e-mail address.", - ), - 'signature' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => "User's signature.", - ), - 'signature_format' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => FALSE, - 'description' => 'The filter format ID of the signature.', - ), - 'created' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Timestamp for when user was created.', - ), - 'access' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Timestamp for previous time user accessed the site.', - ), - 'login' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => "Timestamp for user's last login.", - ), - 'status' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'Whether the user is active(1) or blocked(0).', - ), - 'timezone' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => FALSE, - 'description' => "User's time zone.", - ), - 'preferred_langcode' => array( - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The {language}.langcode that the user prefers for receiving emails and viewing the site.', - ), - 'preferred_admin_langcode' => array( - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The {language}.langcode that the user prefers for viewing administration pages.', - ), - 'init' => array( - 'type' => 'varchar', - 'length' => 254, - 'not null' => FALSE, - 'default' => '', - 'description' => 'E-mail address used for initial account creation.', - ), - ), - 'indexes' => array( - 'access' => array('access'), - 'created' => array('created'), - 'mail' => array('mail'), - ), - 'unique keys' => array( - 'uuid' => array('uuid'), - 'name' => array('name'), - ), - 'primary key' => array('uid'), - ); - - $schema['users_data'] = array( - 'description' => 'Stores module data as key/value pairs per user.', - 'fields' => array( - 'uid' => array( - 'description' => 'Primary key: {users}.uid for user.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'module' => array( - 'description' => 'The name of the module declaring the variable.', - 'type' => 'varchar', - 'length' => DRUPAL_EXTENSION_NAME_MAX_LENGTH, - 'not null' => TRUE, - 'default' => '', - ), - 'name' => array( - 'description' => 'The identifier of the data.', - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - ), - 'value' => array( - 'description' => 'The value.', - 'type' => 'blob', - 'not null' => FALSE, - 'size' => 'big', - ), - 'serialized' => array( - 'description' => 'Whether value is serialized.', - 'type' => 'int', - 'size' => 'tiny', - 'unsigned' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('uid', 'module', 'name'), - 'indexes' => array( - 'module' => array('module'), - 'name' => array('name'), - ), - 'foreign keys' => array( - 'uid' => array('users' => 'uid'), - ), - ); - - $schema['users_roles'] = array( - 'description' => 'Maps users to roles.', - 'fields' => array( - 'uid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Primary Key: {users}.uid for user.', - ), - 'rid' => array( - 'type' => 'varchar', - 'length' => 64, - 'not null' => TRUE, - 'description' => 'Primary Key: ID for the role.', - ), - ), - 'primary key' => array('uid', 'rid'), - 'indexes' => array( - 'rid' => array('rid'), - ), - 'foreign keys' => array( - 'user' => array( - 'table' => 'users', - 'columns' => array('uid' => 'uid'), - ), - ), - ); - - return $schema; -} - -/** - * Implements hook_install(). - */ -function user_install() { - // Insert a row for the anonymous user. - db_insert('users') - ->fields(array( - 'uid' => 0, - 'uuid' => \Drupal::service('uuid')->generate(), - 'name' => '', - 'mail' => '', - 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->id, - )) - ->execute(); - - // We need some placeholders here as name and mail are uniques. - // This will be changed by the settings form in the installer. - db_insert('users') - ->fields(array( - 'uid' => 1, - 'uuid' => \Drupal::service('uuid')->generate(), - 'name' => 'placeholder-for-uid-1', - 'mail' => 'placeholder-for-uid-1', - 'created' => REQUEST_TIME, - 'status' => 1, - 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->id, - )) - ->execute(); +function user_entity_schema_installed(array $storages) { + if (isset($storages['user'])) { + // Insert a row for the anonymous user. + db_insert('users') + ->fields(array( + 'uid' => 0, + 'uuid' => \Drupal::service('uuid')->generate(), + 'name' => '', + 'mail' => '', + 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->id, + )) + ->execute(); + + // We need some placeholders here as name and mail are uniques. + // This will be changed by the settings form in the installer. + db_insert('users') + ->fields(array( + 'uid' => 1, + 'uuid' => \Drupal::service('uuid')->generate(), + 'name' => 'placeholder-for-uid-1', + 'mail' => 'placeholder-for-uid-1', + 'created' => REQUEST_TIME, + 'status' => 1, + 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->id, + )) + ->execute(); + } } diff --git a/core/modules/views/lib/Drupal/views/Entity/View.php b/core/modules/views/lib/Drupal/views/Entity/View.php index 2beb7ac..54ab465 100644 --- a/core/modules/views/lib/Drupal/views/Entity/View.php +++ b/core/modules/views/lib/Drupal/views/Entity/View.php @@ -277,7 +277,10 @@ public function calculateDependencies() { // Ensure that the view is dependant on the module that provides the schema // for the base table. $schema = drupal_get_schema($this->base_table); - if ($this->module != $schema['module']) { + // @todo Entity base tables are no longer registered in hook_schema(). Once + // we automate the views data for entity types add the entity type + // type provider as a dependency. + if ($schema && $this->module != $schema['module']) { $this->addDependency('module', $schema['module']); } diff --git a/core/modules/views/lib/Drupal/views/Tests/Entity/RowEntityRenderersTest.php b/core/modules/views/lib/Drupal/views/Tests/Entity/RowEntityRenderersTest.php index 1694fd0..18ddff0 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Entity/RowEntityRenderersTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Entity/RowEntityRenderersTest.php @@ -56,8 +56,8 @@ public static function getInfo() { protected function setUp() { parent::setUp(); - $this->installSchema('node', array('node', 'node_revision', 'node_field_data', 'node_field_revision', 'node_access')); - $this->installSchema('user', array('users')); + $this->installEntitySchema('node'); + $this->installEntitySchema('user'); $this->installConfig(array('node', 'language')); $this->langcodes = array(\Drupal::languageManager()->getDefaultLanguage()->id); diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/AreaTextTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/AreaTextTest.php index 2ac78f8..3ab29d7 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/AreaTextTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/AreaTextTest.php @@ -38,7 +38,7 @@ protected function setUp() { parent::setUp(); $this->installConfig(array('system', 'filter')); - $this->installSchema('user', array('users')); + $this->installEntitySchema('user'); } public function testAreaText() { diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAliasTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAliasTest.php index caec4c7..ac9b920 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAliasTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAliasTest.php @@ -35,7 +35,7 @@ public static function getInfo() { protected function setUp() { parent::setUp(); - $this->installSchema('user', 'users'); + $this->installEntitySchema('user'); } /** diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php index fae3515..5542619 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/RelationshipJoinTestBase.php @@ -26,7 +26,7 @@ * Overrides \Drupal\views\Tests\ViewUnitTestBase::setUpFixtures(). */ protected function setUpFixtures() { - $this->installSchema('user', array('users', 'users_roles')); + $this->installEntitySchema('user'); $this->installConfig(array('user')); parent::setUpFixtures(); diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/RowEntityTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/RowEntityTest.php index 0b406f5..8cecbd1 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/RowEntityTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/RowEntityTest.php @@ -53,7 +53,7 @@ public static function getInfo() { protected function setUp() { parent::setUp(); - $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy')); + $this->installEntitySchema('taxonomy_term'); $this->installConfig(array('taxonomy')); \Drupal::service('router.builder')->rebuild(); } diff --git a/core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php b/core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php index 7b7d0b7..ff0c225 100644 --- a/core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/QueryGroupByTest.php @@ -49,7 +49,7 @@ public static function getInfo() { protected function setUp() { parent::setUp(); - $this->installSchema('entity_test', array('entity_test')); + $this->installEntitySchema('entity_test'); $this->storage = $this->container->get('entity.manager')->getStorage('entity_test'); } diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php index c0228ba..4e2715d 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php @@ -84,10 +84,11 @@ public static function getInfo() { } protected function setUpFixtures() { - $this->installSchema('user', array('users')); - $this->installSchema('node', array('node', 'node_field_data')); - $this->installSchema('comment', array('comment', 'comment_entity_statistics')); + $this->installEntitySchema('user'); + $this->installEntitySchema('node'); + $this->installEntitySchema('comment'); $this->installConfig(array('field')); + entity_create('node_type', array( 'type' => 'page', 'name' => 'Page', diff --git a/core/tests/Drupal/Tests/Core/Entity/Schema/ContentEntitySchemaHandlerTest.php b/core/tests/Drupal/Tests/Core/Entity/Schema/ContentEntitySchemaHandlerTest.php new file mode 100644 index 0000000..8f1e32b --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Entity/Schema/ContentEntitySchemaHandlerTest.php @@ -0,0 +1,312 @@ + 'Content entity schema handler', + 'description' => 'Tests the schema generation for content entities.', + 'group' => 'Entity', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); + $this->entityType = $this->getMock('Drupal\Core\Entity\ContentEntityTypeInterface'); + $this->sqlStorage = $this->getMock('Drupal\Core\Entity\ContentEntityDatabaseStorageInterface'); + + // Set up basic expectations that apply to all tests. + $this->entityType->expects($this->any()) + ->method('id') + ->will($this->returnValue('entity_test')); + + $this->sqlStorage->expects($this->any()) + ->method('getBaseTable') + ->will($this->returnValue('entity_test')); + } + + /** + * Tests ContentEntitySchemaHandler::getSchema() with a basic table layout. + * + * This tests that the schema is generated correctly for non-revisionable, + * non-translatable entities. + * + * @covers ::getSchema + */ + public function testGetSchemaLayoutBase() { + $this->entityType->expects($this->any()) + ->method('getKey') + ->will($this->returnValueMap(array( + array('id', 'id'), + array('uuid', NULL), + ))); + + $this->sqlStorage->expects($this->once()) + ->method('getLayoutType') + ->will($this->returnValue(ContentEntityDatabaseStorageInterface::LAYOUT_BASE)); + + $this->setupBaseLayoutFieldDefinitions(); + + $this->setupSchemaHandler(); + + $this->sqlStorage->expects($this->once()) + ->method('getTableMapping') + ->will($this->returnValue( + array( + 'entity_test' => array( + 'id' => array('id'), + 'name' => array('name'), + 'type' => array('type'), + ), + ) + )); + + $expected = array( + 'entity_test' => array( + 'description' => 'The base table for entity_test entities.', + 'fields' => array( + 'id' => array( + 'type' => 'serial', + 'description' => 'The id field.', + 'not null' => TRUE, + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'The name field.', + 'not null' => FALSE, + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 32, + 'description' => 'The type field.', + 'not null' => FALSE, + ), + ), + 'primary key' => array('id'), + ), + ); + $actual = $this->schemaHandler->getSchema(); + + $this->assertEquals($expected, $actual); + } + + /** + * Tests ContentEntitySchemaHandler::getSchema() with a basic table layout. + * + * This tests that the schema is generated correctly for non-revisionable, + * non-translatable entities with a UUID key. + * + * @covers ::getSchema + */ + public function testGetSchemaLayoutBaseWithUuuid() { + $this->entityType->expects($this->any()) + ->method('getKey') + ->will($this->returnValueMap(array( + array('id', 'id'), + array('uuid', 'uuid'), + ))); + + $this->sqlStorage->expects($this->once()) + ->method('getLayoutType') + ->will($this->returnValue(ContentEntityDatabaseStorageInterface::LAYOUT_BASE)); + + $this->setupBaseLayoutFieldDefinitions(); + $this->setupFieldDefinition('uuid', array( + 'columns' => array( + 'value' => array( + 'type' => 'varchar', + 'length' => 128, + ), + ), + )); + + $this->setupSchemaHandler(); + + $this->sqlStorage->expects($this->once()) + ->method('getTableMapping') + ->will($this->returnValue( + array( + 'entity_test' => array( + 'id' => array('id'), + 'uuid' => array('uuid'), + 'name' => array('name'), + 'type' => array('type'), + ), + ) + )); + + $expected = array( + 'entity_test' => array( + 'description' => 'The base table for entity_test entities.', + 'fields' => array( + 'id' => array( + 'type' => 'serial', + 'description' => 'The id field.', + 'not null' => TRUE, + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'The name field.', + 'not null' => FALSE, + ), + 'type' => array( + 'type' => 'varchar', + 'length' => 32, + 'description' => 'The type field.', + 'not null' => FALSE, + ), + 'uuid' => array( + 'type' => 'varchar', + 'length' => 128, + 'description' => 'The uuid field.', + 'not null' => TRUE, + ), + ), + 'primary key' => array('id'), + 'unique keys' => array( + 'entity_test__uuid' => array('uuid'), + ), + ), + ); + $actual = $this->schemaHandler->getSchema(); + + $this->assertEquals($expected, $actual); + } + + /** + * Sets up the field definitions that are used for the base storage layout. + */ + protected function setupBaseLayoutFieldDefinitions() { + // @see \Drupal\Core\Field\Plugin\Field\FieldType\IntegerItem::schema() + $this->setupFieldDefinition('id', array( + 'columns' => array( + 'value' => array( + 'type' => 'int', + ), + ), + )); + // @see \Drupal\Core\Field\Plugin\Field\FieldType\StringItem::schema() + $this->setupFieldDefinition('name', array( + 'columns' => array( + 'value' => array( + 'type' => 'varchar', + 'length' => 255, + ), + ), + )); + // @see \Drupal\Core\Field\Plugin\Field\FieldType\StringItem::schema() + $this->setupFieldDefinition('type', array( + 'columns' => array( + 'value' => array( + 'type' => 'varchar', + 'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH, + ), + ), + )); + } + + /** + * Sets up the schema handler. + * + * This uses the field definitions set in $this->fieldDefinitions. + */ + public function setupSchemaHandler() { + $this->entityManager->expects($this->once()) + ->method('getBaseFieldDefinitions') + ->with('entity_test') + ->will($this->returnValue($this->fieldDefinitions)); + $this->schemaHandler = new ContentEntitySchemaHandler( + $this->entityManager, + $this->entityType, + $this->sqlStorage + ); + } + + /** + * Sets up a field definition. + * + * @param string $field_name + * The field name. + * @param array $schema + * The schema array of the field definition, as returned from + * FieldDefinitionInterface::schema(). + */ + public function setupFieldDefinition($field_name, array $schema) { + $this->fieldDefinitions[$field_name] = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface'); + $this->fieldDefinitions[$field_name]->expects($this->once()) + ->method('getName') + ->will($this->returnValue($field_name)); + $this->fieldDefinitions[$field_name]->expects($this->once()) + ->method('getDescription') + ->will($this->returnValue("The $field_name field.")); + // @todo ContentEntitySchemaHandler should be fixed to only call getSchema() + // once per field definition. + $this->fieldDefinitions[$field_name]->expects($this->exactly(2)) + ->method('getSchema') + ->will($this->returnValue($schema)); + } + +} +