Issue #2208617 by tim.plunkett: Add key value entity storage.
parent
773a992920
commit
95c69f5b9b
|
@ -364,6 +364,9 @@ services:
|
|||
entity.query.sql:
|
||||
class: Drupal\Core\Entity\Query\Sql\QueryFactory
|
||||
arguments: ['@database']
|
||||
entity.query.keyvalue:
|
||||
class: Drupal\Core\Entity\KeyValueStore\Query\QueryFactory
|
||||
arguments: ['@keyvalue']
|
||||
router.dumper:
|
||||
class: Drupal\Core\Routing\MatcherDumper
|
||||
arguments: ['@database', '@state']
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Entity\KeyValueStore;
|
||||
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Component\Uuid\UuidInterface;
|
||||
use Drupal\Core\Config\Entity\Exception\ConfigEntityIdLengthException;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityMalformedException;
|
||||
use Drupal\Core\Entity\EntityStorageBase;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a key value backend for entities.
|
||||
*
|
||||
* @todo Entities that depend on auto-incrementing serial IDs need to explicitly
|
||||
* provide an ID until a generic wrapper around the functionality provided by
|
||||
* \Drupal\Core\Database\Connection::nextId() is added and used.
|
||||
* @todo Revisions are currently not supported.
|
||||
*/
|
||||
class KeyValueEntityStorage extends EntityStorageBase {
|
||||
|
||||
/**
|
||||
* Length limit of the entity ID.
|
||||
*/
|
||||
const MAX_ID_LENGTH = 128;
|
||||
|
||||
/**
|
||||
* The key value store.
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
|
||||
*/
|
||||
protected $keyValueStore;
|
||||
|
||||
/**
|
||||
* The UUID service.
|
||||
*
|
||||
* @var \Drupal\Component\Uuid\UuidInterface
|
||||
*/
|
||||
protected $uuidService;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Constructs a new KeyValueEntityStorage.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
* @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $key_value_store
|
||||
* The key value store.
|
||||
* @param \Drupal\Component\Uuid\UuidInterface $uuid_service
|
||||
* The UUID service.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, KeyValueStoreInterface $key_value_store, UuidInterface $uuid_service, LanguageManagerInterface $language_manager) {
|
||||
parent::__construct($entity_type);
|
||||
$this->keyValueStore = $key_value_store;
|
||||
$this->uuidService = $uuid_service;
|
||||
$this->languageManager = $language_manager;
|
||||
|
||||
// Check if the entity type supports UUIDs.
|
||||
$this->uuidKey = $this->entityType->getKey('uuid');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('keyvalue')->get('entity_storage__' . $entity_type->id()),
|
||||
$container->get('uuid'),
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function doCreate(array $values = array()) {
|
||||
// Set default language to site default if not provided.
|
||||
$values += array('langcode' => $this->languageManager->getDefaultLanguage()->id);
|
||||
$entity = new $this->entityClass($values, $this->entityTypeId);
|
||||
|
||||
// @todo This is handled by ContentEntityStorageBase, which assumes
|
||||
// ContentEntityInterface. The current approach in
|
||||
// https://drupal.org/node/1867228 improves this but does not solve it
|
||||
// completely.
|
||||
if ($entity instanceof ContentEntityInterface) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function doLoadMultiple(array $ids = NULL) {
|
||||
if (empty($ids)) {
|
||||
$entities = $this->keyValueStore->getAll();
|
||||
}
|
||||
else {
|
||||
$entities = $this->keyValueStore->getMultiple($ids);
|
||||
}
|
||||
return $this->mapFromStorageRecords($entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadRevision($revision_id) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteRevision($revision_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function doDelete($entities) {
|
||||
$entity_ids = array();
|
||||
foreach ($entities as $entity) {
|
||||
$entity_ids[] = $entity->id();
|
||||
}
|
||||
$this->keyValueStore->deleteMultiple($entity_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(EntityInterface $entity) {
|
||||
$id = $entity->id();
|
||||
if ($id === NULL || $id === '') {
|
||||
throw new EntityMalformedException('The entity does not have an ID.');
|
||||
}
|
||||
|
||||
// Check the entity ID length.
|
||||
// @todo This is not config-specific, but serial IDs will likely never hit
|
||||
// this limit. Consider renaming the exception class.
|
||||
if (strlen($entity->id()) > static::MAX_ID_LENGTH) {
|
||||
throw new ConfigEntityIdLengthException(String::format('Entity ID @id exceeds maximum allowed length of @length characters.', array(
|
||||
'@id' => $entity->id(),
|
||||
'@length' => static::MAX_ID_LENGTH,
|
||||
)));
|
||||
}
|
||||
return parent::save($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave($id, EntityInterface $entity) {
|
||||
$is_new = $entity->isNew();
|
||||
|
||||
// Save the entity data in the key value store.
|
||||
$this->keyValueStore->set($entity->id(), $entity->toArray());
|
||||
|
||||
// If this is a rename, delete the original entity.
|
||||
if ($this->has($id, $entity) && $id !== $entity->id()) {
|
||||
$this->keyValueStore->delete($id);
|
||||
}
|
||||
|
||||
return $is_new ? SAVED_NEW : SAVED_UPDATED;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function has($id, EntityInterface $entity) {
|
||||
return $this->keyValueStore->has($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQueryServicename() {
|
||||
return 'entity.query.keyvalue';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Entity\KeyValueStore\Query\Condition.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Entity\KeyValueStore\Query;
|
||||
|
||||
use Drupal\Core\Config\Entity\Query\Condition as ConditionParent;
|
||||
|
||||
/**
|
||||
* Defines the condition class for the key value entity query.
|
||||
*/
|
||||
class Condition extends ConditionParent {
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Entity\KeyValueStore\Query\Query.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Entity\KeyValueStore\Query;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\Query\QueryBase;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
|
||||
|
||||
/**
|
||||
* Defines the entity query for entities stored in a key value backend.
|
||||
*/
|
||||
class Query extends QueryBase {
|
||||
|
||||
/**
|
||||
* The key value factory.
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
|
||||
*/
|
||||
protected $keyValueFactory;
|
||||
|
||||
/**
|
||||
* Constructs a new Query.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
* @param string $conjunction
|
||||
* - AND: all of the conditions on the query need to match.
|
||||
* - OR: at least one of the conditions on the query need to match.
|
||||
* @param array $namespaces
|
||||
* List of potential namespaces of the classes belonging to this query.
|
||||
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
|
||||
* The key value factory.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, $conjunction, array $namespaces, KeyValueFactoryInterface $key_value_factory) {
|
||||
parent::__construct($entity_type, $conjunction, $namespaces);
|
||||
$this->keyValueFactory = $key_value_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute() {
|
||||
// Load the relevant records.
|
||||
$records = $this->keyValueFactory->get('entity_storage__' . $this->entityTypeId)->getAll();
|
||||
|
||||
// Apply conditions.
|
||||
$result = $this->condition->compile($records);
|
||||
|
||||
// Apply sort settings.
|
||||
foreach ($this->sort as $sort) {
|
||||
$direction = $sort['direction'] == 'ASC' ? -1 : 1;
|
||||
$field = $sort['field'];
|
||||
uasort($result, function($a, $b) use ($field, $direction) {
|
||||
return ($a[$field] <= $b[$field]) ? $direction : -$direction;
|
||||
});
|
||||
}
|
||||
|
||||
// Let the pager do its work.
|
||||
$this->initializePager();
|
||||
|
||||
if ($this->range) {
|
||||
$result = array_slice($result, $this->range['start'], $this->range['length'], TRUE);
|
||||
}
|
||||
if ($this->count) {
|
||||
return count($result);
|
||||
}
|
||||
|
||||
// Create the expected structure of entity_id => entity_id.
|
||||
$entity_ids = array_keys($result);
|
||||
return array_combine($entity_ids, $entity_ids);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Entity\KeyValueStore\Query\QueryFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Entity\KeyValueStore\Query;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\Query\QueryException;
|
||||
use Drupal\Core\Entity\Query\QueryFactoryInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
|
||||
|
||||
/**
|
||||
* Provides a factory for creating the key value entity query.
|
||||
*/
|
||||
class QueryFactory implements QueryFactoryInterface {
|
||||
|
||||
/**
|
||||
* The key value factory.
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
|
||||
*/
|
||||
protected $keyValueFactory;
|
||||
|
||||
/**
|
||||
* The namespace of this class, the parent class etc.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $namespaces;
|
||||
|
||||
/**
|
||||
* Constructs a QueryFactory object.
|
||||
*
|
||||
*/
|
||||
public function __construct(KeyValueFactoryInterface $key_value_factory) {
|
||||
$this->keyValueFactory = $key_value_factory;
|
||||
$this->namespaces = Query::getNamespaces($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get(EntityTypeInterface $entity_type, $conjunction) {
|
||||
return new Query($entity_type, $conjunction, $this->namespaces, $this->keyValueFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAggregate(EntityTypeInterface $entity_type, $conjunction) {
|
||||
throw new QueryException('Aggregation over key-value entity storage is not supported');
|
||||
}
|
||||
|
||||
}
|
|
@ -20,6 +20,11 @@ use Drupal\simpletest\WebTestBase;
|
|||
*/
|
||||
class ConfigEntityTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* The maximum length for the entity storage used in this test.
|
||||
*/
|
||||
const MAX_ID_LENGTH = ConfigEntityStorage::MAX_ID_LENGTH;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
|
@ -164,7 +169,7 @@ class ConfigEntityTest extends WebTestBase {
|
|||
|
||||
// Test with an ID of the maximum allowed length.
|
||||
$id_length_config_test = entity_create('config_test', array(
|
||||
'id' => $this->randomName(ConfigEntityStorage::MAX_ID_LENGTH),
|
||||
'id' => $this->randomName(static::MAX_ID_LENGTH),
|
||||
));
|
||||
try {
|
||||
$id_length_config_test->save();
|
||||
|
@ -178,19 +183,19 @@ class ConfigEntityTest extends WebTestBase {
|
|||
|
||||
// Test with an ID exeeding the maximum allowed length.
|
||||
$id_length_config_test = entity_create('config_test', array(
|
||||
'id' => $this->randomName(ConfigEntityStorage::MAX_ID_LENGTH + 1),
|
||||
'id' => $this->randomName(static::MAX_ID_LENGTH + 1),
|
||||
));
|
||||
try {
|
||||
$status = $id_length_config_test->save();
|
||||
$this->fail(String::format("config_test entity with ID length @length exceeding the maximum allowed length of @max saved successfully", array(
|
||||
'@length' => strlen($id_length_config_test->id),
|
||||
'@max' => ConfigEntityStorage::MAX_ID_LENGTH,
|
||||
'@max' => static::MAX_ID_LENGTH,
|
||||
)));
|
||||
}
|
||||
catch (ConfigEntityIdLengthException $e) {
|
||||
$this->pass(String::format("config_test entity with ID length @length exceeding the maximum allowed length of @max failed to save", array(
|
||||
'@length' => strlen($id_length_config_test->id),
|
||||
'@max' => ConfigEntityStorage::MAX_ID_LENGTH,
|
||||
'@max' => static::MAX_ID_LENGTH,
|
||||
)));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\KeyValueStore\KeyValueConfigEntityStorageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\KeyValueStore;
|
||||
|
||||
use Drupal\config\Tests\ConfigEntityTest;
|
||||
use Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage;
|
||||
|
||||
/**
|
||||
* Tests config entity CRUD with key value entity storage.
|
||||
*/
|
||||
class KeyValueConfigEntityStorageTest extends ConfigEntityTest {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
const MAX_ID_LENGTH = KeyValueEntityStorage::MAX_ID_LENGTH;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('keyvalue_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'KeyValueEntityStorage config entity test',
|
||||
'description' => 'Tests KeyValueEntityStorage for config entities.',
|
||||
'group' => 'Entity API',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\KeyValueStore\KeyValueContentEntityStorageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\KeyValueStore;
|
||||
|
||||
use Drupal\Core\Entity\EntityMalformedException;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\simpletest\DrupalUnitTestBase;
|
||||
|
||||
/**
|
||||
* Tests content entity CRUD with key value entity storage.
|
||||
*/
|
||||
class KeyValueContentEntityStorageTest extends DrupalUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity', 'user', 'entity_test', 'keyvalue_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'KeyValueEntityStorage content entity test',
|
||||
'description' => 'Tests KeyValueEntityStorage for content entities.',
|
||||
'group' => 'Entity API',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CRUD operations.
|
||||
*/
|
||||
function testCRUD() {
|
||||
$default_langcode = language_default()->id;
|
||||
// Verify default properties on a newly created empty entity.
|
||||
$empty = entity_create('entity_test_label');
|
||||
$this->assertIdentical($empty->id->value, NULL);
|
||||
$this->assertIdentical($empty->name->value, NULL);
|
||||
$this->assertTrue($empty->uuid->value);
|
||||
$this->assertIdentical($empty->langcode->value, $default_langcode);
|
||||
|
||||
// Verify ConfigEntity properties/methods on the newly created empty entity.
|
||||
$this->assertIdentical($empty->isNew(), TRUE);
|
||||
$this->assertIdentical($empty->bundle(), 'entity_test_label');
|
||||
$this->assertIdentical($empty->id(), NULL);
|
||||
$this->assertTrue($empty->uuid());
|
||||
$this->assertIdentical($empty->label(), NULL);
|
||||
|
||||
// Verify Entity properties/methods on the newly created empty entity.
|
||||
$this->assertIdentical($empty->getEntityTypeId(), 'entity_test_label');
|
||||
// The URI can only be checked after saving.
|
||||
try {
|
||||
$empty->urlInfo();
|
||||
$this->fail('EntityMalformedException was thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->pass('EntityMalformedException was thrown.');
|
||||
}
|
||||
|
||||
// Verify that an empty entity cannot be saved.
|
||||
try {
|
||||
$empty->save();
|
||||
$this->fail('EntityMalformedException was thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->pass('EntityMalformedException was thrown.');
|
||||
}
|
||||
|
||||
// Verify that an entity with an empty ID string is considered empty, too.
|
||||
$empty_id = entity_create('entity_test_label', array(
|
||||
'id' => '',
|
||||
));
|
||||
$this->assertIdentical($empty_id->isNew(), TRUE);
|
||||
try {
|
||||
$empty_id->save();
|
||||
$this->fail('EntityMalformedException was thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->pass('EntityMalformedException was thrown.');
|
||||
}
|
||||
|
||||
// Verify properties on a newly created entity.
|
||||
$entity_test = entity_create('entity_test_label', $expected = array(
|
||||
'id' => $this->randomName(),
|
||||
'name' => $this->randomString(),
|
||||
));
|
||||
$this->assertIdentical($entity_test->id->value, $expected['id']);
|
||||
$this->assertTrue($entity_test->uuid->value);
|
||||
$this->assertNotEqual($entity_test->uuid->value, $empty->uuid->value);
|
||||
$this->assertIdentical($entity_test->name->value, $expected['name']);
|
||||
$this->assertIdentical($entity_test->langcode->value, $default_langcode);
|
||||
|
||||
// Verify methods on the newly created entity.
|
||||
$this->assertIdentical($entity_test->isNew(), TRUE);
|
||||
$this->assertIdentical($entity_test->id(), $expected['id']);
|
||||
$this->assertTrue($entity_test->uuid());
|
||||
$expected['uuid'] = $entity_test->uuid();
|
||||
$this->assertIdentical($entity_test->label(), $expected['name']);
|
||||
|
||||
// Verify that the entity can be saved.
|
||||
try {
|
||||
$status = $entity_test->save();
|
||||
$this->pass('EntityMalformedException was not thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->fail('EntityMalformedException was not thrown.');
|
||||
}
|
||||
|
||||
// Verify that the correct status is returned and properties did not change.
|
||||
$this->assertIdentical($status, SAVED_NEW);
|
||||
$this->assertIdentical($entity_test->id(), $expected['id']);
|
||||
$this->assertIdentical($entity_test->uuid(), $expected['uuid']);
|
||||
$this->assertIdentical($entity_test->label(), $expected['name']);
|
||||
$this->assertIdentical($entity_test->isNew(), FALSE);
|
||||
|
||||
// Save again, and verify correct status and properties again.
|
||||
$status = $entity_test->save();
|
||||
$this->assertIdentical($status, SAVED_UPDATED);
|
||||
$this->assertIdentical($entity_test->id(), $expected['id']);
|
||||
$this->assertIdentical($entity_test->uuid(), $expected['uuid']);
|
||||
$this->assertIdentical($entity_test->label(), $expected['name']);
|
||||
$this->assertIdentical($entity_test->isNew(), FALSE);
|
||||
|
||||
// Ensure that creating an entity with the same id as an existing one is not
|
||||
// possible.
|
||||
$same_id = entity_create('entity_test_label', array(
|
||||
'id' => $entity_test->id(),
|
||||
));
|
||||
$this->assertIdentical($same_id->isNew(), TRUE);
|
||||
try {
|
||||
$same_id->save();
|
||||
$this->fail('Not possible to overwrite an entity entity.');
|
||||
} catch (EntityStorageException $e) {
|
||||
$this->pass('Not possible to overwrite an entity entity.');
|
||||
}
|
||||
|
||||
// Verify that renaming the ID returns correct status and properties.
|
||||
$ids = array($expected['id'], 'second_' . $this->randomName(4), 'third_' . $this->randomName(4));
|
||||
for ($i = 1; $i < 3; $i++) {
|
||||
$old_id = $ids[$i - 1];
|
||||
$new_id = $ids[$i];
|
||||
// Before renaming, everything should point to the current ID.
|
||||
$this->assertIdentical($entity_test->id(), $old_id);
|
||||
|
||||
// Rename.
|
||||
$entity_test->id = $new_id;
|
||||
$this->assertIdentical($entity_test->id(), $new_id);
|
||||
$status = $entity_test->save();
|
||||
$this->assertIdentical($status, SAVED_UPDATED);
|
||||
$this->assertIdentical($entity_test->isNew(), FALSE);
|
||||
|
||||
// Verify that originalID points to new ID directly after renaming.
|
||||
$this->assertIdentical($entity_test->id(), $new_id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
name: 'KeyValue tests'
|
||||
type: module
|
||||
description: 'A support module to test key value storage.'
|
||||
core: 8.x
|
||||
package: Testing
|
||||
version: VERSION
|
||||
hidden: true
|
||||
dependencies:
|
||||
- config_test
|
||||
- entity_test
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sets up the key value entity storage.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_type_alter().
|
||||
*/
|
||||
function keyvalue_test_entity_type_alter(array &$entity_types) {
|
||||
/** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
|
||||
if (isset($entity_types['config_test'])) {
|
||||
$entity_types['config_test']->setStorageClass('Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage');
|
||||
}
|
||||
if (isset($entity_types['entity_test_label'])) {
|
||||
$entity_types['entity_test_label']->setStorageClass('Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage');
|
||||
$entity_keys = $entity_types['entity_test_label']->getKeys();
|
||||
$entity_types['entity_test_label']->set('entity_keys', $entity_keys + array('uuid' => 'uuid'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,720 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\Entity\KeyValueStore\KeyValueEntityStorageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Entity\KeyValueStore {
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage
|
||||
*
|
||||
* @group Drupal
|
||||
* @group KeyValueEntityStorage
|
||||
*/
|
||||
class KeyValueEntityStorageTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The entity type.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* The key value store.
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $keyValueStore;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The UUID service.
|
||||
*
|
||||
* @var \Drupal\Component\Uuid\UuidInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $uuidService;
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage
|
||||
*/
|
||||
protected $entityStorage;
|
||||
|
||||
/**
|
||||
* The mocked entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The mocked cache backend.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $cacheBackend;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'KeyValueEntityStorage',
|
||||
'description' => 'Tests \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage',
|
||||
'group' => 'Entity',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->entityType = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the key value entity storage.
|
||||
*
|
||||
* @covers ::__construct()
|
||||
*
|
||||
* @param string $uuid_key
|
||||
* (optional) The entity key used for the UUID. Defaults to 'uuid'.
|
||||
*/
|
||||
protected function setUpKeyValueEntityStorage($uuid_key = 'uuid') {
|
||||
$this->entityType->expects($this->atLeastOnce())
|
||||
->method('getKey')
|
||||
->will($this->returnValueMap(array(
|
||||
array('id', 'id'),
|
||||
array('uuid', $uuid_key),
|
||||
)));
|
||||
$this->entityType->expects($this->atLeastOnce())
|
||||
->method('id')
|
||||
->will($this->returnValue('test_entity_type'));
|
||||
|
||||
$this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
|
||||
$this->entityManager->expects($this->any())
|
||||
->method('getDefinition')
|
||||
->with('test_entity_type')
|
||||
->will($this->returnValue($this->entityType));
|
||||
|
||||
$this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
|
||||
|
||||
$this->keyValueStore = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
|
||||
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
|
||||
$this->uuidService = $this->getMock('Drupal\Component\Uuid\UuidInterface');
|
||||
$this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
|
||||
$this->languageManager->expects($this->any())
|
||||
->method('getDefaultLanguage')
|
||||
->will($this->returnValue(new Language(array('langcode' => 'en'))));
|
||||
|
||||
$this->entityStorage = new KeyValueEntityStorage($this->entityType, $this->keyValueStore, $this->uuidService, $this->languageManager);
|
||||
$this->entityStorage->setModuleHandler($this->moduleHandler);
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('entity.manager', $this->entityManager);
|
||||
$container->set('language_manager', $this->languageManager);
|
||||
$container->set('cache.test', $this->cacheBackend);
|
||||
$container->setParameter('cache_bins', array('cache.test' => 'test'));
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::create()
|
||||
* @covers ::doCreate()
|
||||
*/
|
||||
public function testCreateWithPredefinedUuid() {
|
||||
$this->entityType->expects($this->once())
|
||||
->method('getClass')
|
||||
->will($this->returnValue(get_class($this->getMockEntity())));
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$this->moduleHandler->expects($this->at(0))
|
||||
->method('invokeAll')
|
||||
->with('test_entity_type_create');
|
||||
$this->moduleHandler->expects($this->at(1))
|
||||
->method('invokeAll')
|
||||
->with('entity_create');
|
||||
$this->uuidService->expects($this->never())
|
||||
->method('generate');
|
||||
|
||||
$entity = $this->entityStorage->create(array('id' => 'foo', 'uuid' => 'baz'));
|
||||
$this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
|
||||
$this->assertSame('foo', $entity->id());
|
||||
$this->assertSame('baz', $entity->uuid());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::create()
|
||||
* @covers ::doCreate()
|
||||
*/
|
||||
public function testCreateWithoutUuidKey() {
|
||||
// Set up the entity storage to expect no UUID key.
|
||||
$this->entityType->expects($this->once())
|
||||
->method('getClass')
|
||||
->will($this->returnValue(get_class($this->getMockEntity())));
|
||||
$this->setUpKeyValueEntityStorage(NULL);
|
||||
|
||||
$this->moduleHandler->expects($this->at(0))
|
||||
->method('invokeAll')
|
||||
->with('test_entity_type_create');
|
||||
$this->moduleHandler->expects($this->at(1))
|
||||
->method('invokeAll')
|
||||
->with('entity_create');
|
||||
$this->uuidService->expects($this->never())
|
||||
->method('generate');
|
||||
|
||||
$entity = $this->entityStorage->create(array('id' => 'foo', 'uuid' => 'baz'));
|
||||
$this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
|
||||
$this->assertSame('foo', $entity->id());
|
||||
$this->assertSame('baz', $entity->uuid());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::create()
|
||||
* @covers ::doCreate()
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
*/
|
||||
public function testCreate() {
|
||||
$entity = $this->getMockEntity('Drupal\Core\Entity\Entity', array(), array('toArray'));
|
||||
$this->entityType->expects($this->once())
|
||||
->method('getClass')
|
||||
->will($this->returnValue(get_class($entity)));
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$this->moduleHandler->expects($this->at(0))
|
||||
->method('invokeAll')
|
||||
->with('test_entity_type_create');
|
||||
$this->moduleHandler->expects($this->at(1))
|
||||
->method('invokeAll')
|
||||
->with('entity_create');
|
||||
$this->uuidService->expects($this->once())
|
||||
->method('generate')
|
||||
->will($this->returnValue('bar'));
|
||||
|
||||
$entity = $this->entityStorage->create(array('id' => 'foo'));
|
||||
$this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
|
||||
$this->assertSame('foo', $entity->id());
|
||||
$this->assertSame('bar', $entity->uuid());
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::save()
|
||||
* @covers ::doSave()
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
*
|
||||
* @depends testCreate
|
||||
*/
|
||||
public function testSaveInsert(EntityInterface $entity) {
|
||||
$this->entityType->expects($this->once())
|
||||
->method('getClass')
|
||||
->will($this->returnValue(get_class($entity)));
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$expected = array('id' => 'foo');
|
||||
$this->keyValueStore->expects($this->exactly(2))
|
||||
->method('has')
|
||||
->with('foo')
|
||||
->will($this->returnValue(FALSE));
|
||||
$this->keyValueStore->expects($this->never())
|
||||
->method('getMultiple');
|
||||
$this->keyValueStore->expects($this->never())
|
||||
->method('delete');
|
||||
|
||||
$entity->expects($this->atLeastOnce())
|
||||
->method('toArray')
|
||||
->will($this->returnValue($expected));
|
||||
|
||||
$this->moduleHandler->expects($this->at(0))
|
||||
->method('invokeAll')
|
||||
->with('test_entity_type_presave');
|
||||
$this->moduleHandler->expects($this->at(1))
|
||||
->method('invokeAll')
|
||||
->with('entity_presave');
|
||||
$this->moduleHandler->expects($this->at(2))
|
||||
->method('invokeAll')
|
||||
->with('test_entity_type_insert');
|
||||
$this->moduleHandler->expects($this->at(3))
|
||||
->method('invokeAll')
|
||||
->with('entity_insert');
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('set')
|
||||
->with('foo', $expected);
|
||||
$return = $this->entityStorage->save($entity);
|
||||
$this->assertSame(SAVED_NEW, $return);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::save()
|
||||
* @covers ::doSave()
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
*
|
||||
* @depends testSaveInsert
|
||||
*/
|
||||
public function testSaveUpdate(EntityInterface $entity) {
|
||||
$this->entityType->expects($this->once())
|
||||
->method('getClass')
|
||||
->will($this->returnValue(get_class($entity)));
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$expected = array('id' => 'foo');
|
||||
$this->keyValueStore->expects($this->exactly(2))
|
||||
->method('has')
|
||||
->with('foo')
|
||||
->will($this->returnValue(TRUE));
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('getMultiple')
|
||||
->with(array('foo'))
|
||||
->will($this->returnValue(array(array('id' => 'foo'))));
|
||||
$this->keyValueStore->expects($this->never())
|
||||
->method('delete');
|
||||
|
||||
$this->moduleHandler->expects($this->at(0))
|
||||
->method('getImplementations')
|
||||
->with('entity_load')
|
||||
->will($this->returnValue(array()));
|
||||
$this->moduleHandler->expects($this->at(1))
|
||||
->method('getImplementations')
|
||||
->with('test_entity_type_load')
|
||||
->will($this->returnValue(array()));
|
||||
$this->moduleHandler->expects($this->at(2))
|
||||
->method('invokeAll')
|
||||
->with('test_entity_type_presave');
|
||||
$this->moduleHandler->expects($this->at(3))
|
||||
->method('invokeAll')
|
||||
->with('entity_presave');
|
||||
$this->moduleHandler->expects($this->at(4))
|
||||
->method('invokeAll')
|
||||
->with('test_entity_type_update');
|
||||
$this->moduleHandler->expects($this->at(5))
|
||||
->method('invokeAll')
|
||||
->with('entity_update');
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('set')
|
||||
->with('foo', $expected);
|
||||
$return = $this->entityStorage->save($entity);
|
||||
$this->assertSame(SAVED_UPDATED, $return);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::save()
|
||||
* @covers ::doSave()
|
||||
*/
|
||||
public function testSaveConfigEntity() {
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$entity = $this->getMockEntity('Drupal\Core\Config\Entity\ConfigEntityBase', array(array('id' => 'foo')), array(
|
||||
'toArray',
|
||||
'preSave',
|
||||
));
|
||||
$entity->enforceIsNew();
|
||||
// When creating a new entity, the ID is tracked as the original ID.
|
||||
$this->assertSame('foo', $entity->getOriginalId());
|
||||
|
||||
$expected = array('id' => 'foo');
|
||||
$entity->expects($this->once())
|
||||
->method('toArray')
|
||||
->will($this->returnValue($expected));
|
||||
|
||||
$this->keyValueStore->expects($this->exactly(2))
|
||||
->method('has')
|
||||
->with('foo')
|
||||
->will($this->returnValue(FALSE));
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('set')
|
||||
->with('foo', $expected);
|
||||
$this->keyValueStore->expects($this->never())
|
||||
->method('delete');
|
||||
|
||||
$return = $this->entityStorage->save($entity);
|
||||
$this->assertSame(SAVED_NEW, $return);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::save()
|
||||
* @covers ::doSave()
|
||||
*
|
||||
* @depends testSaveConfigEntity
|
||||
*/
|
||||
public function testSaveRenameConfigEntity(ConfigEntityInterface $entity) {
|
||||
$this->entityType->expects($this->once())
|
||||
->method('getClass')
|
||||
->will($this->returnValue(get_class($entity)));
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$this->moduleHandler->expects($this->at(0))
|
||||
->method('getImplementations')
|
||||
->with('entity_load')
|
||||
->will($this->returnValue(array()));
|
||||
$this->moduleHandler->expects($this->at(1))
|
||||
->method('getImplementations')
|
||||
->with('test_entity_type_load')
|
||||
->will($this->returnValue(array()));
|
||||
$expected = array('id' => 'foo');
|
||||
$entity->expects($this->once())
|
||||
->method('toArray')
|
||||
->will($this->returnValue($expected));
|
||||
$this->keyValueStore->expects($this->exactly(2))
|
||||
->method('has')
|
||||
->with('foo')
|
||||
->will($this->returnValue(TRUE));
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('getMultiple')
|
||||
->with(array('foo'))
|
||||
->will($this->returnValue(array(array('id' => 'foo'))));
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('delete')
|
||||
->with('foo');
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('set')
|
||||
->with('bar', $expected);
|
||||
|
||||
// Performing a rename does not change the original ID until saving.
|
||||
$this->assertSame('foo', $entity->getOriginalId());
|
||||
$entity->set('id', 'bar');
|
||||
$this->assertSame('foo', $entity->getOriginalId());
|
||||
|
||||
$return = $this->entityStorage->save($entity);
|
||||
$this->assertSame(SAVED_UPDATED, $return);
|
||||
$this->assertSame('bar', $entity->getOriginalId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::save()
|
||||
* @covers ::doSave()
|
||||
*/
|
||||
public function testSaveContentEntity() {
|
||||
$this->entityType->expects($this->any())
|
||||
->method('getKeys')
|
||||
->will($this->returnValue(array(
|
||||
'id' => 'id',
|
||||
)));
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$expected = array('id' => 'foo');
|
||||
$this->keyValueStore->expects($this->exactly(2))
|
||||
->method('has')
|
||||
->with('foo')
|
||||
->will($this->returnValue(FALSE));
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('set')
|
||||
->with('foo', $expected);
|
||||
$this->keyValueStore->expects($this->never())
|
||||
->method('delete');
|
||||
$entity = $this->getMockEntity('Drupal\Core\Entity\ContentEntityBase', array(), array(
|
||||
'onSaveOrDelete',
|
||||
'toArray',
|
||||
'id',
|
||||
));
|
||||
$entity->expects($this->atLeastOnce())
|
||||
->method('id')
|
||||
->will($this->returnValue('foo'));
|
||||
$entity->expects($this->once())
|
||||
->method('toArray')
|
||||
->will($this->returnValue($expected));
|
||||
$this->entityStorage->save($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::save()
|
||||
* @covers ::doSave()
|
||||
*
|
||||
* @expectedException \Drupal\Core\Entity\EntityMalformedException
|
||||
* @expectedExceptionMessage The entity does not have an ID.
|
||||
*/
|
||||
public function testSaveInvalid() {
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$entity = $this->getMockEntity('Drupal\Core\Config\Entity\ConfigEntityBase');
|
||||
$this->entityStorage->save($entity);
|
||||
$this->keyValueStore->expects($this->never())
|
||||
->method('has');
|
||||
$this->keyValueStore->expects($this->never())
|
||||
->method('set');
|
||||
$this->keyValueStore->expects($this->never())
|
||||
->method('delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::save()
|
||||
* @covers ::doSave()
|
||||
*
|
||||
* @expectedException \Drupal\Core\Entity\EntityStorageException
|
||||
* @expectedExceptionMessage test_entity_type entity with ID foo already exists
|
||||
*/
|
||||
public function testSaveDuplicate() {
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$entity = $this->getMockEntity('Drupal\Core\Entity\Entity', array(array('id' => 'foo')));
|
||||
$entity->enforceIsNew();
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('has')
|
||||
->will($this->returnValue(TRUE));
|
||||
$this->keyValueStore->expects($this->never())
|
||||
->method('set');
|
||||
$this->keyValueStore->expects($this->never())
|
||||
->method('delete');
|
||||
$this->entityStorage->save($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::load()
|
||||
* @covers ::postLoad()
|
||||
*/
|
||||
public function testLoad() {
|
||||
$entity = $this->getMockEntity();
|
||||
$this->entityType->expects($this->once())
|
||||
->method('getClass')
|
||||
->will($this->returnValue(get_class($entity)));
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('getMultiple')
|
||||
->with(array('foo'))
|
||||
->will($this->returnValue(array(array('id' => 'foo'))));
|
||||
$this->moduleHandler->expects($this->at(0))
|
||||
->method('getImplementations')
|
||||
->with('entity_load')
|
||||
->will($this->returnValue(array()));
|
||||
$this->moduleHandler->expects($this->at(1))
|
||||
->method('getImplementations')
|
||||
->with('test_entity_type_load')
|
||||
->will($this->returnValue(array()));
|
||||
$entity = $this->entityStorage->load('foo');
|
||||
$this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
|
||||
$this->assertSame('foo', $entity->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::load()
|
||||
*/
|
||||
public function testLoadMissingEntity() {
|
||||
$this->entityType->expects($this->once())
|
||||
->method('getClass');
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('getMultiple')
|
||||
->with(array('foo'))
|
||||
->will($this->returnValue(array()));
|
||||
$this->moduleHandler->expects($this->never())
|
||||
->method('getImplementations');
|
||||
$entity = $this->entityStorage->load('foo');
|
||||
$this->assertNull($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::loadMultiple()
|
||||
* @covers ::postLoad()
|
||||
* @covers ::mapFromStorageRecords()
|
||||
* @covers ::doLoadMultiple()
|
||||
*/
|
||||
public function testLoadMultipleAll() {
|
||||
$expected['foo'] = $this->getMockEntity('Drupal\Core\Entity\Entity', array(array('id' => 'foo')));
|
||||
$expected['bar'] = $this->getMockEntity('Drupal\Core\Entity\Entity', array(array('id' => 'bar')));
|
||||
$this->entityType->expects($this->once())
|
||||
->method('getClass')
|
||||
->will($this->returnValue(get_class(reset($expected))));
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('getAll')
|
||||
->will($this->returnValue(array(array('id' => 'foo'), array('id' => 'bar'))));
|
||||
$this->moduleHandler->expects($this->at(0))
|
||||
->method('getImplementations')
|
||||
->with('entity_load')
|
||||
->will($this->returnValue(array()));
|
||||
$this->moduleHandler->expects($this->at(1))
|
||||
->method('getImplementations')
|
||||
->with('test_entity_type_load')
|
||||
->will($this->returnValue(array()));
|
||||
$entities = $this->entityStorage->loadMultiple();
|
||||
foreach ($entities as $id => $entity) {
|
||||
$this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
|
||||
$this->assertSame($id, $entity->id());
|
||||
$this->assertSame($id, $expected[$id]->id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::loadMultiple()
|
||||
* @covers ::postLoad()
|
||||
* @covers ::mapFromStorageRecords()
|
||||
* @covers ::doLoadMultiple()
|
||||
*/
|
||||
public function testLoadMultipleIds() {
|
||||
$entity = $this->getMockEntity('Drupal\Core\Entity\Entity', array(array('id' => 'foo')));
|
||||
$this->entityType->expects($this->once())
|
||||
->method('getClass')
|
||||
->will($this->returnValue(get_class($entity)));
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$expected[] = $entity;
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('getMultiple')
|
||||
->with(array('foo'))
|
||||
->will($this->returnValue(array(array('id' => 'foo'))));
|
||||
$this->moduleHandler->expects($this->at(0))
|
||||
->method('getImplementations')
|
||||
->with('entity_load')
|
||||
->will($this->returnValue(array()));
|
||||
$this->moduleHandler->expects($this->at(1))
|
||||
->method('getImplementations')
|
||||
->with('test_entity_type_load')
|
||||
->will($this->returnValue(array()));
|
||||
$entities = $this->entityStorage->loadMultiple(array('foo'));
|
||||
foreach ($entities as $id => $entity) {
|
||||
$this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
|
||||
$this->assertSame($id, $entity->id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::loadRevision()
|
||||
*/
|
||||
public function testLoadRevision() {
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$this->assertSame(FALSE, $this->entityStorage->loadRevision(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::deleteRevision()
|
||||
*/
|
||||
public function testDeleteRevision() {
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$this->assertSame(NULL, $this->entityStorage->deleteRevision(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::delete()
|
||||
* @covers ::doDelete()
|
||||
*/
|
||||
public function testDelete() {
|
||||
$entities['foo'] = $this->getMockEntity('Drupal\Core\Entity\Entity', array(array('id' => 'foo')));
|
||||
$entities['bar'] = $this->getMockEntity('Drupal\Core\Entity\Entity', array(array('id' => 'bar')));
|
||||
$this->entityType->expects($this->once())
|
||||
->method('getClass')
|
||||
->will($this->returnValue(get_class(reset($entities))));
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$this->moduleHandler->expects($this->at(0))
|
||||
->method('invokeAll')
|
||||
->with('test_entity_type_predelete');
|
||||
$this->moduleHandler->expects($this->at(1))
|
||||
->method('invokeAll')
|
||||
->with('entity_predelete');
|
||||
$this->moduleHandler->expects($this->at(2))
|
||||
->method('invokeAll')
|
||||
->with('test_entity_type_predelete');
|
||||
$this->moduleHandler->expects($this->at(3))
|
||||
->method('invokeAll')
|
||||
->with('entity_predelete');
|
||||
$this->moduleHandler->expects($this->at(4))
|
||||
->method('invokeAll')
|
||||
->with('test_entity_type_delete');
|
||||
$this->moduleHandler->expects($this->at(5))
|
||||
->method('invokeAll')
|
||||
->with('entity_delete');
|
||||
$this->moduleHandler->expects($this->at(6))
|
||||
->method('invokeAll')
|
||||
->with('test_entity_type_delete');
|
||||
$this->moduleHandler->expects($this->at(7))
|
||||
->method('invokeAll')
|
||||
->with('entity_delete');
|
||||
|
||||
$this->keyValueStore->expects($this->once())
|
||||
->method('deleteMultiple')
|
||||
->with(array('foo', 'bar'));
|
||||
$this->entityStorage->delete($entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::delete()
|
||||
* @covers ::doDelete()
|
||||
*/
|
||||
public function testDeleteNothing() {
|
||||
$this->setUpKeyValueEntityStorage();
|
||||
|
||||
$this->moduleHandler->expects($this->never())
|
||||
->method($this->anything());
|
||||
$this->keyValueStore->expects($this->never())
|
||||
->method('delete');
|
||||
$this->keyValueStore->expects($this->never())
|
||||
->method('deleteMultiple');
|
||||
|
||||
$this->entityStorage->delete(array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entity with specific methods mocked.
|
||||
*
|
||||
* @param string $class
|
||||
* (optional) The concrete entity class to mock. Defaults to
|
||||
* 'Drupal\Core\Entity\Entity'.
|
||||
* @param array $arguments
|
||||
* (optional) Arguments to pass to the constructor. An empty set of values
|
||||
* and an entity type ID will be provided.
|
||||
* @param array $methods
|
||||
* (optional) The methods to mock.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
public function getMockEntity($class = 'Drupal\Core\Entity\Entity', array $arguments = array(), $methods = array()) {
|
||||
// Ensure the entity is passed at least an array of values and an entity
|
||||
// type ID
|
||||
if (!isset($arguments[0])) {
|
||||
$arguments[0] = array();
|
||||
}
|
||||
if (!isset($arguments[1])) {
|
||||
$arguments[1] = 'test_entity_type';
|
||||
}
|
||||
return $this->getMockForAbstractClass($class, $arguments, '', TRUE, TRUE, TRUE, $methods);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
if (!defined('SAVED_NEW')) {
|
||||
define('SAVED_NEW', 1);
|
||||
}
|
||||
if (!defined('SAVED_UPDATED')) {
|
||||
define('SAVED_UPDATED', 2);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue