Commit 2a839b7d authored by alexpott's avatar alexpott

Issue #1823494 by Berdir, sun, Jānis Bebrītis: Field API assumes...

Issue #1823494 by Berdir, sun, Jānis Bebrītis: Field API assumes serial/integer entity IDs, but the entity system does not.
parent 754536aa
......@@ -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,
......
......@@ -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.
*
......
<?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) {
}
}
......@@ -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"
......
<?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');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment