diff --git a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php index 4228b70..7791b47 100644 --- a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php @@ -248,6 +248,7 @@ public function load($id) { */ protected function mapFromStorageRecords(array $records) { $entities = array(); + $field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityTypeId); foreach ($records as $id => $values) { $entities[$id] = array(); // Skip the item delta and item value levels (if possible) but let the @@ -258,15 +259,18 @@ protected function mapFromStorageRecords(array $records) { // that store several properties). if ($field_name = strstr($name, '__', TRUE)) { $property_name = substr($name, strpos($name, '__') + 2); - $entities[$id][$field_name][Language::LANGCODE_DEFAULT][$property_name] = $value; + + $serialize = !empty($field_definitions[$field_name]->getColumns()[$property_name]['serialize']); + $entities[$id][$field_name][Language::LANGCODE_DEFAULT][$property_name] = $serialize ? unserialize($value) : $value; } else { // Handle columns named directly after the field (e.g if the field // type only stores one property). - $entities[$id][$name][Language::LANGCODE_DEFAULT] = $value; + $serialize = !empty($field_definitions[$name]->getColumns()[$field_definitions[$name]->getMainPropertyName()]['serialize']); + $entities[$id][$name][Language::LANGCODE_DEFAULT] = $serialize ? unserialize($value) : $value; } } - // If we have no multilingual values we can instantiate entity objecs + // If we have no multilingual values we can instantiate entity objects // right now, otherwise we need to collect all the field values first. if (!$this->dataTable) { $bundle = $this->bundleKey ? $values[$this->bundleKey] : FALSE; @@ -305,12 +309,16 @@ protected function attachPropertyData(array &$entities) { $data = $query->execute(); $field_definitions = \Drupal::entityManager()->getBaseFieldDefinitions($this->entityTypeId); + $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()))); + $base_schema = $this->schemaBuilder->getTableSchema($this->entityType, $field_definitions, 'revision_data_table'); + $data_revision_schema = $this->schemaBuilder->getTableSchema($this->entityType, $field_definitions, 'revision_data_table'); + $data_column_names = array_flip(array_diff(array_keys($data_revision_schema['fields']), array_keys($base_schema['fields']))); } else { - $data_column_names = array_flip(drupal_schema_fields_sql($this->entityType->getDataTable())); + $data_schema = $this->schemaBuilder->getTableSchema($this->entityType, $field_definitions, 'data_table'); + $data_column_names = array_flip(array_keys($data_schema['fields'])); } foreach ($data as $values) { @@ -442,11 +450,14 @@ protected function buildQuery($ids, $revision_id = FALSE) { } // Add fields from the {entity} table. - $entity_fields = drupal_schema_fields_sql($this->entityType->getBaseTable()); + $field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityTypeId); + $base_schema = $this->schemaBuilder->getTableSchema($this->entityType, $field_definitions, 'base_table'); + $entity_fields = array_keys($base_schema['fields']); if ($this->revisionTable) { // Add all fields from the {entity_revision} table. - $entity_revision_fields = drupal_schema_fields_sql($this->entityType->getRevisionTable()); + $revision_schema = $this->schemaBuilder->getTableSchema($this->entityType, $field_definitions, 'revision_table'); + $entity_revision_fields = array_keys($revision_schema['fields']); $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]); @@ -726,7 +737,12 @@ protected function mapToStorageRecord(ContentEntityInterface $entity, $table_key $multi_column_fields[$field] = TRUE; continue; } - $values[$name] = isset($definitions[$name]) && isset($entity->$name->value) ? $entity->$name->value : NULL; + $value = isset($definitions[$name]) && isset($entity->$name->value) ? $entity->$name->value : NULL; + + // Since this is not a multi-column field we can assume there is only one + // column. + $serialize = !empty($definitions[$name]->getColumns()[$definitions[$name]->getMainPropertyName()]['serialize']); + $values[$name] = $serialize ? serialize($value) : $value; } // Handle fields that store multiple properties and match each property name @@ -735,9 +751,10 @@ protected function mapToStorageRecord(ContentEntityInterface $entity, $table_key /** @var \Drupal\Core\Field\FieldItemListInterface $field_items */ $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; + foreach ($field_items->getFieldDefinition()->getColumns() as $column_name => $column_info) { + if (isset($schema['fields'][$field_name . '__' . $column_name])) { + $value = isset($field_value[0][$column_name]) ? $field_value[0][$column_name] : NULL; + $values[$field_name . '__' . $column_name] = !empty($column_info['serialize']) ? serialize($value) : $value; } } } @@ -1513,7 +1530,7 @@ static public function _fieldColumnName(FieldConfigInterface $field, $column) { * {@inheritdoc} */ public function getSchema() { - return $this->schemaBuilder->getSchema($this->entityType, $this->entityManager->getBaseFieldDefinitions($this->entityTypeId)); + return $this->schemaBuilder->getAllSchema($this->entityType, $this->entityManager->getBaseFieldDefinitions($this->entityTypeId)); } } diff --git a/core/lib/Drupal/Core/Entity/Schema/EntitySchemaBuilder.php b/core/lib/Drupal/Core/Entity/Schema/EntitySchemaBuilder.php index a6ac5dc..5d39783 100644 --- a/core/lib/Drupal/Core/Entity/Schema/EntitySchemaBuilder.php +++ b/core/lib/Drupal/Core/Entity/Schema/EntitySchemaBuilder.php @@ -91,7 +91,7 @@ public function getAllSchema(ContentEntityType $entity_type, array $field_defini if (isset($tables['revision_table']) && in_array($key, array('id', 'revision', 'language'))) { $this->addFieldSchema($schema[$tables['revision_table']], $field_name, $field_definition); } - if (isset($tables['revision_table']) && in_array($key, array('id', 'revision', 'language', 'default_language'))) { + if (isset($tables['data_table']) && in_array($key, array('id', 'revision', 'language', 'default_language'))) { $this->addFieldSchema($schema[$tables['data_table']], $field_name, $field_definition); } if (isset($tables['revision_data_table']) && in_array($key, array('id', 'revision', 'language', 'default_language'))) { @@ -102,13 +102,13 @@ public function getAllSchema(ContentEntityType $entity_type, array $field_defini else { if (!isset($tables['data_table']) || !$field_definition->isTranslatable()) { $this->addFieldSchema($schema[$tables['base_table']], $field_name, $field_definition); - if ($tables['revision_table']) { + if (isset($tables['revision_table'])) { $this->addFieldSchema($schema[$tables['revision_table']], $field_name, $field_definition); } } else { $this->addFieldSchema($schema[$tables['data_table']], $field_name, $field_definition); - if ($tables['revision_data_table']) { + if (isset($tables['revision_data_table'])) { $this->addFieldSchema($schema[$tables['revision_data_table']], $field_name, $field_definition); } } diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index a20b9b9..44501ee 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -8,6 +8,9 @@ namespace Drupal\Core\Extension; use Drupal\Component\Graph\Graph; +use Drupal\Core\Entity\ContentEntityType; +use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Entity\SchemaStorageControllerInterface; use Symfony\Component\Yaml\Parser; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Cache\CacheBackendInterface; @@ -662,6 +665,25 @@ public function install(array $module_list, $enable_dependencies = TRUE) { // Clear plugin manager caches. \Drupal::getContainer()->get('plugin.cache_clearer')->clearCachedDefinitions(); + // Install entity type tables. + $entity_manager = \Drupal::entityManager(); + $schema = \Drupal::database()->schema(); + $entity_types = array_filter($entity_manager->getDefinitions(), function (EntityTypeInterface $entity_type) use ($module) { + ($entity_type instanceof ContentEntityType) && ($entity_type->getProvider() == $module); + }); + foreach ($entity_types as $entity_type) { + $storage_controller = $entity_manager->getStorageController($entity_type->id()); + if ($storage_controller instanceof SchemaStorageControllerInterface) { + foreach ($storage_controller->getSchema() as $table_name => $table_schema) { + // @todo Remove this check once all entity types have been converted + // to an automatic schema. + if (!$schema->tableExists($table_name)) { + $schema->createTable($table_name, $table_schema); + } + } + } + } + // Set the schema version to the number of the last update provided by // the module, or the minimum core schema version. $version = \Drupal::CORE_MINIMUM_SCHEMA_VERSION; @@ -766,6 +788,25 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) { // Remove the schema. drupal_uninstall_schema($module); + // Remove entity type tables. + $entity_manager = \Drupal::entityManager(); + $schema = \Drupal::database()->schema(); + $entity_types = array_filter($entity_manager->getDefinitions(), function (EntityTypeInterface $entity_type) use ($module) { + ($entity_type instanceof ContentEntityType) && ($entity_type->getProvider() == $module); + }); + foreach ($entity_types as $entity_type) { + $storage_controller = $entity_manager->getStorageController($entity_type->id()); + if ($storage_controller instanceof SchemaStorageControllerInterface) { + foreach ($storage_controller->getSchema() as $table_name => $table_schema) { + // @todo Remove this check once all entity types have been converted + // to an automatic schema. + if ($schema->tableExists($table_name)) { + $schema->dropTable($table_name, $table_schema); + } + } + } + } + // Remove the module's entry from the config. $module_config->clear("enabled.$module")->save(); diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php index 56d4839..a3a3046 100644 --- a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php +++ b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php @@ -95,8 +95,7 @@ public function setRouteName($route_name) { * {@inheritdoc} */ public function getRouteParams() { - $value = $this->get('route_parameters')->getValue(); - return reset($value); + return $this->get('route_parameters')->getValue(); } /** diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php index e3c916d..240358b 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\SchemaStorageControllerInterface; use Drupal\Core\KeyValueStore\KeyValueMemoryFactory; use Drupal\Core\Language\Language; use Symfony\Component\DependencyInjection\Reference; @@ -335,6 +337,36 @@ 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_controller = $entity_manager->getStorageController($entity_type_id); + if ($storage_controller instanceof SchemaStorageControllerInterface) { + $schema = $storage_controller->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/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php index a9f0105..5d2364c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php @@ -25,16 +25,10 @@ public static function getInfo() { public function setUp() { parent::setUp(); - $this->installSchema('entity_test', array( - 'entity_test_mul', - 'entity_test_mul_property_data', - 'entity_test_rev', - 'entity_test_rev_revision', - 'entity_test_mulrev', - 'entity_test_mulrev_revision', - 'entity_test_mulrev_property_data', - 'entity_test_mulrev_property_revision' - )); + + $this->installEntitySchema('entity_test_rev'); + $this->installEntitySchema('entity_test_mul'); + $this->installEntitySchema('entity_test_mulrev'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php index 5dcfd38..f691068 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php @@ -42,16 +42,10 @@ 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('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 1264577..3982b2f 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/EntityQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php index c01a2e2..31e16d0 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 8537fbb..15f2bed 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php @@ -44,7 +44,9 @@ public function setUp() { $this->installSchema('user', array('users', 'users_roles')); $this->installSchema('system', 'sequences'); - $this->installSchema('entity_test', 'entity_test'); + + $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 118abe9..169ab3d 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php @@ -38,16 +38,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/FieldSqlStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php index 8894088..aeb6542 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php @@ -66,8 +66,8 @@ public static function getInfo() { function setUp() { parent::setUp(); - $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision')); - $entity_type = 'entity_test_rev'; + + $this->installEntitySchema('entity_test_rev'); $this->field_name = strtolower($this->randomName()); $this->field_cardinality = 4; 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 86f0e4c..a105f95 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.install +++ b/core/modules/system/tests/modules/entity_test/entity_test.install @@ -24,12 +24,14 @@ function entity_test_install() { $storage_controller->create(array( 'name' => 'field_test_text', 'entity_type' => $entity_type_id, + 'user_id' => 1, 'type' => 'text', 'cardinality' => 1, 'translatable' => FALSE, ))->save(); $storage_controller->create(array( 'entity_type' => $entity_type_id, + 'user_id' => 1, 'field_name' => 'field_test_text', 'bundle' => $entity_type_id, 'label' => 'Test text-field', diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php index c8d151f..49512d4 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php @@ -143,6 +143,10 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setSettings(array('target_type' => 'user')) ->setTranslatable(TRUE); + $fields['field_test_text'] = FieldDefinition::create('text') + ->setLabel(t('Test text')) + ->setDescription(t('A test text field')); + return $fields; }