Exemplo n.º 1
0
 /**
  * Tests table name generation.
  */
 public function testTableNames()
 {
     // Note: we need to test entity types with long names. We therefore use
     // fields on imaginary entity types (works as long as we don't actually save
     // them), and just check the generated table names.
     // Short entity type and field name.
     $entity_type = 'short_entity_type';
     $field_name = 'short_field_name';
     $field_storage = FieldStorageConfig::create(array('entity_type' => $entity_type, 'field_name' => $field_name, 'type' => 'test_field'));
     $expected = 'short_entity_type__short_field_name';
     $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $expected);
     $expected = 'short_entity_type_revision__short_field_name';
     $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $expected);
     // Short entity type, long field name
     $entity_type = 'short_entity_type';
     $field_name = 'long_field_name_abcdefghijklmnopqrstuvwxyz';
     $field_storage = FieldStorageConfig::create(array('entity_type' => $entity_type, 'field_name' => $field_name, 'type' => 'test_field'));
     $expected = 'short_entity_type__' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
     $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $expected);
     $expected = 'short_entity_type_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
     $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $expected);
     // Long entity type, short field name
     $entity_type = 'long_entity_type_abcdefghijklmnopqrstuvwxyz';
     $field_name = 'short_field_name';
     $field_storage = FieldStorageConfig::create(array('entity_type' => $entity_type, 'field_name' => $field_name, 'type' => 'test_field'));
     $expected = 'long_entity_type_abcdefghijklmnopq__' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
     $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $expected);
     $expected = 'long_entity_type_abcdefghijklmnopq_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
     $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $expected);
     // Long entity type and field name.
     $entity_type = 'long_entity_type_abcdefghijklmnopqrstuvwxyz';
     $field_name = 'long_field_name_abcdefghijklmnopqrstuvwxyz';
     $field_storage = FieldStorageConfig::create(array('entity_type' => $entity_type, 'field_name' => $field_name, 'type' => 'test_field'));
     $expected = 'long_entity_type_abcdefghijklmnopq__' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
     $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $expected);
     $expected = 'long_entity_type_abcdefghijklmnopq_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
     $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $expected);
     // Try creating a second field and check there are no clashes.
     $field_storage2 = FieldStorageConfig::create(array('entity_type' => $entity_type, 'field_name' => $field_name . '2', 'type' => 'test_field'));
     $this->assertNotEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $this->tableMapping->getDedicatedDataTableName($field_storage2));
     $this->assertNotEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $this->tableMapping->getDedicatedRevisionTableName($field_storage2));
     // Deleted field.
     $field_storage = FieldStorageConfig::create(array('entity_type' => 'some_entity_type', 'field_name' => 'some_field_name', 'type' => 'test_field', 'deleted' => TRUE));
     $expected = 'field_deleted_data_' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
     $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage, TRUE), $expected);
     $expected = 'field_deleted_revision_' . substr(hash('sha256', $field_storage->uuid()), 0, 10);
     $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage, TRUE), $expected);
 }
Exemplo n.º 2
0
 /**
  * {@inheritdoc}
  */
 public function getTableMapping(array $storage_definitions = NULL)
 {
     $table_mapping = $this->tableMapping;
     // If we are using our internal storage definitions, which is our main use
     // case, we can statically cache the computed table mapping. If a new set
     // of field storage definitions is passed, for instance when comparing old
     // and new storage schema, we compute the table mapping without caching.
     // @todo Clean-up this in https://www.drupal.org/node/2274017 so we can
     //   easily instantiate a new table mapping whenever needed.
     if (!isset($this->tableMapping) || $storage_definitions) {
         $definitions = $storage_definitions ?: $this->entityManager->getFieldStorageDefinitions($this->entityTypeId);
         $table_mapping = new DefaultTableMapping($this->entityType, $definitions);
         $definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use($table_mapping) {
             return $table_mapping->allowsSharedTableStorage($definition);
         });
         $key_fields = array_values(array_filter(array($this->idKey, $this->revisionKey, $this->bundleKey, $this->uuidKey, $this->langcodeKey)));
         $all_fields = array_keys($definitions);
         $revisionable_fields = array_keys(array_filter($definitions, 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://www.drupal.org/node/2248983.
         $revision_metadata_fields = array_intersect(array('revision_timestamp', 'revision_uid', 'revision_log'), $all_fields);
         $revisionable = $this->entityType->isRevisionable();
         $translatable = $this->entityType->isTranslatable();
         if (!$revisionable && !$translatable) {
             // The base layout stores all the base field values in the base table.
             $table_mapping->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.
             $table_mapping->setFieldNames($this->baseTable, array_diff($all_fields, $revision_metadata_fields));
             $revision_key_fields = array($this->idKey, $this->revisionKey);
             $table_mapping->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.
             $table_mapping->setFieldNames($this->baseTable, $key_fields)->setFieldNames($this->dataTable, array_values(array_diff($all_fields, array($this->uuidKey))));
         } 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.
             $table_mapping->setFieldNames($this->baseTable, array_values($key_fields));
             // 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));
             $table_mapping->setFieldNames($this->dataTable, $data_fields);
             $revision_base_fields = array_merge(array($this->idKey, $this->revisionKey, $this->langcodeKey), $revision_metadata_fields);
             $table_mapping->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, array($this->langcodeKey));
             $table_mapping->setFieldNames($this->revisionDataTable, array_merge($revision_data_key_fields, $revision_data_fields));
         }
         // Add dedicated tables.
         $definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use($table_mapping) {
             return $table_mapping->requiresDedicatedTableStorage($definition);
         });
         $extra_columns = array('bundle', 'deleted', 'entity_id', 'revision_id', 'langcode', 'delta');
         foreach ($definitions as $field_name => $definition) {
             foreach (array($table_mapping->getDedicatedDataTableName($definition), $table_mapping->getDedicatedRevisionTableName($definition)) as $table_name) {
                 $table_mapping->setFieldNames($table_name, array($field_name));
                 $table_mapping->setExtraColumns($table_name, $extra_columns);
             }
         }
         // Cache the computed table mapping only if we are using our internal
         // storage definitions.
         if (!$storage_definitions) {
             $this->tableMapping = $table_mapping;
         }
     }
     return $table_mapping;
 }
 /**
  * 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'));
 }
 /**
  * ::onEntityTypeUpdate
  */
 public function testonEntityTypeUpdateWithNewIndex()
 {
     $this->entityType = $original_entity_type = new ContentEntityType(array('id' => 'entity_test', 'entity_keys' => array('id' => 'id')));
     // Add a field with a really long index.
     $this->setUpStorageDefinition('long_index_name', array('columns' => array('long_index_name' => array('type' => 'int')), 'indexes' => array('long_index_name_really_long_long_name' => array(array('long_index_name', 10)))));
     $expected = array('entity_test' => array('description' => 'The base table for entity_test entities.', 'fields' => array('id' => array('type' => 'serial', 'not null' => TRUE), 'long_index_name' => array('type' => 'int', 'not null' => FALSE)), 'indexes' => array('entity_test__b588603cb9' => array(array('long_index_name', 10)))));
     $this->setUpStorageSchema($expected);
     $table_mapping = new DefaultTableMapping($this->entityType, $this->storageDefinitions);
     $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
     $table_mapping->setExtraColumns('entity_test', array('default_langcode'));
     $this->storage->expects($this->any())->method('getTableMapping')->will($this->returnValue($table_mapping));
     $this->storageSchema->expects($this->any())->method('loadEntitySchemaData')->willReturn(['entity_test' => ['indexes' => ['entity_test__b588603cb9' => ['longer_index_name'], 'entity_test__removed_field' => ['removed_field']]]]);
     // The original indexes should be dropped before the new one is added.
     $this->dbSchemaHandler->expects($this->at(0))->method('dropIndex')->with('entity_test', 'entity_test__b588603cb9');
     $this->dbSchemaHandler->expects($this->at(1))->method('dropIndex')->with('entity_test', 'entity_test__removed_field');
     $this->dbSchemaHandler->expects($this->atLeastOnce())->method('fieldExists')->willReturn(TRUE);
     $this->dbSchemaHandler->expects($this->atLeastOnce())->method('addIndex')->with('entity_test', 'entity_test__b588603cb9', [['long_index_name', 10]], $this->callback(function ($actual_value) use($expected) {
         $this->assertEquals($expected['entity_test']['indexes'], $actual_value['indexes']);
         $this->assertEquals($expected['entity_test']['fields'], $actual_value['fields']);
         // If the parameters don't match, the assertions above will throw an
         // exception.
         return TRUE;
     }));
     $this->assertNull($this->storageSchema->onEntityTypeUpdate($this->entityType, $original_entity_type));
 }
 /**
  * Tests the schema for a field dedicated table for an entity with a string identifier.
  *
  * @covers ::getDedicatedTableSchema()
  * @covers ::createDedicatedTableSchema()
  */
 public function testDedicatedTableSchemaForEntityWithStringIdentifier()
 {
     $entity_type_id = 'entity_test';
     $this->entityType = new ContentEntityType(array('id' => 'entity_test', 'entity_keys' => array('id' => 'id')));
     // Setup a field having a dedicated schema.
     $field_name = $this->getRandomGenerator()->name();
     $this->setUpStorageDefinition($field_name, array('columns' => array('shape' => array('type' => 'varchar', 'length' => 32, 'not null' => FALSE), 'color' => array('type' => 'varchar', 'length' => 32, 'not null' => FALSE)), 'foreign keys' => array('color' => array('table' => 'color', 'columns' => array('color' => 'id'))), 'unique keys' => array(), 'indexes' => array()));
     $field_storage = $this->storageDefinitions[$field_name];
     $field_storage->expects($this->any())->method('getType')->will($this->returnValue('shape'));
     $field_storage->expects($this->any())->method('getTargetEntityTypeId')->will($this->returnValue($entity_type_id));
     $field_storage->expects($this->any())->method('isMultiple')->will($this->returnValue(TRUE));
     $this->storageDefinitions['id']->expects($this->any())->method('getType')->will($this->returnValue('string'));
     $expected = array($entity_type_id . '__' . $field_name => array('description' => "Data storage for {$entity_type_id} field {$field_name}.", 'fields' => array('bundle' => array('type' => 'varchar', 'length' => 128, 'not null' => true, 'default' => '', 'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance'), 'deleted' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'A boolean indicating whether this data item has been deleted'), 'entity_id' => array('type' => 'varchar', 'length' => 128, 'not null' => true, 'description' => 'The entity id this data is attached to'), 'revision_id' => array('type' => 'varchar', 'length' => 128, 'not null' => true, 'description' => 'The entity revision id this data is attached to, which for an unversioned entity type is the same as the entity id'), 'langcode' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'The language code for this data item.'), 'delta' => array('type' => 'int', 'unsigned' => true, 'not null' => true, 'description' => 'The sequence number for this data item, used for multi-value fields'), $field_name . '_shape' => array('type' => 'varchar', 'length' => 32, 'not null' => false), $field_name . '_color' => array('type' => 'varchar', 'length' => 32, 'not null' => false)), 'primary key' => array('entity_id', 'deleted', 'delta', 'langcode'), 'indexes' => array('bundle' => array('bundle'), 'deleted' => array('deleted'), 'entity_id' => array('entity_id'), 'revision_id' => array('revision_id'), 'langcode' => array('langcode')), 'foreign keys' => array($field_name . '_color' => array('table' => 'color', 'columns' => array($field_name . '_color' => 'id')))));
     $this->setUpStorageSchema($expected);
     $table_mapping = new DefaultTableMapping($this->storageDefinitions);
     $table_mapping->setFieldNames($entity_type_id, array_keys($this->storageDefinitions));
     $table_mapping->setExtraColumns($entity_type_id, array('default_langcode'));
     $this->storage->expects($this->any())->method('getTableMapping')->will($this->returnValue($table_mapping));
     $this->storageSchema->onFieldStorageDefinitionCreate($field_storage);
 }
 /**
  * Tests DefaultTableMapping::getFieldTableName() with an invalid parameter.
  *
  * @expectedException \Drupal\Core\Entity\Sql\SqlContentEntityStorageException
  * @expectedExceptionMessage Table information not available for the 'invalid_field_name' field.
  *
  * @covers ::getFieldTableName
  */
 public function testGetFieldTableNameInvalid()
 {
     $table_mapping = new DefaultTableMapping($this->entityType, []);
     $table_mapping->getFieldTableName('invalid_field_name');
 }
 /**
  * @covers ::requiresEntityStorageSchemaChanges
  *
  * @dataProvider providerTestRequiresEntityStorageSchemaChanges
  */
 public function testRequiresEntityStorageSchemaChanges(ContentEntityTypeInterface $updated, ContentEntityTypeInterface $original, $requires_change, $change_schema, $change_shared_table)
 {
     $this->entityType = new ContentEntityType(array('id' => 'entity_test', 'entity_keys' => array('id' => 'id')));
     $this->setUpStorageSchema();
     $table_mapping = new DefaultTableMapping($this->entityType, $this->storageDefinitions);
     $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
     $table_mapping->setExtraColumns('entity_test', array('default_langcode'));
     $this->storage->expects($this->any())->method('getTableMapping')->will($this->returnValue($table_mapping));
     // Setup storage schema.
     if ($change_schema) {
         $this->storageSchema->expects($this->once())->method('loadEntitySchemaData')->willReturn(array());
     } else {
         $expected = ['entity_test' => ['primary key' => ['id']]];
         $this->storageSchema->expects($this->any())->method('loadEntitySchemaData')->willReturn($expected);
     }
     if ($change_shared_table) {
         $this->storageSchema->expects($this->once())->method('hasSharedTableNameChanges')->willReturn(TRUE);
     }
     $this->assertEquals($requires_change, $this->storageSchema->requiresEntityStorageSchemaChanges($updated, $original));
 }
 /**
  * Tests the schema for revisionable, translatable entities.
  *
  * @covers ::__construct()
  * @covers ::getSchema()
  * @covers ::getTables()
  * @covers ::initializeDataTable()
  * @covers ::addTableDefaults()
  * @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'), 'unique keys' => array('entity_test__revision_id' => array('revision_id')), 'indexes' => array(), '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'), 'unique keys' => array(), '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'), 'unique keys' => array(), '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'), 'unique keys' => array(), '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);
 }