diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
index da6890e..3d506d0 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
@@ -8,7 +8,13 @@
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\Entity\Sql\DefaultTableMapping;
+use Drupal\Core\Entity\Sql\SqlStorageInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Language\Language;
use Drupal\field\FieldConfigUpdateForbiddenException;
@@ -25,7 +31,21 @@
* 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 SqlStorageInterface, ContentEntitySchemaHandlerInterface {
+
+ /**
+ * The base field definitions for this entity type.
+ *
+ * @var \Drupal\Core\Field\FieldStorageDefinitionInterface[]
+ */
+ protected $storageDefinitions;
+
+ /**
+ * A mapping of schema fields that will be stored per entity table.
+ *
+ * @var \Drupal\Core\Entity\Sql\TableMappingInterface
+ */
+ protected $tableMapping;
/**
* Name of entity's revision database table field, if it supports revisions.
@@ -37,6 +57,20 @@ class ContentEntityDatabaseStorage extends ContentEntityStorageBase {
protected $revisionKey = FALSE;
/**
+ * The entity langcode key
+ *
+ * @var string|bool
+ */
+ protected $langcodeKey = FALSE;
+
+ /**
+ * The base table of the entity.
+ *
+ * @var string
+ */
+ protected $baseTable;
+
+ /**
* The table that stores revisions, if the entity supports revisions.
*
* @var string
@@ -72,6 +106,13 @@ class ContentEntityDatabaseStorage extends ContentEntityStorageBase {
protected $entityManager;
/**
+ * The entity schema handler.
+ *
+ * @var \Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface
+ */
+ protected $schemaHandler;
+
+ /**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
@@ -98,23 +139,191 @@ public function __construct(EntityTypeInterface $entity_type, Connection $databa
$this->database = $database;
$this->entityManager = $entity_manager;
- // Check if the entity type supports UUIDs.
- $this->uuidKey = $this->entityType->getKey('uuid');
+ // @todo Remove the check for FieldDefinitionInterface::isMultiple() when
+ // multiple-value base fields are supported in
+ // https://drupal.org/node/2248977.
+ $this->storageDefinitions = array_filter($entity_manager->getBaseFieldDefinitions($entity_type->id()), function (FieldDefinitionInterface $definition) {
+ return !$definition->isComputed() && !$definition->hasCustomStorage() && !$definition->isMultiple();
+ });
+
+ // @todo Remove table names from the entity type definition in
+ // https://drupal.org/node/2232465
+ $this->baseTable = $this->entityType->getBaseTable() ?: $this->entityTypeId;
+
+ $revisionable = $this->entityType->isRevisionable();
+ if ($revisionable) {
+ $this->revisionKey = $this->entityType->getKey('revision') ?: 'revision_id';
+ $this->revisionTable = $this->entityType->getRevisionTable() ?: $this->entityTypeId . '_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
+ $translatable = $this->entityType->isTranslatable() && $this->entityType->getDataTable();
+ if ($translatable) {
+ $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';
+ }
+ if ($revisionable && $translatable) {
+ $this->revisionDataTable = $this->entityType->getRevisionDataTable() ?: $this->entityTypeId . '_field_revision';
+ }
+ }
+
+ /**
+ * Returns the base table name.
+ *
+ * @return string
+ * The table name.
+ */
+ public function getBaseTable() {
+ return $this->baseTable;
+ }
+
+ /**
+ * Returns the revision table name.
+ *
+ * @return string|false
+ * The table name or FALSE if it is not available.
+ */
+ public function getRevisionTable() {
+ return $this->revisionTable;
+ }
+
+ /**
+ * Returns the data table name.
+ *
+ * @return string|false
+ * The table name or FALSE if it is not available.
+ */
+ public function getDataTable() {
+ return $this->dataTable;
+ }
- if ($this->entityType->isRevisionable()) {
- $this->revisionKey = $this->entityType->getKey('revision');
- $this->revisionTable = $this->entityType->getRevisionTable();
+ /**
+ * Returns the revision data table name.
+ *
+ * @return string|false
+ * The table name or FALSE if it is not available.
+ */
+ public function getRevisionDataTable() {
+ return $this->revisionDataTable;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSchema() {
+ 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;
+ }
- // 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 getTableMapping() {
+ if (!isset($this->tableMapping)) {
+ $this->tableMapping = new DefaultTableMapping($this->storageDefinitions);
+
+ $key_fields = array_values(array_filter(array($this->idKey, $this->revisionKey, $this->bundleKey, $this->uuidKey, $this->langcodeKey)));
+ $all_fields = array_keys($this->storageDefinitions);
+ $revisionable_fields = array_keys(array_filter($this->storageDefinitions, function (FieldStorageDefinitionInterface $definition) {
+ return $definition->isRevisionable();
+ }));
+ // Make sure the key fields come first in the list of fields.
+ $all_fields = array_merge($key_fields, array_diff($all_fields, $key_fields));
+
+ // Nodes have all three of these fields, while custom blocks only have
+ // log.
+ // @todo Provide automatic definitions for revision metadata fields in
+ // https://drupal.org/node/2248983.
+ // @todo Rename 'log' to 'revision_log' in
+ // https://drupal.org/node/2248991.
+ $revision_metadata_fields = array_intersect(array('revision_timestamp', 'revision_uid', 'log'), $all_fields);
+
+ $revisionable = $this->entityType->isRevisionable();
+ // @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
+ $translatable = $this->entityType->getDataTable() && $this->entityType->isTranslatable();
+ if (!$revisionable && !$translatable) {
+ // The base layout stores all the base field values in the base table.
+ $this->tableMapping->setFieldNames($this->baseTable, $all_fields);
+ }
+ elseif ($revisionable && !$translatable) {
+ // The revisionable layout stores all the base field values in the base
+ // table, except for revision metadata fields. Revisionable fields
+ // denormalized in the base table but also stored in the revision table
+ // together with the entity ID and the revision ID as identifiers.
+ $this->tableMapping->setFieldNames($this->baseTable, array_diff($all_fields, $revision_metadata_fields));
+ $revision_key_fields = array($this->idKey, $this->revisionKey);
+ $this->tableMapping->setFieldNames($this->revisionTable, array_merge($revision_key_fields, $revisionable_fields));
+ }
+ elseif (!$revisionable && $translatable) {
+ // Multilingual layouts store key field values in the base table. The
+ // 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. This means that only the UUID is not stored on
+ // the data table.
+ $this->tableMapping
+ ->setFieldNames($this->baseTable, $key_fields)
+ ->setFieldNames($this->dataTable, array_values(array_diff($all_fields, array($this->uuidKey))))
+ // Add the denormalized 'default_langcode' field to the mapping. Its
+ // value is identical to the query expression
+ // "base_table.langcode = data_table.langcode"
+ ->setExtraColumns($this->dataTable, array('default_langcode'));
+ }
+ elseif ($revisionable && $translatable) {
+ // 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 revisionable fields and the data table
+ // holds the data field values for all non-revisionable fields. The data
+ // field values of revisionable fields are denormalized in the data
+ // table, as well.
+ $this->tableMapping->setFieldNames($this->baseTable, array_values(array_diff($key_fields, array($this->langcodeKey))));
+
+ // Like in the multilingual, non-revisionable case the UUID is not
+ // in the data table. Additionally, do not store revision metadata
+ // fields in the data table.
+ $data_fields = array_values(array_diff($all_fields, array($this->uuidKey), $revision_metadata_fields));
+ $this->tableMapping
+ ->setFieldNames($this->dataTable, $data_fields)
+ // Add the denormalized 'default_langcode' field to the mapping. Its
+ // value is identical to the query expression
+ // "base_langcode = data_table.langcode" where "base_langcode" is
+ // the language code of the default revision.
+ ->setExtraColumns($this->dataTable, array('default_langcode'));
+
+ $revision_base_fields = array_merge(array($this->idKey, $this->revisionKey, $this->langcodeKey), $revision_metadata_fields);
+ $this->tableMapping->setFieldNames($this->revisionTable, $revision_base_fields);
+
+ $revision_data_key_fields = array($this->idKey, $this->revisionKey, $this->langcodeKey);
+ $revision_data_fields = array_diff($revisionable_fields, $revision_metadata_fields);
+ $this->tableMapping
+ ->setFieldNames($this->revisionDataTable, array_merge($revision_data_key_fields, $revision_data_fields))
+ // Add the denormalized 'default_langcode' field to the mapping. Its
+ // value is identical to the query expression
+ // "revision_table.langcode = data_table.langcode".
+ ->setExtraColumns($this->revisionDataTable, array('default_langcode'));
}
}
+
+ return $this->tableMapping;
}
/**
@@ -215,13 +424,14 @@ protected function attachPropertyData(array &$entities) {
}
$data = $query->execute();
- $field_definitions = \Drupal::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_fields = array_diff_key($table_mapping->getFieldNames($this->revisionDataTable), $table_mapping->getFieldNames($this->baseTable));
}
else {
- $data_column_names = array_flip(drupal_schema_fields_sql($this->entityType->getDataTable()));
+ $data_fields = $table_mapping->getFieldNames($this->dataTable);
}
foreach ($data as $values) {
@@ -232,23 +442,16 @@ protected function attachPropertyData(array &$entities) {
$langcode = empty($values['default_langcode']) ? $values['langcode'] : Language::LANGCODE_DEFAULT;
$translations[$id][$langcode] = TRUE;
- foreach (array_keys($field_definitions) as $field_name) {
- // Handle columns named directly after the field.
- if (isset($data_column_names[$field_name])) {
- $entities[$id][$field_name][$langcode] = $values[$field_name];
+
+ foreach ($data_fields as $field_name) {
+ $columns = $table_mapping->getColumnNames($field_name);
+ // Do not key single-column fields by property name.
+ if (count($columns) == 1) {
+ $entities[$id][$field_name][$langcode] = $values[reset($columns)];
}
else {
- // @todo Change this logic to be based on a mapping of field
- // definition properties (translatability, revisionability) in
- // https://drupal.org/node/2144631.
- foreach ($data_column_names as $data_column_name) {
- // Handle columns named [field_name]__[column_name], for which we
- // need to look through all column names from the table that start
- // with the name of the field.
- if (($data_field_name = strstr($data_column_name, '__', TRUE)) && $data_field_name === $field_name) {
- $property_name = substr($data_column_name, strpos($data_column_name, '__') + 2);
- $entities[$id][$field_name][$langcode][$property_name] = $values[$data_column_name];
- }
+ foreach ($columns as $property_name => $column_name) {
+ $entities[$id][$field_name][$langcode][$property_name] = $values[$column_name];
}
}
}
@@ -352,11 +555,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 = $table_mapping->getAllColumns($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 = $table_mapping->getAllColumns($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]);
@@ -460,6 +664,7 @@ public function save(EntityInterface $entity) {
db_ignore_slave();
return $return;
}
+
catch (\Exception $e) {
$transaction->rollback();
watchdog_exception($this->entityTypeId, $e);
@@ -477,7 +682,12 @@ protected function doSave($id, EntityInterface $entity) {
$is_new = $entity->isNew();
if (!$is_new) {
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
@@ -485,13 +695,13 @@ protected function doSave($id, 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);
@@ -502,7 +712,17 @@ protected function doSave($id, 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();
@@ -512,8 +732,9 @@ protected function doSave($id, EntityInterface $entity) {
$this->savePropertyData($entity);
}
if ($this->revisionDataTable) {
- $this->savePropertyData($entity, 'revision_data_table');
+ $this->savePropertyData($entity, $this->revisionDataTable);
}
+
$entity->enforceIsNew(FALSE);
if ($this->revisionTable) {
$entity->setNewRevision(FALSE);
@@ -543,13 +764,14 @@ protected function has($id, 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;
@@ -564,7 +786,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))
@@ -587,53 +809,35 @@ protected function invokeHook($hook, EntityInterface $entity) {
/**
* 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') {
- $record = new \stdClass();
- $values = array();
- $definitions = $entity->getFieldDefinitions();
- $schema = drupal_get_schema($this->entityType->get($table_key));
- $is_new = $entity->isNew();
-
- $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;
+ protected function mapToStorageRecord(ContentEntityInterface $entity, $table_name = NULL) {
+ if (!isset($table_name)) {
+ $table_name = $this->baseTable;
}
- // 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;
+ $record = new \stdClass();
+ $table_mapping = $this->getTableMapping();
+ foreach ($table_mapping->getFieldNames($table_name) as $field_name) {
+ $columns = $table_mapping->getColumnNames($field_name);
+ if (!empty($this->storageDefinitions[$field_name])) {
+ $definition = $this->storageDefinitions[$field_name];
+ foreach ($columns as $column_name => $schema_name) {
+ $value = isset($entity->$field_name->$column_name) ? $entity->$field_name->$column_name : NULL;
+ if (!empty($definition->getSchema()['columns'][$column_name]['serialize'])) {
+ $value = serialize($value);
+ }
+ $record->$schema_name = drupal_schema_get_field_value($definition->getSchema()['columns'][$column_name], $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;
}
@@ -642,15 +846,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;
@@ -666,12 +872,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}))
@@ -680,7 +894,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/EntityStorageBase.php b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
index 5d24441..f10eba9 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
@@ -76,6 +76,7 @@ public function __construct(EntityTypeInterface $entity_type) {
$this->entityTypeId = $entity_type->id();
$this->entityType = $entity_type;
$this->idKey = $this->entityType->getKey('id');
+ $this->uuidKey = $this->entityType->getKey('uuid');
$this->entityClass = $this->entityType->getClass();
}
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index 50ab59b..9847483 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
@@ -12,6 +12,7 @@
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\ContentEntityDatabaseStorage;
use Drupal\Core\Entity\Query\QueryException;
+use Drupal\Core\Entity\Sql\SqlStorageInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\FieldConfigInterface;
@@ -35,7 +36,6 @@ class Tables implements TablesInterface {
*/
protected $entityTables = array();
-
/**
* Field table array, key is table name, value is alias.
*
@@ -46,10 +46,18 @@ class Tables implements TablesInterface {
protected $fieldTables = array();
/**
+ * The entity manager.
+ *
+ * @var \Drupal\Core\Entity\EntityManager
+ */
+ protected $entityManager;
+
+ /**
* @param \Drupal\Core\Database\Query\SelectInterface $sql_query
*/
public function __construct(SelectInterface $sql_query) {
$this->sqlQuery = $sql_query;
+ $this->entityManager = \Drupal::entityManager();
}
/**
@@ -57,7 +65,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();
$age = $this->sqlQuery->getMetaData('age');
// This variable ensures grouping works correctly. For example:
// ->condition('tags', 2, '>')
@@ -73,13 +80,13 @@ 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);
$field_storage_definitions = array();
// @todo Needed for menu links, make this implementation content entity
// specific after https://drupal.org/node/2256521.
if ($entity_type instanceof ContentEntityTypeInterface) {
- $field_storage_definitions = $entity_manager->getFieldStorageDefinitions($entity_type_id);
+ $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
}
for ($key = 0; $key <= $count; $key ++) {
// If there is revision support and only the current revision is being
@@ -154,10 +161,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);
}
@@ -174,8 +181,8 @@ public function addField($field, $type, $langcode) {
if (isset($propertyDefinitions[$relationship_specifier]) && $field->getPropertyDefinition('entity')->getDataType() == 'entity_reference' ) {
// If it is, use the entity type.
$entity_type_id = $propertyDefinitions[$relationship_specifier]->getTargetDefinition()->getEntityTypeId();
- $entity_type = $entity_manager->getDefinition($entity_type_id);
- $field_storage_definitions = $entity_manager->getFieldStorageDefinitions($entity_type_id);
+ $entity_type = $this->entityManager->getDefinition($entity_type_id);
+ $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($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);
@@ -199,8 +206,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);
}
@@ -241,4 +248,27 @@ 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()->getAllColumns($table);
+ }
+ 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..9fa1d95
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php
@@ -0,0 +1,517 @@
+entityType = $entity_type;
+ $this->storageDefinitions = $entity_manager->getFieldStorageDefinitions($entity_type->id());
+ $this->storage = $storage;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSchema() {
+ // Prepare basic information about the entity type.
+ $tables = $this->getTables();
+
+ 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();
+ }
+
+ $table_mapping = $this->storage->getTableMapping();
+ foreach ($table_mapping->getTableNames() as $table_name) {
+ // Add the schema from field definitions.
+ foreach ($table_mapping->getFieldNames($table_name) as $field_name) {
+ $column_names = $table_mapping->getColumnNames($field_name);
+ $this->addFieldSchema($schema[$table_name], $field_name, $column_names);
+ }
+
+ // Add the schema for extra fields.
+ foreach ($table_mapping->getExtraColumns($table_name) as $column_name) {
+ if ($column_name == 'default_langcode') {
+ $this->addDefaultLangcodeSchema($schema[$table_name]);
+ }
+ }
+ }
+
+ // 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.
+ * @param string[] $column_mapping
+ * A mapping of field column names to database column names.
+ */
+ protected function addFieldSchema(array &$schema, $field_name, array $column_mapping) {
+ $field_schema = $this->storageDefinitions[$field_name]->getSchema();
+ $field_description = $this->storageDefinitions[$field_name]->getDescription();
+
+ foreach ($column_mapping as $field_column_name => $schema_field_name) {
+ $column_schema = $field_schema['columns'][$field_column_name];
+
+ $schema['fields'][$schema_field_name] = $column_schema;
+ $schema['fields'][$schema_field_name]['description'] = $field_description;
+ // Only entity keys are required.
+ $keys = $this->entityType->getKeys() + array('langcode' => 'langcode');
+ // The label is an entity key, but label fields are not necessarily
+ // required.
+ // Because entity ID and revision ID are both serial fields in the base
+ // and revision table respectively, the revision ID is not known yet, when
+ // inserting data into the base table. Instead the revision ID in the base
+ // table is updated after the data has been inserted into the revision
+ // table. For this reason the revision ID field cannot be marked as NOT
+ // NULL.
+ unset($keys['label'], $keys['revision']);
+ // Key fields may not be NULL.
+ if (in_array($field_name, $keys)) {
+ $schema['fields'][$schema_field_name]['not null'] = TRUE;
+ }
+ }
+
+ if (!empty($field_schema['indexes'])) {
+ $indexes = $this->getFieldIndexes($field_name, $field_schema, $column_mapping);
+ $schema['indexes'] = array_merge($schema['indexes'], $indexes);
+ }
+
+ if (!empty($field_schema['unique keys'])) {
+ $unique_keys = $this->getFieldUniqueKeys($field_name, $field_schema, $column_mapping);
+ $schema['unique keys'] = array_merge($schema['unique keys'], $unique_keys);
+ }
+
+ if (!empty($field_schema['foreign keys'])) {
+ $foreign_keys = $this->getFieldForeignKeys($field_name, $field_schema, $column_mapping);
+ $schema['foreign keys'] = array_merge($schema['foreign keys'], $foreign_keys);
+ }
+ }
+
+ /**
+ * Returns an index schema array for a given field.
+ *
+ * @param string $field_name
+ * The name of the field.
+ * @param array $field_schema
+ * The schema of the field.
+ * @param string[] $column_mapping
+ * A mapping of field column names to database column names.
+ *
+ * @return array
+ * The schema definition for the indexes.
+ */
+ protected function getFieldIndexes($field_name, array $field_schema, array $column_mapping) {
+ return $this->getFieldSchemaData($field_name, $field_schema, $column_mapping, 'indexes');
+ }
+
+ /**
+ * Returns a unique key schema array for a given field.
+ *
+ * @param string $field_name
+ * The name of the field.
+ * @param array $field_schema
+ * The schema of the field.
+ * @param string[] $column_mapping
+ * A mapping of field column names to database column names.
+ *
+ * @return array
+ * The schema definition for the unique keys.
+ */
+ protected function getFieldUniqueKeys($field_name, array $field_schema, array $column_mapping) {
+ return $this->getFieldSchemaData($field_name, $field_schema, $column_mapping, 'unique keys');
+ }
+
+ /**
+ * Returns field schema data for the given key.
+ *
+ * @param string $field_name
+ * The name of the field.
+ * @param array $field_schema
+ * The schema of the field.
+ * @param string[] $column_mapping
+ * A mapping of field column names to database column names.
+ * @param string $schema_key
+ * The type of schema data. Either 'indexes' or 'unique keys'.
+ *
+ * @return array
+ * The schema definition for the specified key.
+ */
+ protected function getFieldSchemaData($field_name, array $field_schema, array $column_mapping, $schema_key) {
+ $data = array();
+
+ foreach ($field_schema[$schema_key] as $key => $columns) {
+ // To avoid clashes with entity-level indexes or unique keys we use
+ // "{$entity_type_id}_field__" as a prefix instead of just
+ // "{$entity_type_id}__". We additionally namespace the specifier by the
+ // field name to avoid clashes when multiple fields of the same type are
+ // added to an entity type.
+ $entity_type_id = $this->entityType->id();
+ $real_key = "{$entity_type_id}_field__{$field_name}__{$key}";
+ foreach ($columns as $column) {
+ // Allow for indexes and unique keys to specified as an array of column
+ // name and length.
+ if (is_array($column)) {
+ list($column_name, $length) = $column;
+ $data[$real_key][] = array($column_mapping[$column_name], $length);
+ }
+ else {
+ $data[$real_key][] = $column_mapping[$column];
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Returns field foreign keys.
+ *
+ * @param string $field_name
+ * The name of the field.
+ * @param array $field_schema
+ * The schema of the field.
+ * @param string[] $column_mapping
+ * A mapping of field column names to database column names.
+ *
+ * @return array
+ * The schema definition for the foreign keys.
+ */
+ protected function getFieldForeignKeys($field_name, array $field_schema, array $column_mapping) {
+ $foreign_keys = array();
+
+ foreach ($field_schema['foreign keys'] as $specifier => $specification) {
+ // To avoid clashes with entity-level foreign keys we use
+ // "{$entity_type_id}_field__" as a prefix instead of just
+ // "{$entity_type_id}__". We additionally namespace the specifier by the
+ // field name to avoid clashes when multiple fields of the same type are
+ // added to an entity type.
+ $entity_type_id = $this->entityType->id();
+ $real_specifier = "{$entity_type_id}_field__{$field_name}__{$specifier}";
+ $foreign_keys[$real_specifier]['table'] = $specification['table'];
+ foreach ($specification['columns'] as $column => $referenced) {
+ $foreign_keys[$real_specifier]['columns'][$column_mapping[$column]] = $referenced;
+ }
+ }
+
+ return $foreign_keys;
+ }
+
+ /**
+ * 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',
+ 'size' => 'tiny',
+ '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() {
+ $entity_type_id = $this->entityType->id();
+
+ $schema = array(
+ 'description' => "The base table for $entity_type_id entities.",
+ 'primary key' => array($this->entityType->getKey('id')),
+ 'indexes' => array(),
+ 'foreign keys' => array(),
+ );
+
+ if ($this->entityType->hasKey('uuid')) {
+ $uuid_key = $this->entityType->getKey('uuid');
+ $schema['unique keys'] = array(
+ $this->getEntityIndexName($uuid_key) => array($uuid_key),
+ );
+ }
+
+ if ($this->entityType->hasKey('revision')) {
+ $revision_key = $this->entityType->getKey('revision');
+ $key_name = $this->getEntityIndexName($revision_key);
+ $schema['unique keys'][$key_name] = array($revision_key);
+ $schema['foreign keys'][$entity_type_id . '__revision'] = array(
+ 'table' => $this->storage->getRevisionTable(),
+ 'columns' => array($revision_key => $revision_key),
+ );
+ }
+
+ return $schema;
+ }
+
+ /**
+ * Initializes common information for a revision table.
+ *
+ * @return array
+ * A partial schema array for the revision table.
+ */
+ protected function initializeRevisionTable() {
+ $entity_type_id = $this->entityType->id();
+ $id_key = $this->entityType->getKey('id');
+ $revision_key = $this->entityType->getKey('revision');
+
+ $schema = array(
+ 'description' => "The revision table for $entity_type_id entities.",
+ 'primary key' => array($revision_key),
+ 'indexes' => array(),
+ 'foreign keys' => array(
+ $entity_type_id . '__revisioned' => array(
+ 'table' => $this->storage->getBaseTable(),
+ 'columns' => array($id_key => $id_key),
+ ),
+ ),
+ );
+
+ $schema['indexes'][$this->getEntityIndexName($id_key)] = array($id_key);
+
+ return $schema;
+ }
+
+ /**
+ * Initializes common information for a data table.
+ *
+ * @return array
+ * A partial schema array for the data table.
+ */
+ protected function initializeDataTable() {
+ $entity_type_id = $this->entityType->id();
+ $id_key = $this->entityType->getKey('id');
+
+ $schema = array(
+ 'description' => "The data table for $entity_type_id entities.",
+ // @todo Use the language entity key when https://drupal.org/node/2143729
+ // is in.
+ 'primary key' => array($id_key, 'langcode'),
+ 'indexes' => array(),
+ 'foreign keys' => array(
+ $entity_type_id => array(
+ 'table' => $this->storage->getBaseTable(),
+ 'columns' => array($id_key => $id_key),
+ ),
+ ),
+ );
+
+ if ($this->entityType->hasKey('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() {
+ $entity_type_id = $this->entityType->id();
+ $id_key = $this->entityType->getKey('id');
+ $revision_key = $this->entityType->getKey('revision');
+
+ $schema = array(
+ 'description' => "The revision data table for $entity_type_id entities.",
+ // @todo Use the language entity key when https://drupal.org/node/2143729
+ // is in.
+ 'primary key' => array($revision_key, 'langcode'),
+ 'indexes' => array(),
+ 'foreign keys' => array(
+ $entity_type_id => array(
+ 'table' => $this->storage->getBaseTable(),
+ 'columns' => array($id_key => $id_key),
+ ),
+ $entity_type_id . '__revision' => array(
+ 'table' => $this->storage->getRevisionTable(),
+ 'columns' => array($revision_key => $revision_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 array $schema
+ * The table schema, passed by reference.
+ * @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..01cfd0c
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandlerInterface.php
@@ -0,0 +1,21 @@
+storageDefinitions = $storage_definitions;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTableNames() {
+ return array_unique(array_merge(array_keys($this->fieldNames), array_keys($this->extraColumns)));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAllColumns($table_name) {
+ if (!isset($this->allColumns[$table_name])) {
+ $this->allColumns[$table_name] = array();
+
+ foreach ($this->getFieldNames($table_name) as $field_name) {
+ $this->allColumns[$table_name] = array_merge($this->allColumns[$table_name], array_values($this->getColumnNames($field_name)));
+ }
+
+ $this->allColumns[$table_name] = array_merge($this->allColumns[$table_name], $this->getExtraColumns($table_name));
+ }
+ return $this->allColumns[$table_name];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFieldNames($table_name) {
+ if (isset($this->fieldNames[$table_name])) {
+ return $this->fieldNames[$table_name];
+ }
+ return array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getColumnNames($field_name) {
+ if (!isset($this->columnMapping[$field_name])) {
+ $column_names = array_keys($this->storageDefinitions[$field_name]->getColumns());
+ if (count($column_names) == 1) {
+ $this->columnMapping[$field_name] = array(reset($column_names) => $field_name);
+ }
+ else {
+ $this->columnMapping[$field_name] = array();
+ foreach ($column_names as $column_name) {
+ $this->columnMapping[$field_name][$column_name] = $field_name . '__' . $column_name;
+ }
+ }
+ }
+ return $this->columnMapping[$field_name];
+ }
+
+ /**
+ * Adds field columns for a table to the table mapping.
+ *
+ * @param string $table_name
+ * The name of the table to add the field column for.
+ * @param string[] $field_names
+ * A list of field names to add the columns for.
+ *
+ * @return $this
+ */
+ public function setFieldNames($table_name, array $field_names) {
+ $this->fieldNames[$table_name] = $field_names;
+ // Force the re-computation of the column list.
+ unset($this->allColumns[$table_name]);
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getExtraColumns($table_name) {
+ if (isset($this->extraColumns[$table_name])) {
+ return $this->extraColumns[$table_name];
+ }
+ return array();
+ }
+
+ /**
+ * Adds a extra columns for a table to the table mapping.
+ *
+ * @param string $table_name
+ * The name of table to add the extra columns for.
+ * @param string[] $column_names
+ * The list of column names.
+ *
+ * @return $this
+ */
+ public function setExtraColumns($table_name, array $column_names) {
+ $this->extraColumns[$table_name] = $column_names;
+ // Force the re-computation of the column list.
+ unset($this->allColumns[$table_name]);
+ return $this;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlStorageInterface.php b/core/lib/Drupal/Core/Entity/Sql/SqlStorageInterface.php
new file mode 100644
index 0000000..8a0bc1b
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlStorageInterface.php
@@ -0,0 +1,24 @@
+schema();
+ foreach ($entity_manager->getDefinitions() as $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);
+ }
+ }
+ }
+ }
+ }
+
// Record the fact that it was installed.
$modules_installed[] = $module;
@@ -891,6 +908,22 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) {
// Remove all configuration belonging to the module.
\Drupal::service('config.manager')->uninstall('module', $module);
+ // Remove any entity schemas belonging to the module.
+ $entity_manager = \Drupal::entityManager();
+ $schema = \Drupal::database()->schema();
+ foreach ($entity_manager->getDefinitions() as $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->dropTable($table_name);
+ }
+ }
+ }
+ }
+ }
+
// Remove the schema.
drupal_uninstall_schema($module);
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
index 1697c16..a5d37b8 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
@@ -7,6 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
+use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\TypedData\EntityDataDefinition;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
@@ -114,7 +115,9 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
'target_id' => array(
'description' => 'The ID of the target entity.',
'type' => 'varchar',
- 'length' => '255',
+ // If the target entities act as bundles for another entity type, their
+ // IDs should not exceed the maximum length for bundles.
+ 'length' => $target_type_info->getBundleOf() ? EntityTypeInterface::BUNDLE_MAX_LENGTH : 255,
),
);
}
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
index 2bafa8b..2d3fa30 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
@@ -41,6 +41,9 @@ public static function defaultInstanceSettings() {
'max' => '',
'prefix' => '',
'suffix' => '',
+ // Valid size property values include: 'tiny', 'small', 'medium', 'normal'
+ // and 'big'.
+ 'size' => 'normal',
) + parent::defaultInstanceSettings();
}
@@ -91,6 +94,9 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
'not null' => FALSE,
// Expose the 'unsigned' setting in the field item schema.
'unsigned' => $field_definition->getSetting('unsigned'),
+ // Expose the 'size' setting in the field item schema. For instance,
+ // supply 'big' as a value to produce a 'bigint' type.
+ 'size' => $field_definition->getSetting('size'),
),
),
);
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
index 693e4ce..dbef995 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
@@ -52,7 +52,8 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
return array(
'columns' => array(
'value' => array(
- 'type' => 'text',
+ 'type' => 'varchar',
+ 'length' => (int) $field_definition->getSetting('max_length'),
'not null' => TRUE,
),
),
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 24430b9..4222152 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php
@@ -153,7 +153,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setLabel(t('URL'))
->setDescription(t('The fully-qualified URL of the feed.'))
->setRequired(TRUE)
- ->setSetting('max_length', NULL)
->setDisplayOptions('form', array(
'type' => 'uri',
'weight' => -3,
@@ -176,11 +175,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 bb9e0aa..162bb19 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Item.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Item.php
@@ -71,8 +71,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.'));
@@ -81,7 +80,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/FeedStorage.php b/core/modules/aggregator/lib/Drupal/aggregator/FeedStorage.php
index 5824240..da784f5 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/FeedStorage.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/FeedStorage.php
@@ -21,6 +21,29 @@ class FeedStorage extends ContentEntityDatabaseStorage implements FeedStorageInt
/**
* {@inheritdoc}
*/
+ public function getSchema() {
+ $schema = parent::getSchema();
+
+ // Marking the respective fields as NOT NULL makes the indexes more
+ // performant.
+ $schema['aggregator_feed']['fields']['url']['not null'] = TRUE;
+ $schema['aggregator_feed']['fields']['queued']['not null'] = TRUE;
+ $schema['aggregator_feed']['fields']['title']['not null'] = TRUE;
+
+ $schema['aggregator_feed']['indexes'] += array(
+ 'aggregator_feed__url' => array(array('url', 255)),
+ 'aggregator_feed__queued' => array('queued'),
+ );
+ $schema['aggregator_feed']['unique keys'] += array(
+ 'aggregator_feed__title' => array('title'),
+ );
+
+ return $schema;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function getFeedDuplicates(FeedInterface $feed) {
$query = \Drupal::entityQuery('aggregator_feed');
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/ItemStorage.php b/core/modules/aggregator/lib/Drupal/aggregator/ItemStorage.php
index bedea41..f2b4aa6 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/ItemStorage.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/ItemStorage.php
@@ -22,6 +22,29 @@ class ItemStorage extends ContentEntityDatabaseStorage implements ItemStorageInt
/**
* {@inheritdoc}
*/
+ public function getSchema() {
+ $schema = parent::getSchema();
+
+ // Marking the respective fields as NOT NULL makes the indexes more
+ // performant.
+ $schema['aggregator_item']['fields']['timestamp']['not null'] = TRUE;
+
+ $schema['aggregator_item']['indexes'] += array(
+ 'aggregator_item__timestamp' => array('timestamp'),
+ );
+ $schema['aggregator_item']['foreign keys'] += array(
+ 'aggregator_item__aggregator_feed' => array(
+ 'table' => 'aggregator_feed',
+ 'columns' => array('fid' => 'fid'),
+ ),
+ );
+
+ return $schema;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function getItemCount(FeedInterface $feed) {
$query = \Drupal::entityQuery('aggregator_item')
->condition('fid', $feed->id())
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
deleted file mode 100644
index 6f85a6a..0000000
--- a/core/modules/block/custom_block/custom_block.install
+++ /dev/null
@@ -1,125 +0,0 @@
- '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;
-}
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorage.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorage.php
new file mode 100644
index 0000000..32b9db0
--- /dev/null
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorage.php
@@ -0,0 +1,33 @@
+ array('info'),
+ );
+
+ return $schema;
+ }
+
+}
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
index 71913b7..585026e 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
@@ -21,6 +21,7 @@
* label = @Translation("Custom Block"),
* bundle_label = @Translation("Custom Block type"),
* controllers = {
+ * "storage" = "Drupal\custom_block\CustomBlockStorage",
* "access" = "Drupal\custom_block\CustomBlockAccessController",
* "list_builder" = "Drupal\custom_block\CustomBlockListBuilder",
* "view_builder" = "Drupal\custom_block\CustomBlockViewBuilder",
@@ -186,7 +187,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setSetting('target_type', 'custom_block_type')
->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH);
- $fields['log'] = FieldDefinition::create('string')
+ $fields['log'] = FieldDefinition::create('string_long')
->setLabel(t('Revision log message'))
->setDescription(t('The revision log message.'))
->setRevisionable(TRUE);
diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install
index 40fb1a0..d2bae78 100644
--- a/core/modules/comment/comment.install
+++ b/core/modules/comment/comment.install
@@ -28,157 +28,10 @@ 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(
diff --git a/core/modules/comment/lib/Drupal/comment/CommentStorage.php b/core/modules/comment/lib/Drupal/comment/CommentStorage.php
index 88a1bd4..0246f73 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentStorage.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentStorage.php
@@ -127,4 +127,51 @@ public function getChildCids(array $comments) {
->fetchCol();
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getSchema() {
+ $schema = parent::getSchema();
+
+ // Marking the respective fields as NOT NULL makes the indexes more
+ // performant.
+ $schema['comment']['fields']['pid']['not null'] = TRUE;
+ $schema['comment']['fields']['status']['not null'] = TRUE;
+ $schema['comment']['fields']['entity_id']['not null'] = TRUE;
+ $schema['comment']['fields']['entity_type']['not null'] = TRUE;
+ $schema['comment']['fields']['field_id']['not null'] = TRUE;
+ $schema['comment']['fields']['created']['not null'] = TRUE;
+ $schema['comment']['fields']['thread']['not null'] = TRUE;
+
+ unset($schema['comment']['indexes']['field__pid']);
+ unset($schema['comment']['indexes']['field__entity_id']);
+ $schema['comment']['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__entity_langcode' => array(
+ 'entity_id',
+ array('entity_type', 32),
+ array('field_id', 32),
+ 'langcode',
+ ),
+ 'comment__created' => array('created'),
+ );
+ $schema['comment']['foreign keys'] += array(
+ 'comment__author' => array(
+ 'table' => 'users',
+ 'columns' => array('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 de10346..9b4707f 100644
--- a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php
+++ b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php
@@ -273,7 +273,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/CommentDefaultFormatterCacheTagsTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentDefaultFormatterCacheTagsTest.php
index 3fe2a3f..ff8c05a 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentDefaultFormatterCacheTagsTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentDefaultFormatterCacheTagsTest.php
@@ -47,7 +47,8 @@ public function setUp() {
$current_user->setAccount($this->createUser(array(), array('access comments')));
// Install tables and config needed to render comments.
- $this->installSchema('comment', array('comment', 'comment_entity_statistics'));
+ $this->installSchema('comment', array('comment_entity_statistics'));
+ $this->installEntitySchema('comment');
$this->installConfig(array('system', 'filter'));
// Comment rendering generates links, so build the router.
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentValidationTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentValidationTest.php
index 03a0e82..b56ed6b 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentValidationTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentValidationTest.php
@@ -38,7 +38,8 @@ 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');
+ $this->installEntitySchema('comment');
$this->installSchema('comment', array('comment_entity_statistics'));
}
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportRecreateTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportRecreateTest.php
index 3ce6b12..9afec8c 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', 'user', 'node');
public static function getInfo() {
return array(
@@ -42,7 +42,7 @@ public static function getInfo() {
public function setUp() {
parent::setUp();
- $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/config/lib/Drupal/config/Tests/ConfigImportRenameValidationTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportRenameValidationTest.php
index fbffae7..7e8024a 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportRenameValidationTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportRenameValidationTest.php
@@ -32,7 +32,7 @@ class ConfigImportRenameValidationTest extends DrupalUnitTestBase {
*
* @var array
*/
- public static $modules = array('system', 'node', 'field', 'text', 'entity', 'config_test');
+ public static $modules = array('system', 'user', 'node', 'field', 'text', 'entity', 'config_test');
/**
* {@inheritdoc}
@@ -51,7 +51,8 @@ public static function getInfo() {
public function setUp() {
parent::setUp();
- $this->installSchema('node', 'node');
+ $this->installEntitySchema('user');
+ $this->installEntitySchema('node');
// Set up the ConfigImporter object for testing.
$storage_comparer = new StorageComparer(
diff --git a/core/modules/editor/lib/Drupal/editor/Tests/EditorFileUsageTest.php b/core/modules/editor/lib/Drupal/editor/Tests/EditorFileUsageTest.php
index 3be50ec..184d849 100644
--- a/core/modules/editor/lib/Drupal/editor/Tests/EditorFileUsageTest.php
+++ b/core/modules/editor/lib/Drupal/editor/Tests/EditorFileUsageTest.php
@@ -31,8 +31,10 @@ 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');
+ $this->installSchema('node', array('node_access'));
+ $this->installSchema('file', array('file_usage'));
// Add text formats.
$filtered_html_format = entity_create('filter_format', array(
diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
index 77745f2..250f5e0 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
@@ -268,7 +268,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/Tests/EntityReferenceFieldTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldTest.php
index bde0b2c..b3905bc 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
@@ -76,7 +76,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/lib/Drupal/field/Tests/FieldAttachOtherTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
index 39f6e28..ee94256 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 0bf7732..22ba695 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
@@ -37,7 +37,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');
}
/**
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
index c97a481..317e8cb 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
@@ -23,6 +23,15 @@ public static function getInfo() {
}
/**
+ * {@inheritdoc}
+ */
+ public function setUp() {
+ parent::setUp();
+
+ $this->installSchema('user', array('users_data'));
+ }
+
+ /**
* Test that field types and field definitions are correctly cached.
*/
function testFieldInfo() {
@@ -180,7 +189,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 5717a23..457a09f 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'));
- $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/file/file.install b/core/modules/file/file.install
index 6bbed79..8d13c03 100644
--- a/core/modules/file/file.install
+++ b/core/modules/file/file.install
@@ -9,105 +9,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 system.file.temporary_maximum_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(
diff --git a/core/modules/file/lib/Drupal/file/Entity/File.php b/core/modules/file/lib/Drupal/file/Entity/File.php
index 39f27d6..8564127 100644
--- a/core/modules/file/lib/Drupal/file/Entity/File.php
+++ b/core/modules/file/lib/Drupal/file/Entity/File.php
@@ -258,11 +258,12 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['filesize'] = FieldDefinition::create('integer')
->setLabel(t('File size'))
->setDescription(t('The size of the file in bytes.'))
- ->setSetting('unsigned', TRUE);
+ ->setSetting('unsigned', TRUE)
+ ->setSetting('size', 'big');
- $fields['status'] = FieldDefinition::create('integer')
+ $fields['status'] = FieldDefinition::create('boolean')
->setLabel(t('Status'))
- ->setDescription(t('The status of the file, temporary (0) and permanent (1).'));
+ ->setDescription(t('The status of the file, temporary (FALSE) and permanent (TRUE).'));
$fields['created'] = FieldDefinition::create('created')
->setLabel(t('Created'))
diff --git a/core/modules/file/lib/Drupal/file/FileStorage.php b/core/modules/file/lib/Drupal/file/FileStorage.php
index c6f2ccc..9637234 100644
--- a/core/modules/file/lib/Drupal/file/FileStorage.php
+++ b/core/modules/file/lib/Drupal/file/FileStorage.php
@@ -38,4 +38,33 @@ public function retrieveTemporaryFiles() {
':changed' => REQUEST_TIME - DRUPAL_MAXIMUM_TEMP_FILE_AGE
));
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSchema() {
+ $schema = parent::getSchema();
+
+ // Marking the respective fields as NOT NULL makes the indexes more
+ // performant.
+ $schema['file_managed']['fields']['status']['not null'] = TRUE;
+ $schema['file_managed']['fields']['changed']['not null'] = TRUE;
+ $schema['file_managed']['fields']['uri']['not null'] = TRUE;
+
+ // @todo There should be a 'binary' field type or setting.
+ $schema['file_managed']['fields']['uri']['binary'] = TRUE;
+ $schema['file_managed']['indexes'] += array(
+ 'file__status' => array('status'),
+ 'file__changed' => array('changed'),
+ );
+ $schema['file_managed']['unique keys'] += array(
+ // FIXME We have an index size of 255, but the max URI length is 2048 so
+ // this might now always work. Should we replace this with a regular
+ // index?
+ 'file__uri' => array(array('uri', 255)),
+ );
+
+ 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..57b908a 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileItemTest.php
@@ -42,8 +42,8 @@ public static function getInfo() {
public function setUp() {
parent::setUp();
- $this->installSchema('file', 'file_managed');
- $this->installSchema('file', 'file_usage');
+ $this->installEntitySchema('file');
+ $this->installSchema('file', array('file_usage'));
entity_create('field_config', array(
'name' => 'file_test',
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileManagedUnitTestBase.php b/core/modules/file/lib/Drupal/file/Tests/FileManagedUnitTestBase.php
index 8c83ac7..503490f 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileManagedUnitTestBase.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileManagedUnitTestBase.php
@@ -29,8 +29,9 @@ 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');
+ $this->installSchema('file', array('file_usage'));
// Make sure that a user with uid 1 exists, self::createFile() relies on
// it.
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/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/hal/lib/Drupal/hal/Tests/EntityTest.php b/core/modules/hal/lib/Drupal/hal/Tests/EntityTest.php
index bfbe6f1..8bc3fed 100644
--- a/core/modules/hal/lib/Drupal/hal/Tests/EntityTest.php
+++ b/core/modules/hal/lib/Drupal/hal/Tests/EntityTest.php
@@ -38,10 +38,10 @@ 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('comment', array('comment', 'comment_entity_statistics'));
- $this->installSchema('user', array('users_roles'));
- $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy'));
+ $this->installSchema('comment', array('comment_entity_statistics'));
+ $this->installEntitySchema('node');
+ $this->installEntitySchema('comment');
+ $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..e17987b 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php
@@ -47,7 +47,8 @@ public static function getInfo() {
public function setUp() {
parent::setUp();
- $this->installSchema('file', array('file_managed', 'file_usage'));
+ $this->installEntitySchema('file');
+ $this->installSchema('file', array('file_usage'));
entity_create('field_config', array(
'name' => 'image_test',
diff --git a/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/d6/MigrateBookTest.php b/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/d6/MigrateBookTest.php
index d29ad95..d7fc9a3 100644
--- a/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/d6/MigrateBookTest.php
+++ b/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/d6/MigrateBookTest.php
@@ -38,6 +38,7 @@ protected function setUp() {
$entity = entity_create('node', array(
'type' => 'story',
'nid' => $i,
+ 'status' => TRUE,
));
$entity->enforceIsNew();
$entity->save();
diff --git a/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/d6/MigrateFieldInstanceTest.php b/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/d6/MigrateFieldInstanceTest.php
index d0c38be..c6ad455 100644
--- a/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/d6/MigrateFieldInstanceTest.php
+++ b/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/d6/MigrateFieldInstanceTest.php
@@ -107,6 +107,7 @@ public function testFieldInstanceSettings() {
'prefix' => 'pref',
'suffix' => 'suf',
'unsigned' => '',
+ 'size' => 'normal',
);
$this->assertEqual($field->getSettings(), $expected);
@@ -139,6 +140,7 @@ public function testFieldInstanceSettings() {
'uri_scheme' => 'public',
'handler' => 'default',
'target_bundle' => NULL,
+ 'max_length' => 255,
);
// This is the only way to compare arrays.
$this->assertFalse(array_diff_assoc($field->getSettings(), $expected));
diff --git a/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/d6/MigrateTaxonomyTermTest.php b/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/d6/MigrateTaxonomyTermTest.php
index a4f5b13..a4027e6 100644
--- a/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/d6/MigrateTaxonomyTermTest.php
+++ b/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/d6/MigrateTaxonomyTermTest.php
@@ -60,11 +60,13 @@ public function testTaxonomyTerms() {
'source_vid' => 1,
'vid' => 'vocabulary_1_i_0_',
'weight' => 0,
+ 'parent' => array(0),
),
'2' => array(
'source_vid' => 2,
'vid' => 'vocabulary_2_i_1_',
'weight' => 3,
+ 'parent' => array(0),
),
'3' => array(
'source_vid' => 2,
@@ -76,6 +78,7 @@ public function testTaxonomyTerms() {
'source_vid' => 3,
'vid' => 'vocabulary_3_i_2_',
'weight' => 6,
+ 'parent' => array(0),
),
'5' => array(
'source_vid' => 3,
@@ -98,8 +101,8 @@ public function testTaxonomyTerms() {
$this->assertIdentical($term->description->value, "description of term {$tid} of vocabulary {$values['source_vid']}");
$this->assertEqual($term->vid->value, $values['vid']);
$this->assertEqual($term->weight->value, $values['weight']);
- if (empty($values['parent'])) {
- $this->assertNull($term->parent->value);
+ if ($values['parent'] === array(0)) {
+ $this->assertEqual($term->parent->value, 0);
}
else {
$parents = array();
diff --git a/core/modules/node/lib/Drupal/node/Entity/Node.php b/core/modules/node/lib/Drupal/node/Entity/Node.php
index 1d21504..7fb33e6 100644
--- a/core/modules/node/lib/Drupal/node/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Entity/Node.php
@@ -65,6 +65,23 @@ class Node extends ContentEntityBase implements NodeInterface {
/**
* {@inheritdoc}
*/
+ 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}
+ */
public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) {
parent::preSaveRevision($storage, $record);
@@ -340,6 +357,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setLabel(t('Type'))
->setDescription(t('The node type.'))
->setSetting('target_type', 'node_type')
+ ->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH)
->setReadOnly(TRUE);
$fields['langcode'] = FieldDefinition::create('language')
@@ -353,10 +371,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setRequired(TRUE)
->setTranslatable(TRUE)
->setRevisionable(TRUE)
- ->setSettings(array(
- 'default_value' => '',
- 'max_length' => 255,
- ))
+ ->setSetting('default_value', '')
+ ->setSetting('max_length', 255)
->setDisplayOptions('view', array(
'label' => 'hidden',
'type' => 'string',
@@ -372,10 +388,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setLabel(t('Author'))
->setDescription(t('The user that is the node author.'))
->setRevisionable(TRUE)
- ->setSettings(array(
- 'target_type' => 'user',
- 'default_value' => 0,
- ))
+ ->setSetting('target_type', 'user')
->setTranslatable(TRUE);
$fields['status'] = FieldDefinition::create('boolean')
@@ -408,7 +421,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)
@@ -417,11 +430,11 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['revision_uid'] = FieldDefinition::create('entity_reference')
->setLabel(t('Revision user ID'))
->setDescription(t('The user ID of the author of the current revision.'))
- ->setSettings(array('target_type' => 'user'))
+ ->setSetting('target_type', 'user')
->setQueryable(FALSE)
->setRevisionable(TRUE);
- $fields['log'] = FieldDefinition::create('string')
+ $fields['log'] = FieldDefinition::create('string_long')
->setLabel(t('Log'))
->setDescription(t('The log entry explaining the changes in this revision.'))
->setRevisionable(TRUE)
@@ -436,10 +449,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 in https://drupal.org/node/2248795
+ 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 307e947..2d9f072 100644
--- a/core/modules/node/lib/Drupal/node/Entity/NodeType.php
+++ b/core/modules/node/lib/Drupal/node/Entity/NodeType.php
@@ -209,4 +209,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/NodeForm.php b/core/modules/node/lib/Drupal/node/NodeForm.php
index 37bb7da..0c5bbc6 100644
--- a/core/modules/node/lib/Drupal/node/NodeForm.php
+++ b/core/modules/node/lib/Drupal/node/NodeForm.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
index 8bb17fd..71831bf 100644
--- a/core/modules/node/lib/Drupal/node/NodeStorage.php
+++ b/core/modules/node/lib/Drupal/node/NodeStorage.php
@@ -2,7 +2,7 @@
/**
* @file
- * Definition of Drupal\node\NodeStorageController.
+ * Contains \Drupal\node\NodeStorage.
*/
namespace Drupal\node;
@@ -59,4 +59,51 @@ public function clearRevisionsLanguage($language) {
->condition('langcode', $language->id)
->execute();
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSchema() {
+ $schema = parent::getSchema();
+
+ // Marking the respective fields as NOT NULL makes the indexes more
+ // performant.
+ $schema['node_field_data']['fields']['changed']['not null'] = TRUE;
+ $schema['node_field_data']['fields']['created']['not null'] = TRUE;
+ $schema['node_field_data']['fields']['default_langcode']['not null'] = TRUE;
+ $schema['node_field_data']['fields']['promote']['not null'] = TRUE;
+ $schema['node_field_data']['fields']['status']['not null'] = TRUE;
+ $schema['node_field_data']['fields']['sticky']['not null'] = TRUE;
+ $schema['node_field_data']['fields']['title']['not null'] = TRUE;
+ $schema['node_field_revision']['fields']['default_langcode']['not null'] = TRUE;
+
+ // @todo Revisit index definitions in https://drupal.org/node/2015277.
+ $schema['node_revision']['indexes'] += array(
+ 'node__langcode' => array('langcode'),
+ );
+ $schema['node_revision']['foreign keys'] += array(
+ 'node__revision_author' => array(
+ 'table' => 'users',
+ 'columns' => array('revision_uid' => 'uid'),
+ ),
+ );
+
+ $schema['node_field_data']['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)),
+ );
+
+ $schema['node_field_revision']['indexes'] += array(
+ 'node__default_langcode' => array('default_langcode'),
+ 'node__langcode' => array('langcode'),
+ );
+
+ return $schema;
+ }
+
}
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 2f6a8db..fadc706 100644
--- a/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportCreateTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/Config/NodeImportCreateTest.php
@@ -27,7 +27,7 @@ class NodeImportCreateTest extends DrupalUnitTestBase {
*/
public function setUp() {
parent::setUp();
- $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/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 b075d93..d0d6d1c 100644
--- a/core/modules/node/node.install
+++ b/core/modules/node/node.install
@@ -39,311 +39,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(
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 71a1f38..d0caacf 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -783,7 +783,10 @@ function node_user_cancel($edit, $account, $method) {
// Anonymize all of the nodes for this old account.
module_load_include('inc', 'node', 'node.admin');
$vids = \Drupal::entityManager()->getStorage('node')->userRevisionIds($account);
- node_mass_update($vids, array('uid' => 0), NULL, TRUE, TRUE);
+ node_mass_update($vids, array(
+ 'uid' => 0,
+ 'revision_uid' => 0,
+ ), NULL, TRUE, TRUE);
break;
}
}
diff --git a/core/modules/quickedit/lib/Drupal/quickedit/Tests/QuickEditTestBase.php b/core/modules/quickedit/lib/Drupal/quickedit/Tests/QuickEditTestBase.php
index c72899b..9fb217c 100644
--- a/core/modules/quickedit/lib/Drupal/quickedit/Tests/QuickEditTestBase.php
+++ b/core/modules/quickedit/lib/Drupal/quickedit/Tests/QuickEditTestBase.php
@@ -27,7 +27,7 @@
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/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 5086bf1..f733014 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/Plugin/rest/resource/EntityResource.php b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php
index 9b3c792..94aeace 100644
--- a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php
@@ -133,6 +133,12 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity
// Overwrite the received properties.
foreach ($entity as $field_name => $field) {
if (isset($entity->{$field_name})) {
+ // It is not possible to set the language to NULL as it is automatically
+ // re-initialized. As it must not be empty, skip it if it is.
+ // @todo: Use the langcode entity key when available.
+ if ($field->isEmpty() && $field_name == 'langcode') {
+ continue;
+ }
if ($field->isEmpty() && !$original_entity->get($field_name)->access('delete')) {
throw new AccessDeniedHttpException(t('Access denied on deleting field @field.', array('@field' => $field_name)));
}
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
index 15ef7f3..7afedfa 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/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/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php
index 5807c66..65325ed 100644
--- a/core/modules/shortcut/src/Entity/Shortcut.php
+++ b/core/modules/shortcut/src/Entity/Shortcut.php
@@ -168,6 +168,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setLabel(t('Shortcut set'))
->setDescription(t('The bundle of the shortcut.'))
->setSetting('target_type', 'shortcut_set')
+ ->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH)
->setRequired(TRUE);
$fields['title'] = FieldDefinition::create('string')
@@ -175,10 +176,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setDescription(t('The name of the shortcut.'))
->setRequired(TRUE)
->setTranslatable(TRUE)
- ->setSettings(array(
- 'default_value' => '',
- 'max_length' => 255,
- ))
+ ->setSetting('default_value', '')
+ ->setSetting('max_length', 255)
->setDisplayOptions('form', array(
'type' => 'string',
'weight' => -10,
@@ -203,10 +202,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/src/Entity/ShortcutSet.php b/core/modules/shortcut/src/Entity/ShortcutSet.php
index 32c1bb6..b1b0fd7 100644
--- a/core/modules/shortcut/src/Entity/ShortcutSet.php
+++ b/core/modules/shortcut/src/Entity/ShortcutSet.php
@@ -63,15 +63,17 @@ class ShortcutSet extends ConfigEntityBase implements ShortcutSetInterface {
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
parent::postSave($storage, $update);
- // Generate menu-compatible set name.
- if (!$update && !$this->getOriginalId()) {
+ if (!$update && !$this->isSyncing()) {
// Save a new shortcut set with links copied from the user's default set.
$default_set = shortcut_default_set();
- foreach ($default_set->getShortcuts() as $shortcut) {
- $shortcut = $shortcut->createDuplicate();
- $shortcut->enforceIsNew();
- $shortcut->shortcut_set->target_id = $this->id();
- $shortcut->save();
+ // This is the default set, do not copy shortcuts.
+ if ($default_set->id() != $this->id()) {
+ foreach ($default_set->getShortcuts() as $shortcut) {
+ $shortcut = $shortcut->createDuplicate();
+ $shortcut->enforceIsNew();
+ $shortcut->shortcut_set->target_id = $this->id();
+ $shortcut->save();
+ }
}
}
}
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
index d6bd64f..d7c34d9 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;
@@ -336,6 +338,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..5c9b464 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');
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', '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,20 +125,9 @@ function testEnableModulesInstallContainer() {
* Tests expected behavior of installSchema().
*/
function testInstallSchema() {
- $module = 'entity_test';
- $table = 'entity_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.");
-
- // Verify that the schema is known to Schema API.
- $schema = drupal_get_schema();
- $this->assertTrue($schema[$table], "'$table' table found in schema.");
- $schema = drupal_get_schema($table);
- $this->assertTrue($schema, "'$table' table schema found.");
-
- // Verify that a unknown table from an enabled module throws an error.
- $table = 'unknown_entity_test_table';
+ // Verify that a table from a unknown module cannot be installed.
+ $module = 'database_test';
+ $table = 'test';
try {
$this->installSchema($module, $table);
$this->fail('Exception for non-retrievable schema found.');
@@ -149,9 +139,19 @@ function testInstallSchema() {
$schema = drupal_get_schema($table);
$this->assertFalse($schema, "'$table' table schema not found.");
- // Verify that a table from a unknown module cannot be installed.
- $module = 'database_test';
- $table = 'test';
+ // Verify that the same table can be installed after enabling the module.
+ $this->enableModules(array($module));
+ $this->installSchema($module, $table);
+ $this->assertTrue(db_table_exists($table), "'$table' database table found.");
+
+ // Verify that the schema is known to Schema API.
+ $schema = drupal_get_schema();
+ $this->assertTrue($schema[$table], "'$table' table found in schema.");
+ $schema = drupal_get_schema($table);
+ $this->assertTrue($schema, "'$table' table schema found.");
+
+ // Verify that a unknown table from an enabled module throws an error.
+ $table = 'unknown_entity_test_table';
try {
$this->installSchema($module, $table);
$this->fail('Exception for non-retrievable schema found.');
@@ -162,13 +162,6 @@ function testInstallSchema() {
$this->assertFalse(db_table_exists($table), "'$table' database table not found.");
$schema = drupal_get_schema($table);
$this->assertFalse($schema, "'$table' table schema not found.");
-
- // Verify that the same table can be installed after enabling the module.
- $this->enableModules(array($module));
- $this->installSchema($module, $table);
- $this->assertTrue(db_table_exists($table), "'$table' database table found.");
- $schema = drupal_get_schema($table);
- $this->assertTrue($schema, "'$table' table schema found.");
}
/**
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 7e2f651..70fc663 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 490750e..2bc4464 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..fa701a7 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,14 @@ public static function getInfo() {
public function setUp() {
parent::setUp();
+
+ $this->installEntitySchema('node');
+ $this->installEntitySchema('comment');
+
$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->installSchema('file', array('file_usage'));
+ $this->installSchema('node', array('node_access'));
+ $this->installSchema('comment', array('comment_entity_statistics'));
}
/**
@@ -219,7 +224,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 +352,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 +421,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 a838d2f..f532b1f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
@@ -41,18 +41,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 ada2fbd..c76fc97 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 8ea9daa..24a4de9 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 8549d99..f10a315 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 6a8badd..0c0c753 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 9930262..a8abda5 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
@@ -46,17 +46,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 21f6c54..b30cbb1 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
@@ -46,23 +46,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/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
index 510524a..8b6996a 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
@@ -11,6 +11,7 @@
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinition;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\taxonomy\TermInterface;
@@ -164,6 +165,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['parent'] = FieldDefinition::create('integer')
->setLabel(t('Term Parents'))
->setDescription(t('The parents of this term.'))
+ ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)
// Save new terms with no parents by default.
->setSetting('default_value', 0)
->setSetting('unsigned', TRUE)
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorage.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorage.php
index b7b9201..381b0ab 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorage.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermStorage.php
@@ -8,6 +8,7 @@
namespace Drupal\taxonomy;
use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Entity\ContentEntityDatabaseStorage;
@@ -151,4 +152,103 @@ public function resetWeights($vid) {
->execute();
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getSchema() {
+ $schema = parent::getSchema();
+
+ // Marking the respective fields as NOT NULL makes the indexes more
+ // performant.
+ $schema['taxonomy_term_data']['fields']['weight']['not null'] = TRUE;
+ $schema['taxonomy_term_data']['fields']['name']['not null'] = TRUE;
+
+ unset($schema['taxonomy_term_data']['indexes']['field__vid']);
+ unset($schema['taxonomy_term_data']['indexes']['field__description__format']);
+ $schema['taxonomy_term_data']['indexes'] += array(
+ 'taxonomy_term__tree' => array('vid', 'weight', 'name'),
+ 'taxonomy_term__vid_name' => array('vid', 'name'),
+ 'taxonomy_term__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,
+ ),
+ ),
+ 'primary key' => array('nid', 'tid'),
+ 'indexes' => array(
+ 'term_node' => array('tid', 'sticky', 'created'),
+ ),
+ '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 ae5dc7f..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,
- ),
- ),
- 'primary key' => array('nid', 'tid'),
- 'indexes' => array(
- 'term_node' => array('tid', 'sticky', 'created'),
- ),
- '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/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/user/lib/Drupal/user/AccountForm.php b/core/modules/user/lib/Drupal/user/AccountForm.php
index 42a2292..5ac1c00 100644
--- a/core/modules/user/lib/Drupal/user/AccountForm.php
+++ b/core/modules/user/lib/Drupal/user/AccountForm.php
@@ -378,9 +378,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 in https://drupal.org/node/2227381.
+ $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 25eb641..6c0fb85 100644
--- a/core/modules/user/lib/Drupal/user/Entity/User.php
+++ b/core/modules/user/lib/Drupal/user/Entity/User.php
@@ -18,6 +18,9 @@
/**
* Defines the user entity class.
*
+ * The base table name here is plural, despite Drupal table naming standards,
+ * because "user" is a reserved word in many databases.
+ *
* @ContentEntityType(
* id = "user",
* label = @Translation("User"),
@@ -500,8 +503,11 @@ 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 As the status has access implications users should be created as
+ // blocked by default and activated explicitly if needed. See
+ // https://drupal.org/node/2248969.
+ ->setSetting('default_value', TRUE);
$fields['created'] = FieldDefinition::create('created')
->setLabel(t('Created'))
@@ -525,6 +531,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 d8d384d..f71d1e6 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php
@@ -34,13 +34,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');
@@ -86,8 +95,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');
@@ -100,8 +113,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 7014a03..9fdb8db 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_install();
}
@@ -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 499877e..06d42b9 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 d5d91ca..c2574b6 100644
--- a/core/modules/user/lib/Drupal/user/UserStorage.php
+++ b/core/modules/user/lib/Drupal/user/UserStorage.php
@@ -11,6 +11,7 @@
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 Symfony\Component\DependencyInjection\ContainerInterface;
@@ -47,15 +48,13 @@ class UserStorage extends ContentEntityDatabaseStorage implements UserStorageInt
* The database connection to be used.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
- * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
- * The UUID Service.
* @param \Drupal\Core\Password\PasswordInterface $password
* The password hashing service.
* @param \Drupal\user\UserDataInterface $user_data
* The user data service.
*/
- public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) {
- parent::__construct($entity_type, $database, $entity_manager, $uuid_service);
+ public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, PasswordInterface $password, UserDataInterface $user_data) {
+ parent::__construct($entity_type, $database, $entity_manager);
$this->password = $password;
$this->userData = $user_data;
@@ -69,7 +68,6 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
$entity_type,
$container->get('database'),
$container->get('entity.manager'),
- $container->get('uuid'),
$container->get('password'),
$container->get('user.data')
);
@@ -154,4 +152,60 @@ public function updateLastLoginTimestamp(UserInterface $account) {
->execute();
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getSchema() {
+ $schema = parent::getSchema();
+
+ // Marking the respective fields as NOT NULL makes the indexes more
+ // performant.
+ $schema['users']['fields']['access']['not null'] = TRUE;
+ $schema['users']['fields']['created']['not null'] = TRUE;
+ $schema['users']['fields']['mail']['not null'] = TRUE;
+ $schema['users']['fields']['name']['not null'] = TRUE;
+
+ // The "users" table does not use serial identifiers.
+ $schema['users']['fields']['uid']['type'] = 'int';
+ $schema['users']['indexes'] += array(
+ 'user__access' => array('access'),
+ 'user__created' => array('created'),
+ 'user__mail' => array('mail'),
+ );
+ $schema['users']['unique keys'] += array(
+ 'user__name' => array('name'),
+ );
+
+ $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 086a5a0..e0658c5 100644
--- a/core/modules/user/user.install
+++ b/core/modules/user/user.install
@@ -9,130 +9,6 @@
* Implements hook_schema().
*/
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(
@@ -181,35 +57,6 @@ function user_schema() {
),
);
- $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/views/lib/Drupal/views/Entity/View.php b/core/modules/views/lib/Drupal/views/Entity/View.php
index c3e19aa..8193ff5 100644
--- a/core/modules/views/lib/Drupal/views/Entity/View.php
+++ b/core/modules/views/lib/Drupal/views/Entity/View.php
@@ -275,7 +275,10 @@ public function calculateDependencies() {
// Ensure that the view is dependent on the module that provides the schema
// for the base table.
$schema = $this->drupalGetSchema($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']);
}
@@ -283,7 +286,6 @@ public function calculateDependencies() {
foreach (Views::getHandlerTypes() as $type) {
$handler_types[] = $type['plural'];
}
-
foreach ($this->get('display') as $display) {
// Collect all dependencies of all handlers.
foreach ($handler_types as $handler_type) {
@@ -296,10 +298,10 @@ public function calculateDependencies() {
// Add the additional dependencies from the handler configuration.
if (!empty($handler['dependencies'])) {
$this->addDependencies($handler['dependencies']);
- }
}
}
}
+ }
// Collect all dependencies of plugins.
foreach (Views::getPluginTypes('plugin') as $plugin_type) {
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
index 175e7f4..5b51afd 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
@@ -1425,7 +1425,6 @@ function execute(ViewExecutable $view) {
$result = $query->execute();
$result->setFetchMode(\PDO::FETCH_CLASS, 'Drupal\views\ResultRow');
-
$view->result = iterator_to_array($result);
$view->pager->postExecute($view->result);
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 869ad88..35c8c61 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,9 @@ 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->installSchema('node', array('node_access'));
$this->installConfig(array('node', 'language'));
// The node.view route must exist when nodes are rendered.
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..259e0db 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php
@@ -84,10 +84,12 @@ 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->installSchema('comment', array('comment_entity_statistics'));
$this->installConfig(array('field'));
+
entity_create('node_type', array(
'type' => 'page',
'name' => 'Page',
diff --git a/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php
index 6e60480..cc77d6d 100644
--- a/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php
@@ -23,6 +23,34 @@
class ContentEntityDatabaseStorageTest extends UnitTestCase {
/**
+ * The content entity database storage used in this test.
+ *
+ * @var \Drupal\Core\Entity\ContentEntityDatabaseStorage|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $entityStorage;
+
+ /**
+ * The mocked entity type used in this test.
+ *
+ * @var \Drupal\Core\Entity\ContentEntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $entityType;
+
+ /**
+ * An array of field definitions used for this test, keyed by field name.
+ *
+ * @var \Drupal\Core\Field\FieldDefinition[]|\PHPUnit_Framework_MockObject_MockObject[]
+ */
+ protected $fieldDefinitions = array();
+
+ /**
+ * The mocked entity manager used in this test.
+ *
+ * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $entityManager;
+
+ /**
* {@inheritdoc}
*/
public static function getInfo() {
@@ -34,27 +62,875 @@ public static function getInfo() {
}
/**
+ * {@inheritdoc}
+ */
+ public function setUp() {
+ $this->entityType = $this->getMock('Drupal\Core\Entity\ContentEntityTypeInterface');
+ $this->entityType->expects($this->any())
+ ->method('id')
+ ->will($this->returnValue('entity_test'));
+
+ $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+ }
+
+ /**
+ * Tests ContentEntityDatabaseStorage::getBaseTable().
+ *
+ * @param string $base_table
+ * The base table to be returned by the mocked entity type.
+ * @param string $expected
+ * The expected return value of
+ * ContentEntityDatabaseStorage::getBaseTable().
+ *
+ * @covers ::__construct()
+ * @covers ::getBaseTable()
+ *
+ * @dataProvider providerTestGetBaseTable
+ */
+ public function testGetBaseTable($base_table, $expected) {
+ $this->entityType->expects($this->once())
+ ->method('getBaseTable')
+ ->will($this->returnValue('entity_test'));
+
+ $this->setUpEntityStorage();
+
+ $this->assertSame($expected, $this->entityStorage->getBaseTable());
+ }
+
+ /**
+ * Provides test data for testGetBaseTable().
+ *
+ * @return array[]
+ * An nested array where each inner array has the base table to be returned
+ * by the mocked entity type as the first value and the expected return
+ * value of ContentEntityDatabaseStorage::getBaseTable() as the second
+ * value.
+ */
+ public function providerTestGetBaseTable() {
+ return array(
+ // Test that the entity type's base table is used, if provided.
+ array('entity_test', 'entity_test'),
+ // Test that the storage falls back to the entity type ID.
+ array(NULL, 'entity_test'),
+ );
+ }
+
+ /**
+ * Tests ContentEntityDatabaseStorage::getRevisionTable().
+ *
+ * @param string $revision_table
+ * The revision table to be returned by the mocked entity type.
+ * @param string $expected
+ * The expected return value of
+ * ContentEntityDatabaseStorage::getRevisionTable().
+ *
+ * @cover ::__construct()
+ * @covers ::getRevisionTable()
+ *
+ * @dataProvider providerTestGetRevisionTable
+ */
+ public function testGetRevisionTable($revision_table, $expected) {
+ $this->entityType->expects($this->once())
+ ->method('isRevisionable')
+ ->will($this->returnValue(TRUE));
+ $this->entityType->expects($this->once())
+ ->method('getRevisionTable')
+ ->will($this->returnValue($revision_table));
+
+ $this->setUpEntityStorage();
+
+ $this->assertSame($expected, $this->entityStorage->getRevisionTable());
+ }
+
+ /**
+ * Provides test data for testGetRevisionTable().
+ *
+ * @return array[]
+ * An nested array where each inner array has the revision table to be
+ * returned by the mocked entity type as the first value and the expected
+ * return value of ContentEntityDatabaseStorage::getRevisionTable() as the
+ * second value.
+ */
+ public function providerTestGetRevisionTable() {
+ return array(
+ // Test that the entity type's revision table is used, if provided.
+ array('entity_test_revision', 'entity_test_revision'),
+ // Test that the storage falls back to the entity type ID with a
+ // '_revision' suffix.
+ array(NULL, 'entity_test_revision'),
+ );
+ }
+
+ /**
+ * Tests ContentEntityDatabaseStorage::getDataTable().
+ *
+ * @cover ::__construct()
+ * @covers ::getDataTable()
+ */
+ public function testGetDataTable() {
+ $this->entityType->expects($this->once())
+ ->method('isTranslatable')
+ ->will($this->returnValue(TRUE));
+ $this->entityType->expects($this->exactly(2))
+ ->method('getDataTable')
+ ->will($this->returnValue('entity_test_field_data'));
+
+ $this->setUpEntityStorage();
+
+ $this->assertSame('entity_test_field_data', $this->entityStorage->getDataTable());
+ }
+
+ /**
+ * Tests ContentEntityDatabaseStorage::getRevisionDataTable().
+ *
+ * @param string $revision_data_table
+ * The revision data table to be returned by the mocked entity type.
+ * @param string $expected
+ * The expected return value of
+ * ContentEntityDatabaseStorage::getRevisionDataTable().
+ *
+ * @cover ::__construct()
+ * @covers ::getRevisionDataTable()
+ *
+ * @dataProvider providerTestGetRevisionDataTable
+ */
+ public function testGetRevisionDataTable($revision_data_table, $expected) {
+ $this->entityType->expects($this->once())
+ ->method('isRevisionable')
+ ->will($this->returnValue(TRUE));
+ $this->entityType->expects($this->once())
+ ->method('isTranslatable')
+ ->will($this->returnValue(TRUE));
+ $this->entityType->expects($this->exactly(2))
+ ->method('getDataTable')
+ ->will($this->returnValue('entity_test_field_data'));
+ $this->entityType->expects($this->once())
+ ->method('getRevisionDataTable')
+ ->will($this->returnValue($revision_data_table));
+
+ $this->setUpEntityStorage();
+
+ $actual = $this->entityStorage->getRevisionDataTable();
+ $this->assertSame($expected, $actual);
+ }
+
+ /**
+ * Provides test data for testGetRevisionDataTable().
+ *
+ * @return array[]
+ * An nested array where each inner array has the revision data table to be
+ * returned by the mocked entity type as the first value and the expected
+ * return value of ContentEntityDatabaseStorage::getRevisionDataTable() as
+ * the second value.
+ */
+ public function providerTestGetRevisionDataTable() {
+ return array(
+ // Test that the entity type's revision data table is used, if provided.
+ array('entity_test_field_revision', 'entity_test_field_revision'),
+ // Test that the storage falls back to the entity type ID with a
+ // '_field_revision' suffix.
+ array(NULL, 'entity_test_field_revision'),
+ );
+ }
+
+ /**
+ * Tests ContentEntityDatabaseStorage::getSchema().
+ *
+ * @covers ::__construct()
+ * @covers ::getSchema()
+ * @covers ::schemaHandler()
+ * @covers ::getTableMapping()
+ */
+ public function testGetSchema() {
+ $columns = array(
+ 'value' => array(
+ 'type' => 'int',
+ ),
+ );
+
+ $this->fieldDefinitions['id'] = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
+ $this->fieldDefinitions['id']->expects($this->once())
+ ->method('getColumns')
+ ->will($this->returnValue($columns));
+ $this->fieldDefinitions['id']->expects($this->once())
+ ->method('getSchema')
+ ->will($this->returnValue(array('columns' => $columns)));
+
+ $this->entityType->expects($this->once())
+ ->method('getKeys')
+ ->will($this->returnValue(array('id' => 'id')));
+ $this->entityType->expects($this->any())
+ ->method('getKey')
+ ->will($this->returnValueMap(array(
+ // EntityStorageBase::__construct()
+ array('id', 'id'),
+ // ContentEntityStorageBase::__construct()
+ array('uuid', NULL),
+ array('bundle', NULL),
+ // ContentEntitySchemaHandler::initializeBaseTable()
+ array('id' => 'id'),
+ // ContentEntitySchemaHandler::processBaseTable()
+ array('id' => 'id'),
+ )));
+
+ $this->entityManager->expects($this->once())
+ ->method('getFieldStorageDefinitions')
+ ->with($this->entityType->id())
+ ->will($this->returnValue($this->fieldDefinitions));
+
+ $this->setUpEntityStorage();
+
+ $expected = array(
+ 'entity_test' => array(
+ 'description' => 'The base table for entity_test entities.',
+ 'fields' => array(
+ 'id' => array(
+ 'type' => 'serial',
+ 'description' => NULL,
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('id'),
+ 'indexes' => array(),
+ 'foreign keys' => array(),
+ ),
+ );
+ $this->assertEquals($expected, $this->entityStorage->getSchema());
+
+ // Test that repeated calls do not result in repeatedly instantiating
+ // ContentEntitySchemaHandler as getFieldStorageDefinitions() is only
+ // expected to be called once.
+ $this->assertEquals($expected, $this->entityStorage->getSchema());
+ }
+
+ /**
+ * Tests getTableMapping() with an empty entity type.
+ *
+ * @covers ::__construct()
+ * @covers ::getTableMapping()
+ */
+ public function testGetTableMappingEmpty() {
+ $this->setUpEntityStorage();
+
+ $mapping = $this->entityStorage->getTableMapping();
+ $this->assertSame(array('entity_test'), $mapping->getTableNames());
+ $this->assertSame(array(), $mapping->getFieldNames('entity_test'));
+ $this->assertSame(array(), $mapping->getExtraColumns('entity_test'));
+ }
+
+ /**
+ * Tests getTableMapping() with a simple entity type.
+ *
+ * @param string[] $entity_keys
+ * A map of entity keys to use for the mocked entity type.
+ *
+ * @covers ::__construct()
+ * @covers ::getTableMapping()
+ *
+ * @dataProvider providerTestGetTableMappingSimple()
+ */
+ public function testGetTableMappingSimple(array $entity_keys) {
+ $this->entityType->expects($this->any())
+ ->method('getKey')
+ ->will($this->returnValueMap(array(
+ array('id', $entity_keys['id']),
+ array('uuid', $entity_keys['uuid']),
+ array('bundle', $entity_keys['bundle']),
+ )));
+
+ $this->setUpEntityStorage();
+
+ $mapping = $this->entityStorage->getTableMapping();
+
+ $this->assertEquals(array('entity_test'), $mapping->getTableNames());
+
+ $expected = array_values(array_filter($entity_keys));
+ $this->assertEquals($expected, $mapping->getFieldNames('entity_test'));
+
+ $this->assertEquals(array(), $mapping->getExtraColumns('entity_test'));
+ }
+
+ /**
+ * Tests getTableMapping() with a simple entity type with some base fields.
+ *
+ * @param string[] $entity_keys
+ * A map of entity keys to use for the mocked entity type.
+ *
+ * @covers ::__construct()
+ * @covers ::getTableMapping()
+ *
+ * @dataProvider providerTestGetTableMappingSimple()
+ */
+ public function testGetTableMappingSimpleWithFields(array $entity_keys) {
+ $base_field_names = array('title', 'description', 'owner');
+ $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
+
+ $definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
+ $this->fieldDefinitions = array_fill_keys($field_names, $definition);
+
+ $this->entityType->expects($this->any())
+ ->method('getKey')
+ ->will($this->returnValueMap(array(
+ array('id', $entity_keys['id']),
+ array('uuid', $entity_keys['uuid']),
+ array('bundle', $entity_keys['bundle']),
+ )));
+
+ $this->setUpEntityStorage();
+
+ $mapping = $this->entityStorage->getTableMapping();
+ $this->assertEquals(array('entity_test'), $mapping->getTableNames());
+ $this->assertEquals($field_names, $mapping->getFieldNames('entity_test'));
+ $this->assertEquals(array(), $mapping->getExtraColumns('entity_test'));
+ }
+
+ /**
+ * Provides test data for testGetTableMappingSimple().
+ *
+ * @return array[]
+ * A nested array, where each inner array has a single value being a map of
+ * entity keys to use for the mocked entity type.
+ */
+ public function providerTestGetTableMappingSimple() {
+ return array(
+ array(array(
+ 'id' => 'test_id',
+ 'bundle' => NULL,
+ 'uuid' => NULL,
+ )),
+ array(array(
+ 'id' => 'test_id',
+ 'bundle' => 'test_bundle',
+ 'uuid' => NULL,
+ )),
+ array(array(
+ 'id' => 'test_id',
+ 'bundle' => NULL,
+ 'uuid' => 'test_uuid',
+ )),
+ array(array(
+ 'id' => 'test_id',
+ 'bundle' => 'test_bundle',
+ 'uuid' => 'test_uuid',
+ )),
+ );
+ }
+
+ /**
+ * Tests getTableMapping() with a revisionable, non-translatable entity type.
+ *
+ * @param string[] $entity_keys
+ * A map of entity keys to use for the mocked entity type.
+ *
+ * @covers ::__construct()
+ * @covers ::getTableMapping()
+ *
+ * @dataProvider providerTestGetTableMappingSimple()
+ */
+ public function testGetTableMappingRevisionable(array $entity_keys) {
+ // This allows to re-use the data provider.
+ $entity_keys = array(
+ 'id' => $entity_keys['id'],
+ 'revision' => 'test_revision',
+ 'bundle' => $entity_keys['bundle'],
+ 'uuid' => $entity_keys['uuid'],
+ );
+
+ $this->entityType->expects($this->exactly(2))
+ ->method('isRevisionable')
+ ->will($this->returnValue(TRUE));
+ $this->entityType->expects($this->any())
+ ->method('getKey')
+ ->will($this->returnValueMap(array(
+ array('id', $entity_keys['id']),
+ array('uuid', $entity_keys['uuid']),
+ array('bundle', $entity_keys['bundle']),
+ array('revision', $entity_keys['revision']),
+ )));
+
+ $this->setUpEntityStorage();
+
+ $mapping = $this->entityStorage->getTableMapping();
+
+ $expected = array('entity_test', 'entity_test_revision');
+ $this->assertEquals($expected, $mapping->getTableNames());
+
+ $expected = array_values(array_filter($entity_keys));
+ $this->assertEquals($expected, $mapping->getFieldNames('entity_test'));
+ $expected = array($entity_keys['id'], $entity_keys['revision']);
+ $this->assertEquals($expected, $mapping->getFieldNames('entity_test_revision'));
+
+ $this->assertEquals(array(), $mapping->getExtraColumns('entity_test'));
+ $this->assertEquals(array(), $mapping->getExtraColumns('entity_test_revision'));
+ }
+
+ /**
+ * Tests getTableMapping() with a revisionable entity type with fields.
+ *
+ * @param string[] $entity_keys
+ * A map of entity keys to use for the mocked entity type.
+ *
+ * @covers ::__construct()
+ * @covers ::getTableMapping()
+ *
+ * @dataProvider providerTestGetTableMappingSimple()
+ */
+ public function testGetTableMappingRevisionableWithFields(array $entity_keys) {
+ // This allows to re-use the data provider.
+ $entity_keys = array(
+ 'id' => $entity_keys['id'],
+ 'revision' => 'test_revision',
+ 'bundle' => $entity_keys['bundle'],
+ 'uuid' => $entity_keys['uuid'],
+ );
+
+ // PHPUnit does not allow for multiple data providers.
+ $test_cases = array(
+ array(),
+ array('revision_timestamp'),
+ array('revision_uid'),
+ array('log'),
+ array('revision_timestamp', 'revision_uid'),
+ array('revision_timestamp', 'log'),
+ array('revision_uid', 'log'),
+ array('revision_timestamp', 'revision_uid', 'log'),
+ );
+ foreach ($test_cases as $revision_metadata_field_names) {
+ $this->setUp();
+
+ $base_field_names = array('title');
+ $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
+
+ $definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
+ $this->fieldDefinitions = array_fill_keys($field_names, $definition);
+
+ $revisionable_field_names = array('description', 'owner');
+ $definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
+ // isRevisionable() is only called once, but we re-use the same definition
+ // for all revisionable fields.
+ $definition->expects($this->any())
+ ->method('isRevisionable')
+ ->will($this->returnValue(TRUE));
+ $field_names = array_merge(
+ $field_names,
+ $revisionable_field_names
+ );
+ $this->fieldDefinitions += array_fill_keys(
+ array_merge($revisionable_field_names, $revision_metadata_field_names),
+ $definition
+ );
+
+ $this->entityType->expects($this->exactly(2))
+ ->method('isRevisionable')
+ ->will($this->returnValue(TRUE));
+ $this->entityType->expects($this->any())
+ ->method('getKey')
+ ->will($this->returnValueMap(array(
+ array('id', $entity_keys['id']),
+ array('uuid', $entity_keys['uuid']),
+ array('bundle', $entity_keys['bundle']),
+ array('revision', $entity_keys['revision']),
+ )));
+
+ $this->setUpEntityStorage();
+
+ $mapping = $this->entityStorage->getTableMapping();
+
+ $expected = array('entity_test', 'entity_test_revision');
+ $this->assertEquals($expected, $mapping->getTableNames());
+
+ $this->assertEquals($field_names, $mapping->getFieldNames('entity_test'));
+ $expected = array_merge(
+ array($entity_keys['id'], $entity_keys['revision']),
+ $revisionable_field_names,
+ $revision_metadata_field_names
+ );
+ $this->assertEquals($expected, $mapping->getFieldNames('entity_test_revision'));
+
+ $this->assertEquals(array(), $mapping->getExtraColumns('entity_test'));
+ $this->assertEquals(array(), $mapping->getExtraColumns('entity_test_revision'));
+ }
+ }
+
+ /**
+ * Tests getTableMapping() with a non-revisionable, translatable entity type.
+ *
+ * @param string[] $entity_keys
+ * A map of entity keys to use for the mocked entity type.
+ *
+ * @covers ::__construct()
+ * @covers ::getTableMapping()
+ *
+ * @dataProvider providerTestGetTableMappingSimple()
+ */
+ public function testGetTableMappingTranslatable(array $entity_keys) {
+ // This allows to re-use the data provider.
+ $entity_keys['langcode'] = 'langcode';
+
+ $this->entityType->expects($this->exactly(2))
+ ->method('isTranslatable')
+ ->will($this->returnValue(TRUE));
+ $this->entityType->expects($this->exactly(3))
+ ->method('getDataTable')
+ ->will($this->returnValue('entity_test_field_data'));
+ $this->entityType->expects($this->any())
+ ->method('getKey')
+ ->will($this->returnValueMap(array(
+ array('id', $entity_keys['id']),
+ array('uuid', $entity_keys['uuid']),
+ array('bundle', $entity_keys['bundle']),
+ )));
+
+ $this->setUpEntityStorage();
+
+ $mapping = $this->entityStorage->getTableMapping();
+
+ $expected = array('entity_test', 'entity_test_field_data');
+ $this->assertEquals($expected, $mapping->getTableNames());
+
+ $expected = array_values(array_filter($entity_keys));
+ $actual = $mapping->getFieldNames('entity_test');
+ $this->assertEquals($expected, $actual);
+ // The UUID is not stored on the data table.
+ $expected = array_values(array_filter(array(
+ $entity_keys['id'],
+ $entity_keys['bundle'],
+ $entity_keys['langcode'],
+ )));
+ $actual = $mapping->getFieldNames('entity_test_field_data');
+ $this->assertEquals($expected, $actual);
+
+ $expected = array();
+ $actual = $mapping->getExtraColumns('entity_test');
+ $this->assertEquals($expected, $actual);
+ $expected = array('default_langcode');
+ $actual = $mapping->getExtraColumns('entity_test_field_data');
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * Tests getTableMapping() with a translatable entity type with fields.
+ *
+ * @param string[] $entity_keys
+ * A map of entity keys to use for the mocked entity type.
+ *
+ * @covers ::__construct()
+ * @covers ::getTableMapping()
+ *
+ * @dataProvider providerTestGetTableMappingSimple()
+ */
+ public function testGetTableMappingTranslatableWithFields(array $entity_keys) {
+ // This allows to re-use the data provider.
+ $entity_keys['langcode'] = 'langcode';
+
+ $base_field_names = array('title', 'description', 'owner');
+ $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
+
+ $definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
+ $this->fieldDefinitions = array_fill_keys($field_names, $definition);
+
+ $this->entityType->expects($this->exactly(2))
+ ->method('isTranslatable')
+ ->will($this->returnValue(TRUE));
+ $this->entityType->expects($this->exactly(3))
+ ->method('getDataTable')
+ ->will($this->returnValue('entity_test_field_data'));
+ $this->entityType->expects($this->any())
+ ->method('getKey')
+ ->will($this->returnValueMap(array(
+ array('id', $entity_keys['id']),
+ array('uuid', $entity_keys['uuid']),
+ array('bundle', $entity_keys['bundle']),
+ )));
+
+ $this->setUpEntityStorage();
+
+ $mapping = $this->entityStorage->getTableMapping();
+
+ $expected = array('entity_test', 'entity_test_field_data');
+ $this->assertEquals($expected, $mapping->getTableNames());
+
+ $expected = array_values(array_filter($entity_keys));
+ $actual = $mapping->getFieldNames('entity_test');
+ $this->assertEquals($expected, $actual);
+ // The UUID is not stored on the data table.
+ $expected = array_merge(array_filter(array(
+ $entity_keys['id'],
+ $entity_keys['bundle'],
+ $entity_keys['langcode'],
+ )), $base_field_names);
+ $actual = $mapping->getFieldNames('entity_test_field_data');
+ $this->assertEquals($expected, $actual);
+
+ $expected = array();
+ $actual = $mapping->getExtraColumns('entity_test');
+ $this->assertEquals($expected, $actual);
+ $expected = array('default_langcode');
+ $actual = $mapping->getExtraColumns('entity_test_field_data');
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * Tests getTableMapping() with a revisionable, translatable entity type.
+ *
+ * @param string[] $entity_keys
+ * A map of entity keys to use for the mocked entity type.
+ *
+ * @covers ::__construct()
+ * @covers ::getTableMapping()
+ *
+ * @dataProvider providerTestGetTableMappingSimple()
+ */
+ public function testGetTableMappingRevisionableTranslatable(array $entity_keys) {
+ // This allows to re-use the data provider.
+ $entity_keys = array(
+ 'id' => $entity_keys['id'],
+ 'revision' => 'test_revision',
+ 'bundle' => $entity_keys['bundle'],
+ 'uuid' => $entity_keys['uuid'],
+ 'langcode' => 'langcode',
+ );
+
+ $this->entityType->expects($this->exactly(2))
+ ->method('isRevisionable')
+ ->will($this->returnValue(TRUE));
+ $this->entityType->expects($this->exactly(2))
+ ->method('isTranslatable')
+ ->will($this->returnValue(TRUE));
+ $this->entityType->expects($this->exactly(3))
+ ->method('getDataTable')
+ ->will($this->returnValue('entity_test_field_data'));
+ $this->entityType->expects($this->any())
+ ->method('getKey')
+ ->will($this->returnValueMap(array(
+ array('id', $entity_keys['id']),
+ array('uuid', $entity_keys['uuid']),
+ array('bundle', $entity_keys['bundle']),
+ array('revision', $entity_keys['revision']),
+ )));
+
+ $this->setUpEntityStorage();
+
+ $mapping = $this->entityStorage->getTableMapping();
+
+ $expected = array(
+ 'entity_test',
+ 'entity_test_field_data',
+ 'entity_test_revision',
+ 'entity_test_field_revision',
+ );
+ $this->assertEquals($expected, $mapping->getTableNames());
+
+ // The language code is not stored on the base table, but on the revision
+ // table.
+ $expected = array_values(array_filter(array(
+ $entity_keys['id'],
+ $entity_keys['revision'],
+ $entity_keys['bundle'],
+ $entity_keys['uuid'],
+ )));
+ $actual = $mapping->getFieldNames('entity_test');
+ $this->assertEquals($expected, $actual);
+ // The revision table on the other hand does not store the bundle and the
+ // UUID.
+ $expected = array_values(array_filter(array(
+ $entity_keys['id'],
+ $entity_keys['revision'],
+ $entity_keys['langcode'],
+ )));
+ $actual = $mapping->getFieldNames('entity_test_revision');
+ $this->assertEquals($expected, $actual);
+ // The UUID is not stored on the data table.
+ $expected = array_values(array_filter(array(
+ $entity_keys['id'],
+ $entity_keys['revision'],
+ $entity_keys['bundle'],
+ $entity_keys['langcode'],
+ )));
+ $actual = $mapping->getFieldNames('entity_test_field_data');
+ $this->assertEquals($expected, $actual);
+ // The data revision also does not store the bundle.
+ $expected = array_values(array_filter(array(
+ $entity_keys['id'],
+ $entity_keys['revision'],
+ $entity_keys['langcode'],
+ )));
+ $actual = $mapping->getFieldNames('entity_test_field_revision');
+ $this->assertEquals($expected, $actual);
+
+ $expected = array();
+ $actual = $mapping->getExtraColumns('entity_test');
+ $this->assertEquals($expected, $actual);
+ $actual = $mapping->getExtraColumns('entity_test_revision');
+ $this->assertEquals($expected, $actual);
+ $expected = array('default_langcode');
+ $actual = $mapping->getExtraColumns('entity_test_field_data');
+ $this->assertEquals($expected, $actual);
+ $actual = $mapping->getExtraColumns('entity_test_field_revision');
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * Tests getTableMapping() with a complex entity type with fields.
+ *
+ * @param string[] $entity_keys
+ * A map of entity keys to use for the mocked entity type.
+ *
+ * @covers ::__construct()
+ * @covers ::getTableMapping()
+ *
+ * @dataProvider providerTestGetTableMappingSimple()
+ */
+ public function testGetTableMappingRevisionableTranslatableWithFields(array $entity_keys) {
+ // This allows to re-use the data provider.
+ $entity_keys = array(
+ 'id' => $entity_keys['id'],
+ 'revision' => 'test_revision',
+ 'bundle' => $entity_keys['bundle'],
+ 'uuid' => $entity_keys['uuid'],
+ 'langcode' => 'langcode',
+ );
+
+ // PHPUnit does not allow for multiple data providers.
+ $test_cases = array(
+ array(),
+ array('revision_timestamp'),
+ array('revision_uid'),
+ array('log'),
+ array('revision_timestamp', 'revision_uid'),
+ array('revision_timestamp', 'log'),
+ array('revision_uid', 'log'),
+ array('revision_timestamp', 'revision_uid', 'log'),
+ );
+ foreach ($test_cases as $revision_metadata_field_names) {
+ $this->setUp();
+
+ $base_field_names = array('title');
+ $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
+
+ $definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
+ $this->fieldDefinitions = array_fill_keys($field_names, $definition);
+
+ $revisionable_field_names = array('description', 'owner');
+ $definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
+ // isRevisionable() is only called once, but we re-use the same definition
+ // for all revisionable fields.
+ $definition->expects($this->any())
+ ->method('isRevisionable')
+ ->will($this->returnValue(TRUE));
+ $this->fieldDefinitions += array_fill_keys(
+ array_merge($revisionable_field_names, $revision_metadata_field_names),
+ $definition
+ );
+
+ $this->entityType->expects($this->exactly(2))
+ ->method('isRevisionable')
+ ->will($this->returnValue(TRUE));
+ $this->entityType->expects($this->exactly(2))
+ ->method('isTranslatable')
+ ->will($this->returnValue(TRUE));
+ $this->entityType->expects($this->exactly(3))
+ ->method('getDataTable')
+ ->will($this->returnValue('entity_test_field_data'));
+ $this->entityType->expects($this->any())
+ ->method('getKey')
+ ->will($this->returnValueMap(array(
+ array('id', $entity_keys['id']),
+ array('uuid', $entity_keys['uuid']),
+ array('bundle', $entity_keys['bundle']),
+ array('revision', $entity_keys['revision']),
+ )));
+
+ $this->setUpEntityStorage();
+
+ $mapping = $this->entityStorage->getTableMapping();
+
+ $expected = array(
+ 'entity_test',
+ 'entity_test_field_data',
+ 'entity_test_revision',
+ 'entity_test_field_revision',
+ );
+ $this->assertEquals($expected, $mapping->getTableNames());
+
+ $expected = array(
+ 'entity_test',
+ 'entity_test_field_data',
+ 'entity_test_revision',
+ 'entity_test_field_revision',
+ );
+ $this->assertEquals($expected, $mapping->getTableNames());
+
+ // The language code is not stored on the base table, but on the revision
+ // table.
+ $expected = array_values(array_filter(array(
+ $entity_keys['id'],
+ $entity_keys['revision'],
+ $entity_keys['bundle'],
+ $entity_keys['uuid'],
+ )));
+ $actual = $mapping->getFieldNames('entity_test');
+ $this->assertEquals($expected, $actual);
+ // The revision table on the other hand does not store the bundle and the
+ // UUID.
+ $expected = array_merge(array_filter(array(
+ $entity_keys['id'],
+ $entity_keys['revision'],
+ $entity_keys['langcode'],
+ )), $revision_metadata_field_names);
+ $actual = $mapping->getFieldNames('entity_test_revision');
+ $this->assertEquals($expected, $actual);
+ // The UUID is not stored on the data table.
+ $expected = array_merge(array_filter(array(
+ $entity_keys['id'],
+ $entity_keys['revision'],
+ $entity_keys['bundle'],
+ $entity_keys['langcode'],
+ )), $base_field_names, $revisionable_field_names);
+ $actual = $mapping->getFieldNames('entity_test_field_data');
+ $this->assertEquals($expected, $actual);
+ // The data revision also does not store the bundle.
+ $expected = array_merge(array_filter(array(
+ $entity_keys['id'],
+ $entity_keys['revision'],
+ $entity_keys['langcode'],
+ )), $revisionable_field_names);
+ $actual = $mapping->getFieldNames('entity_test_field_revision');
+ $this->assertEquals($expected, $actual);
+
+ $expected = array();
+ $actual = $mapping->getExtraColumns('entity_test');
+ $this->assertEquals($expected, $actual);
+ $actual = $mapping->getExtraColumns('entity_test_revision');
+ $this->assertEquals($expected, $actual);
+ $expected = array('default_langcode');
+ $actual = $mapping->getExtraColumns('entity_test_field_data');
+ $this->assertEquals($expected, $actual);
+ $actual = $mapping->getExtraColumns('entity_test_field_revision');
+ $this->assertEquals($expected, $actual);
+ }
+ }
+
+ /**
* Tests field SQL schema generation for an entity with a string identifier.
*
* @covers ::_fieldSqlSchema()
*/
public function testFieldSqlSchemaForEntityWithStringIdentifier() {
$field_type_manager = $this->getMock('Drupal\Core\Field\FieldTypePluginManagerInterface');
- $entity_manager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
$container = new ContainerBuilder();
$container->set('plugin.manager.field.field_type', $field_type_manager);
- $container->set('entity.manager', $entity_manager);
+ $container->set('entity.manager', $this->entityManager);
\Drupal::setContainer($container);
- $definition = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
- $definition->expects($this->any())
+ $this->entityType->expects($this->any())
->method('getKey')
->will($this->returnValueMap(array(
array('id', 'id'),
array('revision', 'revision'),
)));
- $definition->expects($this->once())
+ $this->entityType->expects($this->once())
->method('hasKey')
->with('revision')
->will($this->returnValue(TRUE));
@@ -66,19 +942,18 @@ public function testFieldSqlSchemaForEntityWithStringIdentifier() {
->method('getDefaultInstanceSettings')
->will($this->returnValue(array()));
- $fields['id'] = FieldDefinition::create('string')
+ $this->fieldDefinitions['id'] = FieldDefinition::create('string')
->setName('id');
- $fields['revision'] = FieldDefinition::create('string')
+ $this->fieldDefinitions['revision'] = FieldDefinition::create('string')
->setName('revision');
- $entity_manager->expects($this->any())
+ $this->entityManager->expects($this->any())
->method('getDefinition')
->with('test_entity')
- ->will($this->returnValue($definition));
- $entity_manager->expects($this->any())
+ ->will($this->returnValue($this->entityType));
+ $this->entityManager->expects($this->once())
->method('getBaseFieldDefinitions')
- ->with('test_entity')
- ->will($this->returnValue($fields));
+ ->will($this->returnValue($this->fieldDefinitions));
// Define a field definition for a test_field field.
$field = $this->getMock('\Drupal\field\FieldConfigInterface');
@@ -118,43 +993,42 @@ public function testFieldSqlSchemaForEntityWithStringIdentifier() {
public function testCreate() {
$language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
$module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
- $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
- // @todo Add field definitions to test default values of fields.
- $entity_manager->expects($this->atLeastOnce())
- ->method('getFieldDefinitions')
- ->will($this->returnValue(array()));
$container = new ContainerBuilder();
$container->set('language_manager', $language_manager);
- $container->set('entity.manager', $entity_manager);
+ $container->set('entity.manager', $this->entityManager);
$container->set('module_handler', $module_handler);
\Drupal::setContainer($container);
- $entity = $this->getMockForAbstractClass('Drupal\Core\Entity\ContentEntityBase', array(), '', FALSE, TRUE, TRUE, array('id'));
- $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
- $entity_type->expects($this->atLeastOnce())
- ->method('id')
- ->will($this->returnValue('test_entity_type'));
- $entity_type->expects($this->atLeastOnce())
+ $entity = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase')
+ ->disableOriginalConstructor()
+ ->setMethods(array('id'))
+ ->getMockForAbstractClass();
+
+ $this->entityType->expects($this->atLeastOnce())
->method('getClass')
->will($this->returnValue(get_class($entity)));
- $entity_type->expects($this->atLeastOnce())
+ $this->entityType->expects($this->atLeastOnce())
->method('getKeys')
->will($this->returnValue(array('id' => 'id')));
- $entity_type->expects($this->atLeastOnce())
+
+ // ContentEntityStorageBase iterates over the entity which calls this method
+ // internally in ContentEntityBase::getProperties().
+ $this->entityManager->expects($this->once())
+ ->method('getFieldDefinitions')
+ ->will($this->returnValue(array()));
+
+ $this->entityType->expects($this->atLeastOnce())
->method('isRevisionable')
->will($this->returnValue(FALSE));
- $entity_manager->expects($this->atLeastOnce())
+ $this->entityManager->expects($this->atLeastOnce())
->method('getDefinition')
- ->with('test_entity_type')
- ->will($this->returnValue($entity_type));
+ ->with($this->entityType->id())
+ ->will($this->returnValue($this->entityType));
- $connection = $this->getMockBuilder('Drupal\Core\Database\Connection')
- ->disableOriginalConstructor()
- ->getMock();
- $entity_storage = new ContentEntityDatabaseStorage($entity_type, $connection, $entity_manager);
+ $this->setUpEntityStorage();
- $entity = $entity_storage->create();
+ $entity = $this->entityStorage->create();
$entity->expects($this->atLeastOnce())
->method('id')
->will($this->returnValue('foo'));
@@ -164,4 +1038,19 @@ public function testCreate() {
$this->assertTrue($entity->isNew());
}
+ /**
+ * Sets up the content entity database storage.
+ */
+ protected function setUpEntityStorage() {
+ $connection = $this->getMockBuilder('Drupal\Core\Database\Connection')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->entityManager->expects($this->once())
+ ->method('getBaseFieldDefinitions')
+ ->will($this->returnValue($this->fieldDefinitions));
+
+ $this->entityStorage = new ContentEntityDatabaseStorage($this->entityType, $connection, $this->entityManager);
+ }
+
}
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..f347b2a
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Entity/Schema/ContentEntitySchemaHandlerTest.php
@@ -0,0 +1,769 @@
+ '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->storage = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityDatabaseStorage')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->storage->expects($this->any())
+ ->method('getBaseTable')
+ ->will($this->returnValue('entity_test'));
+
+ // Add an ID field. This also acts as a test for a simple, single-column
+ // field.
+ $this->setUpStorageDefinition('id', array(
+ 'columns' => array(
+ 'value' => array(
+ 'type' => 'int',
+ ),
+ ),
+ ));
+ }
+
+ /**
+ * Tests the schema for non-revisionable, non-translatable entities.
+ *
+ * @param bool $uuid_key
+ * Whether or not the tested entity type should have a UUID key.
+ *
+ * @covers ::__construct()
+ * @covers ::getSchema()
+ * @covers ::getTables()
+ * @covers ::initializeBaseTable()
+ * @covers ::getEntityIndexName()
+ * @covers ::addFieldSchema()
+ * @covers ::getFieldIndexes()
+ * @covers ::getFieldUniqueKeys()
+ * @covers ::getFieldForeignKeys()
+ * @covers ::getFieldSchemaData()
+ * @covers ::addDefaultLangcodeSchema()
+ * @covers ::processBaseTable()
+ * @covers ::processIdentifierSchema()
+ *
+ * @dataProvider providerTestGetSchemaLayoutBase
+ */
+ public function testGetSchemaBase($uuid_key) {
+ $this->entityType = new ContentEntityType(array(
+ 'id' => 'entity_test',
+ 'entity_keys' => array(
+ 'id' => 'id',
+ 'uuid' => $uuid_key ? 'uuid' : NULL,
+ ),
+ ));
+
+ // Add a field with a 'length' constraint.
+ $this->setUpStorageDefinition('name', array(
+ 'columns' => array(
+ 'value' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ ),
+ ),
+ ));
+ if ($uuid_key) {
+ $this->setUpStorageDefinition('uuid', array(
+ 'columns' => array(
+ 'value' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ ),
+ ),
+ ));
+ }
+ // Add a multi-column field.
+ $this->setUpStorageDefinition('description', array(
+ 'columns' => array(
+ 'value' => array(
+ 'type' => 'text',
+ 'description' => 'The text value',
+ ),
+ 'format' => array(
+ 'type' => 'varchar',
+ 'description' => 'The text description',
+ ),
+ ),
+ ));
+ // Add a field with an index.
+ $this->setUpStorageDefinition('owner', array(
+ 'columns' => array(
+ 'target_id' => array(
+ 'description' => 'The ID of the target entity.',
+ 'type' => 'int',
+ ),
+ ),
+ 'indexes' => array(
+ 'target_id' => array('target_id'),
+ ),
+ ));
+ // Add a field with an index, specified as column name and length.
+ $this->setUpStorageDefinition('translator', array(
+ 'columns' => array(
+ 'target_id' => array(
+ 'description' => 'The ID of the target entity.',
+ 'type' => 'int',
+ ),
+ ),
+ 'indexes' => array(
+ 'target_id' => array(array('target_id', 10)),
+ ),
+ ));
+ // Add a field with a multi-column index.
+ $this->setUpStorageDefinition('location', array(
+ 'columns' => array(
+ 'country' => array(
+ 'type' => 'varchar',
+ ),
+ 'state' => array(
+ 'type' => 'varchar',
+ ),
+ 'city' => array(
+ 'type' => 'varchar',
+ )
+ ),
+ 'indexes' => array(
+ 'country_state_city' => array('country', 'state', array('city', 10)),
+ ),
+ ));
+ // Add a field with a foreign key.
+ $this->setUpStorageDefinition('editor', array(
+ 'columns' => array(
+ 'target_id' => array(
+ 'type' => 'int',
+ ),
+ ),
+ 'foreign keys' => array(
+ 'user_id' => array(
+ 'table' => 'users',
+ 'columns' => array('target_id' => 'uid'),
+ ),
+ ),
+ ));
+ // Add a multi-column field with a foreign key.
+ $this->setUpStorageDefinition('editor_revision', array(
+ 'columns' => array(
+ 'target_id' => array(
+ 'type' => 'int',
+ ),
+ 'target_revision_id' => array(
+ 'type' => 'int',
+ ),
+ ),
+ 'foreign keys' => array(
+ 'user_id' => array(
+ 'table' => 'users',
+ 'columns' => array('target_id' => 'uid'),
+ ),
+ ),
+ ));
+
+ $this->setUpSchemaHandler();
+
+ $table_mapping = new DefaultTableMapping($this->storageDefinitions);
+ $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
+ $table_mapping->setExtraColumns('entity_test', array('default_langcode'));
+
+ $this->storage->expects($this->once())
+ ->method('getTableMapping')
+ ->will($this->returnValue($table_mapping));
+
+ $expected = array(
+ 'entity_test' => array(
+ 'description' => 'The base table for entity_test entities.',
+ 'fields' => array(
+ 'id' => array(
+ 'description' => 'The id field.',
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ ),
+ 'name' => array(
+ 'description' => 'The name field.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ ),
+ 'description__value' => array(
+ 'description' => 'The description field.',
+ 'type' => 'text',
+ ),
+ 'description__format' => array(
+ 'description' => 'The description field.',
+ 'type' => 'varchar',
+ ),
+ 'owner' => array(
+ 'description' => 'The owner field.',
+ 'type' => 'int',
+ ),
+ 'translator' => array(
+ 'description' => 'The translator field.',
+ 'type' => 'int',
+ ),
+ 'location__country' => array(
+ 'description' => 'The location field.',
+ 'type' => 'varchar',
+ ),
+ 'location__state' => array(
+ 'description' => 'The location field.',
+ 'type' => 'varchar',
+ ),
+ 'location__city' => array(
+ 'description' => 'The location field.',
+ 'type' => 'varchar',
+ ),
+ 'editor' => array(
+ 'description' => 'The editor field.',
+ 'type' => 'int',
+ ),
+ 'editor_revision__target_id' => array(
+ 'description' => 'The editor_revision field.',
+ 'type' => 'int',
+ ),
+ 'editor_revision__target_revision_id' => array(
+ 'description' => 'The editor_revision field.',
+ 'type' => 'int',
+ ),
+ 'default_langcode' => array(
+ 'description' => 'Boolean indicating whether field values are in the default entity language.',
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'not null' => TRUE,
+ 'default' => 1,
+ ),
+ ),
+ 'primary key' => array('id'),
+ 'indexes' => array(
+ 'entity_test_field__owner__target_id' => array('owner'),
+ 'entity_test_field__translator__target_id' => array(
+ array('translator', 10),
+ ),
+ 'entity_test_field__location__country_state_city' => array(
+ 'location__country',
+ 'location__state',
+ array('location__city', 10),
+ ),
+ ),
+ 'foreign keys' => array(
+ 'entity_test_field__editor__user_id' => array(
+ 'table' => 'users',
+ 'columns' => array('editor' => 'uid'),
+ ),
+ 'entity_test_field__editor_revision__user_id' => array(
+ 'table' => 'users',
+ 'columns' => array('editor_revision__target_id' => 'uid'),
+ ),
+ ),
+ ),
+ );
+ if ($uuid_key) {
+ $expected['entity_test']['fields']['uuid'] = array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'description' => 'The uuid field.',
+ 'not null' => TRUE,
+ );
+ $expected['entity_test']['unique keys']['entity_test__uuid'] = array('uuid');
+ }
+ $actual = $this->schemaHandler->getSchema();
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * Provides data for testGetSchemaLayoutBase().
+ *
+ * @return array
+ * Returns a nested array where each inner array returns a boolean,
+ * indicating whether or not the tested entity type should include a UUID
+ * key.
+ */
+ public function providerTestGetSchemaLayoutBase() {
+ return array(
+ array(FALSE),
+ array(TRUE),
+ );
+ }
+
+ /**
+ * Tests the schema for revisionable, non-translatable entities.
+ *
+ * @covers ::__construct()
+ * @covers ::getSchema()
+ * @covers ::getTables()
+ * @covers ::initializeBaseTable()
+ * @covers ::initializeRevisionTable()
+ * @covers ::getEntityIndexName()
+ * @covers ::processRevisionTable()
+ * @covers ::processIdentifierSchema()
+ */
+ public function testGetSchemaRevisionable() {
+ $this->entityType = new ContentEntityType(array(
+ 'id' => 'entity_test',
+ 'entity_keys' => array(
+ 'id' => 'id',
+ 'revision' => 'revision_id',
+ ),
+ ));
+
+ $this->storage->expects($this->exactly(2))
+ ->method('getRevisionTable')
+ ->will($this->returnValue('entity_test_revision'));
+
+ $this->setUpStorageDefinition('revision_id', array(
+ 'columns' => array(
+ 'value' => array(
+ 'type' => 'int',
+ ),
+ ),
+ ));
+
+ $this->setUpSchemaHandler();
+
+ $table_mapping = new DefaultTableMapping($this->storageDefinitions);
+ $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
+ $table_mapping->setFieldNames('entity_test_revision', array_keys($this->storageDefinitions));
+
+ $this->storage->expects($this->once())
+ ->method('getTableMapping')
+ ->will($this->returnValue($table_mapping));
+
+ $expected = array(
+ 'entity_test' => array(
+ 'description' => 'The base table for entity_test entities.',
+ 'fields' => array(
+ 'id' => array(
+ 'description' => 'The id field.',
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ ),
+ 'revision_id' => array(
+ 'description' => 'The revision_id field.',
+ 'type' => 'int',
+ )
+ ),
+ 'primary key' => array('id'),
+ 'indexes' => array(),
+ 'foreign keys' => array(
+ 'entity_test__revision' => array(
+ 'table' => 'entity_test_revision',
+ 'columns' => array('revision_id' => 'revision_id'),
+ )
+ ),
+ 'unique keys' => array(
+ 'entity_test__revision_id' => array('revision_id'),
+ ),
+ ),
+ 'entity_test_revision' => array(
+ 'description' => 'The revision table for entity_test entities.',
+ 'fields' => array(
+ 'id' => array(
+ 'description' => 'The id field.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ ),
+ 'revision_id' => array(
+ 'description' => 'The revision_id field.',
+ 'type' => 'serial',
+ ),
+ ),
+ 'primary key' => array('revision_id'),
+ 'indexes' => array(
+ 'entity_test__id' => array('id'),
+ ),
+ 'foreign keys' => array(
+ 'entity_test__revisioned' => array(
+ 'table' => 'entity_test',
+ 'columns' => array('id' => 'id'),
+ ),
+ ),
+ ),
+ );
+
+ $actual = $this->schemaHandler->getSchema();
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * Tests the schema for non-revisionable, translatable entities.
+ *
+ * @covers ::__construct()
+ * @covers ::getSchema()
+ * @covers ::getTables()
+ * @covers ::initializeDataTable()
+ * @covers ::getEntityIndexName()
+ * @covers ::processDataTable()
+ */
+ public function testGetSchemaTranslatable() {
+ $this->entityType = new ContentEntityType(array(
+ 'id' => 'entity_test',
+ 'entity_keys' => array(
+ 'id' => 'id',
+ ),
+ ));
+
+ $this->storage->expects($this->once())
+ ->method('getDataTable')
+ ->will($this->returnValue('entity_test_field_data'));
+
+ $this->setUpStorageDefinition('langcode', array(
+ 'columns' => array(
+ 'value' => array(
+ 'type' => 'varchar',
+ ),
+ ),
+ ));
+
+ $this->setUpSchemaHandler();
+
+ $table_mapping = new DefaultTableMapping($this->storageDefinitions);
+ $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
+ $table_mapping->setFieldNames('entity_test_field_data', array_keys($this->storageDefinitions));
+
+ $this->storage->expects($this->once())
+ ->method('getTableMapping')
+ ->will($this->returnValue($table_mapping));
+
+ $expected = array(
+ 'entity_test' => array(
+ 'description' => 'The base table for entity_test entities.',
+ 'fields' => array(
+ 'id' => array(
+ 'description' => 'The id field.',
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ ),
+ 'langcode' => array(
+ 'description' => 'The langcode field.',
+ 'type' => 'varchar',
+ 'not null' => TRUE,
+ )
+ ),
+ 'primary key' => array('id'),
+ 'indexes' => array(),
+ 'foreign keys' => array(),
+ ),
+ 'entity_test_field_data' => array(
+ 'description' => 'The data table for entity_test entities.',
+ 'fields' => array(
+ 'id' => array(
+ 'description' => 'The id field.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ ),
+ 'langcode' => array(
+ 'description' => 'The langcode field.',
+ 'type' => 'varchar',
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('id', 'langcode'),
+ 'indexes' => array(),
+ 'foreign keys' => array(
+ 'entity_test' => array(
+ 'table' => 'entity_test',
+ 'columns' => array('id' => 'id'),
+ ),
+ ),
+ ),
+ );
+
+ $actual = $this->schemaHandler->getSchema();
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * Tests the schema for revisionable, translatable entities.
+ *
+ * @covers ::__construct()
+ * @covers ::getSchema()
+ * @covers ::getTables()
+ * @covers ::initializeDataTable()
+ * @covers ::getEntityIndexName()
+ * @covers ::initializeRevisionDataTable()
+ * @covers ::processRevisionDataTable()
+ */
+ public function testGetSchemaRevisionableTranslatable() {
+ $this->entityType = new ContentEntityType(array(
+ 'id' => 'entity_test',
+ 'entity_keys' => array(
+ 'id' => 'id',
+ 'revision' => 'revision_id',
+ ),
+ ));
+
+ $this->storage->expects($this->exactly(3))
+ ->method('getRevisionTable')
+ ->will($this->returnValue('entity_test_revision'));
+ $this->storage->expects($this->once())
+ ->method('getDataTable')
+ ->will($this->returnValue('entity_test_field_data'));
+ $this->storage->expects($this->once())
+ ->method('getRevisionDataTable')
+ ->will($this->returnValue('entity_test_revision_field_data'));
+
+ $this->setUpStorageDefinition('revision_id', array(
+ 'columns' => array(
+ 'value' => array(
+ 'type' => 'int',
+ ),
+ ),
+ ));
+ $this->setUpStorageDefinition('langcode', array(
+ 'columns' => array(
+ 'value' => array(
+ 'type' => 'varchar',
+ ),
+ ),
+ ));
+
+ $this->setUpSchemaHandler();
+
+ $table_mapping = new DefaultTableMapping($this->storageDefinitions);
+ $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
+ $table_mapping->setFieldNames('entity_test_revision', array_keys($this->storageDefinitions));
+ $table_mapping->setFieldNames('entity_test_field_data', array_keys($this->storageDefinitions));
+ $table_mapping->setFieldNames('entity_test_revision_field_data', array_keys($this->storageDefinitions));
+
+ $this->storage->expects($this->once())
+ ->method('getTableMapping')
+ ->will($this->returnValue($table_mapping));
+
+ $expected = array(
+ 'entity_test' => array(
+ 'description' => 'The base table for entity_test entities.',
+ 'fields' => array(
+ 'id' => array(
+ 'description' => 'The id field.',
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ ),
+ 'revision_id' => array(
+ 'description' => 'The revision_id field.',
+ 'type' => 'int',
+ ),
+ 'langcode' => array(
+ 'description' => 'The langcode field.',
+ 'type' => 'varchar',
+ 'not null' => TRUE,
+ )
+ ),
+ 'primary key' => array('id'),
+ 'indexes' => array(),
+ 'unique keys' => array(
+ 'entity_test__revision_id' => array('revision_id'),
+ ),
+ 'foreign keys' => array(
+ 'entity_test__revision' => array(
+ 'table' => 'entity_test_revision',
+ 'columns' => array('revision_id' => 'revision_id'),
+ ),
+ ),
+ ),
+ 'entity_test_revision' => array(
+ 'description' => 'The revision table for entity_test entities.',
+ 'fields' => array(
+ 'id' => array(
+ 'description' => 'The id field.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ ),
+ 'revision_id' => array(
+ 'description' => 'The revision_id field.',
+ 'type' => 'serial',
+ ),
+ 'langcode' => array(
+ 'description' => 'The langcode field.',
+ 'type' => 'varchar',
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('revision_id'),
+ 'indexes' => array(
+ 'entity_test__id' => array('id'),
+ ),
+ 'foreign keys' => array(
+ 'entity_test__revisioned' => array(
+ 'table' => 'entity_test',
+ 'columns' => array('id' => 'id'),
+ ),
+ ),
+ ),
+ 'entity_test_field_data' => array(
+ 'description' => 'The data table for entity_test entities.',
+ 'fields' => array(
+ 'id' => array(
+ 'description' => 'The id field.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ ),
+ 'revision_id' => array(
+ 'description' => 'The revision_id field.',
+ 'type' => 'int',
+ ),
+ 'langcode' => array(
+ 'description' => 'The langcode field.',
+ 'type' => 'varchar',
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('id', 'langcode'),
+ 'indexes' => array(
+ 'entity_test__revision_id' => array('revision_id'),
+ ),
+ 'foreign keys' => array(
+ 'entity_test' => array(
+ 'table' => 'entity_test',
+ 'columns' => array('id' => 'id'),
+ ),
+ ),
+ ),
+ 'entity_test_revision_field_data' => array(
+ 'description' => 'The revision data table for entity_test entities.',
+ 'fields' => array(
+ 'id' => array(
+ 'description' => 'The id field.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ ),
+ 'revision_id' => array(
+ 'description' => 'The revision_id field.',
+ 'type' => 'int',
+ ),
+ 'langcode' => array(
+ 'description' => 'The langcode field.',
+ 'type' => 'varchar',
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('revision_id', 'langcode'),
+ 'indexes' => array(),
+ 'foreign keys' => array(
+ 'entity_test' => array(
+ 'table' => 'entity_test',
+ 'columns' => array('id' => 'id'),
+ ),
+ 'entity_test__revision' => array(
+ 'table' => 'entity_test_revision',
+ 'columns' => array('revision_id' => 'revision_id'),
+ ),
+ ),
+ ),
+ );
+
+ $actual = $this->schemaHandler->getSchema();
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * Sets up the schema handler.
+ *
+ * This uses the field definitions set in $this->fieldDefinitions.
+ */
+ protected function setUpSchemaHandler() {
+ $this->entityManager->expects($this->once())
+ ->method('getFieldStorageDefinitions')
+ ->with($this->entityType->id())
+ ->will($this->returnValue($this->storageDefinitions));
+ $this->schemaHandler = new ContentEntitySchemaHandler(
+ $this->entityManager,
+ $this->entityType,
+ $this->storage
+ );
+ }
+
+ /**
+ * 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 setUpStorageDefinition($field_name, array $schema) {
+ $this->storageDefinitions[$field_name] = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
+ // getDescription() is called once for each table.
+ $this->storageDefinitions[$field_name]->expects($this->any())
+ ->method('getDescription')
+ ->will($this->returnValue("The $field_name field."));
+ // getSchema() is called once for each table.
+ $this->storageDefinitions[$field_name]->expects($this->any())
+ ->method('getSchema')
+ ->will($this->returnValue($schema));
+ $this->storageDefinitions[$field_name]->expects($this->once())
+ ->method('getColumns')
+ ->will($this->returnValue($schema['columns']));
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/DefaultTableMappingTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/DefaultTableMappingTest.php
new file mode 100644
index 0000000..501315d
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/DefaultTableMappingTest.php
@@ -0,0 +1,267 @@
+ 'Default table mapping',
+ 'description' => 'Check that the default table mapping works.',
+ 'group' => 'Entity',
+ ];
+ }
+
+ /**
+ * Tests DefaultTableMapping::getTableNames().
+ *
+ * @covers ::getTableNames()
+ */
+ public function testGetTableNames() {
+ // The storage definitions are only used in getColumnNames() so we do not
+ // need to provide any here.
+ $table_mapping = new DefaultTableMapping([]);
+ $this->assertSame([], $table_mapping->getTableNames());
+
+ $table_mapping->setFieldNames('foo', []);
+ $this->assertSame(['foo'], $table_mapping->getTableNames());
+
+ $table_mapping->setFieldNames('bar', []);
+ $this->assertSame(['foo', 'bar'], $table_mapping->getTableNames());
+
+ $table_mapping->setExtraColumns('baz', []);
+ $this->assertSame(['foo', 'bar', 'baz'], $table_mapping->getTableNames());
+
+ // Test that table names are not duplicated.
+ $table_mapping->setExtraColumns('foo', []);
+ $this->assertSame(['foo', 'bar', 'baz'], $table_mapping->getTableNames());
+ }
+
+ /**
+ * Tests DefaultTableMapping::getAllColumns().
+ *
+ * @covers ::__construct()
+ * @covers ::getAllColumns()
+ * @covers ::getFieldNames()
+ * @covers ::getColumnNames()
+ * @covers ::setFieldNames()
+ * @covers ::getExtraColumns()
+ * @covers ::setExtraColumns()
+ */
+ public function testGetAllColumns() {
+ // Set up single-column and multi-column definitions.
+ $definitions['id'] = $this->setUpDefinition(['value']);
+ $definitions['name'] = $this->setUpDefinition(['value']);
+ $definitions['type'] = $this->setUpDefinition(['value']);
+ $definitions['description'] = $this->setUpDefinition(['value', 'format']);
+ $definitions['owner'] = $this->setUpDefinition([
+ 'target_id',
+ 'target_revision_id',
+ ]);
+
+ $table_mapping = new DefaultTableMapping($definitions);
+ $expected = [];
+ $this->assertSame($expected, $table_mapping->getAllColumns('test'));
+
+ // Test adding field columns.
+ $table_mapping->setFieldNames('test', ['id']);
+ $expected = ['id'];
+ $this->assertSame($expected, $table_mapping->getAllColumns('test'));
+
+ $table_mapping->setFieldNames('test', ['id', 'name']);
+ $expected = ['id', 'name'];
+ $this->assertSame($expected, $table_mapping->getAllColumns('test'));
+
+ $table_mapping->setFieldNames('test', ['id', 'name', 'type']);
+ $expected = ['id', 'name', 'type'];
+ $this->assertSame($expected, $table_mapping->getAllColumns('test'));
+
+ $table_mapping->setFieldNames('test', [
+ 'id',
+ 'name',
+ 'type',
+ 'description',
+ ]);
+ $expected = [
+ 'id',
+ 'name',
+ 'type',
+ 'description__value',
+ 'description__format',
+ ];
+ $this->assertSame($expected, $table_mapping->getAllColumns('test'));
+
+ $table_mapping->setFieldNames('test', [
+ 'id',
+ 'name',
+ 'type',
+ 'description',
+ 'owner',
+ ]);
+ $expected = [
+ 'id',
+ 'name',
+ 'type',
+ 'description__value',
+ 'description__format',
+ 'owner__target_id',
+ 'owner__target_revision_id',
+ ];
+ $this->assertSame($expected, $table_mapping->getAllColumns('test'));
+
+ // Test adding extra columns.
+ $table_mapping->setFieldNames('test', []);
+ $table_mapping->setExtraColumns('test', ['default_langcode']);
+ $expected = ['default_langcode'];
+ $this->assertSame($expected, $table_mapping->getAllColumns('test'));
+
+ $table_mapping->setExtraColumns('test', [
+ 'default_langcode',
+ 'default_revision',
+ ]);
+ $expected = ['default_langcode', 'default_revision'];
+ $this->assertSame($expected, $table_mapping->getAllColumns('test'));
+
+ // Test adding both field and extra columns.
+ $table_mapping->setFieldNames('test', [
+ 'id',
+ 'name',
+ 'type',
+ 'description',
+ 'owner',
+ ]);
+ $table_mapping->setExtraColumns('test', [
+ 'default_langcode',
+ 'default_revision',
+ ]);
+ $expected = [
+ 'id',
+ 'name',
+ 'type',
+ 'description__value',
+ 'description__format',
+ 'owner__target_id',
+ 'owner__target_revision_id',
+ 'default_langcode',
+ 'default_revision',
+ ];
+ $this->assertSame($expected, $table_mapping->getAllColumns('test'));
+ }
+
+ /**
+ * Tests DefaultTableMapping::getFieldNames().
+ *
+ * @covers ::getFieldNames()
+ * @covers ::setFieldNames()
+ */
+ public function testGetFieldNames() {
+ // The storage definitions are only used in getColumnNames() so we do not
+ // need to provide any here.
+ $table_mapping = new DefaultTableMapping([]);
+
+ // Test that requesting the list of field names for a table for which no
+ // fields have been added does not fail.
+ $this->assertSame([], $table_mapping->getFieldNames('foo'));
+
+ $return = $table_mapping->setFieldNames('foo', ['id', 'name', 'type']);
+ $this->assertSame($table_mapping, $return);
+ $expected = ['id', 'name', 'type'];
+ $this->assertSame($expected, $table_mapping->getFieldNames('foo'));
+ $this->assertSame([], $table_mapping->getFieldNames('bar'));
+
+ $return = $table_mapping->setFieldNames('bar', ['description', 'owner']);
+ $this->assertSame($table_mapping, $return);
+ $expected = ['description', 'owner'];
+ $this->assertSame($expected, $table_mapping->getFieldNames('bar'));
+ // Test that the previously added field names are unaffected.
+ $expected = ['id', 'name', 'type'];
+ $this->assertSame($expected, $table_mapping->getFieldNames('foo'));
+ }
+
+ /**
+ * Tests DefaultTableMapping::getColumnNames().
+ *
+ * @covers ::__construct()
+ * @covers ::getColumnNames()
+ */
+ public function testGetColumnNames() {
+ $definitions['test'] = $this->setUpDefinition([]);
+ $table_mapping = new DefaultTableMapping($definitions);
+ $expected = [];
+ $this->assertSame($expected, $table_mapping->getColumnNames('test'));
+
+ $definitions['test'] = $this->setUpDefinition(['value']);
+ $table_mapping = new DefaultTableMapping($definitions);
+ $expected = ['value' => 'test'];
+ $this->assertSame($expected, $table_mapping->getColumnNames('test'));
+
+ $definitions['test'] = $this->setUpDefinition(['value', 'format']);
+ $table_mapping = new DefaultTableMapping($definitions);
+ $expected = ['value' => 'test__value', 'format' => 'test__format'];
+ $this->assertSame($expected, $table_mapping->getColumnNames('test'));
+ }
+
+ /**
+ * Tests DefaultTableMapping::getExtraColumns().
+ *
+ * @covers ::getExtraColumns()
+ * @covers ::setExtraColumns()
+ */
+ public function testGetExtraColumns() {
+ // The storage definitions are only used in getColumnNames() so we do not
+ // need to provide any here.
+ $table_mapping = new DefaultTableMapping([]);
+
+ // Test that requesting the list of field names for a table for which no
+ // fields have been added does not fail.
+ $this->assertSame([], $table_mapping->getExtraColumns('foo'));
+
+ $return = $table_mapping->setExtraColumns('foo', ['id', 'name', 'type']);
+ $this->assertSame($table_mapping, $return);
+ $expected = ['id', 'name', 'type'];
+ $this->assertSame($expected, $table_mapping->getExtraColumns('foo'));
+ $this->assertSame([], $table_mapping->getExtraColumns('bar'));
+
+ $return = $table_mapping->setExtraColumns('bar', ['description', 'owner']);
+ $this->assertSame($table_mapping, $return);
+ $expected = ['description', 'owner'];
+ $this->assertSame($expected, $table_mapping->getExtraColumns('bar'));
+ // Test that the previously added field names are unaffected.
+ $expected = ['id', 'name', 'type'];
+ $this->assertSame($expected, $table_mapping->getExtraColumns('foo'));
+ }
+
+ /**
+ * Sets up a field storage definition for the test.
+ *
+ * @param array $column_names
+ * An array of column names for the storage definition.
+ *
+ * @return \Drupal\Core\Field\FieldStorageDefinitionInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function setUpDefinition(array $column_names) {
+ $definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
+ $definition->expects($this->any())
+ ->method('getColumns')
+ ->will($this->returnValue(array_fill_keys($column_names, [])));
+ return $definition;
+ }
+
+}