diff --git a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php index cb8b4cba691edd010a7b9fe265e96199ce001f4b..3ad3acfe54253068ee50f1fcadeef96350ae6702 100644 --- a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php @@ -83,20 +83,6 @@ class FieldableDatabaseStorageController extends FieldableEntityStorageControlle */ protected $fieldInfo; - /** - * The entity bundle key. - * - * @var string|bool - */ - protected $bundleKey = FALSE; - - /** - * Name of the entity class. - * - * @var string - */ - protected $entityClass; - /** * {@inheritdoc} */ @@ -123,8 +109,6 @@ public function __construct(EntityTypeInterface $entity_info, Connection $databa $this->database = $database; $this->fieldInfo = $field_info; - $this->bundleKey = $this->entityInfo->getKey('bundle'); - $this->entityClass = $this->entityInfo->getClass(); // Check if the entity type supports IDs. if ($this->entityInfo->hasKey('id')) { @@ -151,46 +135,6 @@ public function __construct(EntityTypeInterface $entity_info, Connection $databa } } - /** - * {@inheritdoc} - */ - public function create(array $values) { - $entity_class = $this->entityClass; - $entity_class::preCreate($this, $values); - - // We have to determine the bundle first. - $bundle = FALSE; - if ($this->bundleKey) { - if (!isset($values[$this->bundleKey])) { - throw new EntityStorageException(format_string('Missing bundle for entity type @type', array('@type' => $this->entityType))); - } - $bundle = $values[$this->bundleKey]; - } - $entity = new $entity_class(array(), $this->entityType, $bundle); - - foreach ($entity as $name => $field) { - if (isset($values[$name])) { - $entity->$name = $values[$name]; - } - elseif (!array_key_exists($name, $values)) { - $entity->get($name)->applyDefaultValue(); - } - unset($values[$name]); - } - - // Set any passed values for non-defined fields also. - foreach ($values as $name => $value) { - $entity->$name = $value; - } - $entity->postCreate($this); - - // Modules might need to add or change the data initially held by the new - // entity object, for instance to fill-in default values. - $this->invokeHook('create', $entity); - - return $entity; - } - /** * {@inheritdoc} */ @@ -1243,6 +1187,50 @@ public static function _fieldSqlSchema(FieldInterface $field, array $schema = NU $description_revision = "Revision archive storage for {$field->entity_type} field {$field->getName()}."; } + $entity_type = $field->entity_type; + $entity_manager = \Drupal::entityManager(); + $info = $entity_manager->getDefinition($entity_type); + $definitions = $entity_manager->getFieldDefinitions($entity_type); + + // Define the entity ID schema based on the field definitions. + $id_definition = $definitions[$info->getKey('id')]; + if ($id_definition->getType() == 'integer') { + $id_schema = array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'The entity id this data is attached to', + ); + } + else { + $id_schema = array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'description' => 'The entity id this data is attached to', + ); + } + + // Define the revision ID schema, default to integer if there is no revision + // ID. + $revision_id_definition = $info->hasKey('revision_id') ? $definitions[$info->getKey('revision_id')] : NULL; + if (!$revision_id_definition || $revision_id_definition->getType() == 'integer') { + $revision_id_schema = array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned', + ); + } + else { + $revision_id_schema = array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + 'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned', + ); + } + $current = array( 'description' => $description_current, 'fields' => array( @@ -1260,18 +1248,8 @@ public static function _fieldSqlSchema(FieldInterface $field, array $schema = NU 'default' => 0, 'description' => 'A boolean indicating whether this data item has been deleted' ), - 'entity_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'The entity id this data is attached to', - ), - 'revision_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned', - ), + 'entity_id' => $id_schema, + 'revision_id' => $revision_id_schema, 'langcode' => array( 'type' => 'varchar', 'length' => 32, diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php index 9aa3a63986dd1089980a3fbb5f2e8e4fab953185..02755974d55124c64f9e4e60f53f86cde5e7daf8 100644 --- a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php +++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php @@ -7,14 +7,91 @@ namespace Drupal\Core\Entity; +use Drupal\Component\Utility\String; use Drupal\Core\Field\PrepareCacheInterface; use Drupal\field\FieldInterface; use Drupal\field\FieldInstanceInterface; use Drupal\Core\Field\ConfigFieldItemListInterface; -use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerInterface; abstract class FieldableEntityStorageControllerBase extends EntityStorageControllerBase implements FieldableEntityStorageControllerInterface { + /** + * The entity bundle key. + * + * @var string|bool + */ + protected $bundleKey = FALSE; + + /** + * Name of the entity class. + * + * @var string + */ + protected $entityClass; + + /** + * Constructs a FieldableEntityStorageControllerBase object. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info + * The entity info for the entity type. + */ + public function __construct(EntityTypeInterface $entity_info) { + parent::__construct($entity_info); + + $this->bundleKey = $this->entityInfo->getKey('bundle'); + $this->entityClass = $this->entityInfo->getClass(); + } + + /** + * {@inheritdoc} + */ + public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) { + return new static( + $entity_info + ); + } + + /** + * {@inheritdoc} + */ + public function create(array $values) { + $entity_class = $this->entityInfo->getClass(); + $entity_class::preCreate($this, $values); + + // We have to determine the bundle first. + $bundle = FALSE; + if ($this->bundleKey) { + if (!isset($values[$this->bundleKey])) { + throw new EntityStorageException(String::format('Missing bundle for entity type @type', array('@type' => $this->entityType))); + } + $bundle = $values[$this->bundleKey]; + } + $entity = new $entity_class(array(), $this->entityType, $bundle); + + foreach ($entity as $name => $field) { + if (isset($values[$name])) { + $entity->$name = $values[$name]; + } + elseif (!array_key_exists($name, $values)) { + $entity->get($name)->applyDefaultValue(); + } + unset($values[$name]); + } + + // Set any passed values for non-defined fields also. + foreach ($values as $name => $value) { + $entity->$name = $value; + } + $entity->postCreate($this); + + // Modules might need to add or change the data initially held by the new + // entity object, for instance to fill-in default values. + $this->invokeHook('create', $entity); + + return $entity; + } + /** * Loads values of configurable fields for a group of entities. * diff --git a/core/lib/Drupal/Core/Entity/FieldableNullStorageController.php b/core/lib/Drupal/Core/Entity/FieldableNullStorageController.php new file mode 100644 index 0000000000000000000000000000000000000000..6f0288473332e9aab9d0e9f49db8ed9b035ca91a --- /dev/null +++ b/core/lib/Drupal/Core/Entity/FieldableNullStorageController.php @@ -0,0 +1,109 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Entity\FieldableNullStorageController. + */ + +namespace Drupal\Core\Entity; + +use Drupal\Core\Entity\Query\QueryException; +use Drupal\field\FieldInstanceInterface; + +/** + * Defines a null entity controller class. + * + * Used for content entity types that have no storage. + */ +class FieldableNullStorageController extends FieldableEntityStorageControllerBase { + + /** + * {@inheritdoc} + */ + public function loadMultiple(array $ids = NULL) { + return array(); + } + + /** + * {@inheritdoc} + */ + public function load($id) { + return NULL; + } + + /** + * {@inheritdoc} + */ + public function loadRevision($revision_id) { + return NULL; + } + + /** + * {@inheritdoc} + */ + public function deleteRevision($revision_id) { + } + + /** + * {@inheritdoc} + */ + public function loadByProperties(array $values = array()) { + return array(); + } + + /** + * {@inheritdoc} + */ + public function delete(array $entities) { + } + + /** + * {@inheritdoc} + */ + public function save(EntityInterface $entity) { + } + + /** + * {@inheritdoc} + */ + public function getQueryServiceName() { + throw new QueryException('Null implementation can not be queried.'); + } + + /** + * {@inheritdoc} + */ + protected function doLoadFieldItems($entities, $age) { + } + + /** + * {@inheritdoc} + */ + protected function doSaveFieldItems(EntityInterface $entity, $update) { + } + + /** + * {@inheritdoc} + */ + protected function doDeleteFieldItems(EntityInterface $entity) { + } + + /** + * {@inheritdoc} + */ + protected function doDeleteFieldItemsRevision(EntityInterface $entity) { + } + + /** + * {@inheritdoc} + */ + protected function readFieldItemsToPurge(EntityInterface $entity, FieldInstanceInterface $instance) { + } + + /** + * {@inheritdoc} + */ + protected function purgeFieldItems(EntityInterface $entity, FieldInstanceInterface $instance) { + } + +} diff --git a/core/modules/contact/lib/Drupal/contact/Entity/Message.php b/core/modules/contact/lib/Drupal/contact/Entity/Message.php index f12bbf4628ca731ab182d8e05a5507e2859755a0..b8a1523c12df3dc9f7b4032311e0e9d934db4e25 100644 --- a/core/modules/contact/lib/Drupal/contact/Entity/Message.php +++ b/core/modules/contact/lib/Drupal/contact/Entity/Message.php @@ -18,7 +18,7 @@ * id = "contact_message", * label = @Translation("Contact message"), * controllers = { - * "storage" = "Drupal\Core\Entity\FieldableDatabaseStorageController", + * "storage" = "Drupal\Core\Entity\FieldableNullStorageController", * "view_builder" = "Drupal\contact\MessageViewBuilder", * "form" = { * "default" = "Drupal\contact\MessageFormController" diff --git a/core/tests/Drupal/Tests/Core/Entity/FieldableDatabaseStorageControllerTest.php b/core/tests/Drupal/Tests/Core/Entity/FieldableDatabaseStorageControllerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..81dd807dbba691fe09a83f347e66f5c905b5f50b --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Entity/FieldableDatabaseStorageControllerTest.php @@ -0,0 +1,104 @@ +<?php + +/** + * @file + * Contains \Drupal\Tests\Core\Entity\FieldableDatabaseStorageControllerTest. + */ + +namespace Drupal\Tests\Core\Entity; + +use Drupal\Core\Entity\EntityType; +use Drupal\Core\Entity\FieldableDatabaseStorageController; +use Drupal\Core\Field\FieldDefinition; +use Drupal\Tests\UnitTestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Tests the fieldable database storage controller. + * + * @see \Drupal\Core\Entity\FieldableDatabaseStorageController + * + * @group Drupal + * @group Entity + */ +class FieldableDatabaseStorageControllerTest extends UnitTestCase { + + /** + * {@inheritdoc} + */ + public static function getInfo() { + return array( + 'name' => 'Fieldable database storage controller', + 'description' => 'Tests the fieldable database storage enhancer for entities.', + 'group' => 'Entity' + ); + } + + /** + * Tests field SQL schema generation for an entity with a string identifier. + * + * @see \Drupal\Core\Entity\Controller\FieldableDatabaseStorageController::_fieldSqlSchema() + */ + public function testFieldSqlSchemaForEntityWithStringIdentifier() { + + // Mock the entity manager to return the minimal entity and field + // definitions for the test_entity entity. + $definition = new EntityType(array( + 'entity_keys' => array( + 'id' => 'id', + 'revision_id' => 'revision_id', + ), + )); + $fields['id'] = FieldDefinition::create('string') + ->setName('id'); + $fields['revision_id'] = FieldDefinition::create('string') + ->setName('revision_id'); + + $entity_manager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $entity_manager->expects($this->any()) + ->method('getDefinition') + ->with('test_entity') + ->will($this->returnValue($definition)); + + $entity_manager->expects($this->any()) + ->method('getFieldDefinitions') + ->with('test_entity') + ->will($this->returnValue($fields)); + + $container = new ContainerBuilder(); + $container->set('entity.manager', $entity_manager); + \Drupal::setContainer($container); + + // Define a field definition for a test_field field. + $field = $this->getMock('\Drupal\field\FieldInterface'); + $field->deleted = FALSE; + $field->entity_type = 'test_entity'; + $field->name = 'test_field'; + + $field->expects($this->any()) + ->method('getName') + ->will($this->returnValue('test')); + + $field_schema = array( + 'columns' => array( + 'value' => array( + 'type' => 'varchar', + 'length' => 10, + 'not null' => FALSE, + ), + ), + 'indexes' => array(), + 'foreign keys' => array(), + ); + $field->expects($this->any()) + ->method('getSchema') + ->will($this->returnValue($field_schema)); + + $schema = FieldableDatabaseStorageController::_fieldSqlSchema($field); + + // Make sure that the entity_id schema field if of type varchar. + $this->assertEquals($schema['test_entity__test_field']['fields']['entity_id']['type'], 'varchar'); + $this->assertEquals($schema['test_entity__test_field']['fields']['revision_id']['type'], 'varchar'); + } + +}