summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcatch2014-03-30 13:02:28 (GMT)
committercatch2014-03-30 13:02:28 (GMT)
commit979bb625cefb89a893dfe03e3fc8cca6e243c9f4 (patch)
tree1649741c22be782e6d9f1f9552f5bdbafcf6c81a
parent669b898b86e4209f7d6e8e0b191ef4ddf2313dcf (diff)
Issue #2226027 by tim.plunkett: ConfigEntityBase::preSave() tries to load the original entity but instead loads itself.
-rw-r--r--core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php3
-rw-r--r--core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php26
-rw-r--r--core/lib/Drupal/Core/Entity/Entity.php2
-rw-r--r--core/modules/breakpoint/lib/Drupal/breakpoint/Entity/Breakpoint.php19
-rw-r--r--core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php13
-rw-r--r--core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php6
-rw-r--r--core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php2
-rw-r--r--core/modules/field/lib/Drupal/field/FieldConfigStorage.php8
-rw-r--r--core/modules/field/lib/Drupal/field/FieldInstanceConfigStorage.php8
-rw-r--r--core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php7
-rw-r--r--core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php2
-rw-r--r--core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php723
12 files changed, 793 insertions, 26 deletions
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
index e5d943c..dd6481e 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
@@ -278,8 +278,9 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
throw new ConfigDuplicateUUIDException(String::format('Attempt to save a configuration entity %id with UUID %uuid when this UUID is already used for %matched', array('%id' => $this->id(), '%uuid' => $this->uuid(), '%matched' => $matched_entity)));
}
+ // If this entity is not new, load the original entity for comparison.
if (!$this->isNew()) {
- $original = $storage->loadUnchanged($this->id());
+ $original = $storage->loadUnchanged($this->getOriginalId());
// Ensure that the UUID cannot be changed for an existing entity.
if ($original && ($original->uuid() != $this->uuid())) {
throw new ConfigDuplicateUUIDException(String::format('Attempt to save a configuration entity %id with UUID %uuid when this entity already exists with UUID %original_uuid', array('%id' => $this->id(), '%uuid' => $this->uuid(), '%original_uuid' => $original->uuid())));
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php
index 0ab9a60..f807abd 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php
@@ -18,6 +18,7 @@ use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Component\Uuid\UuidInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -66,6 +67,13 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
protected $configStorage;
/**
+ * The language manager.
+ *
+ * @var \Drupal\Core\Language\LanguageManagerInterface
+ */
+ protected $languageManager;
+
+ /**
* Constructs a ConfigEntityStorage object.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
@@ -76,8 +84,10 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
* The config storage service.
* @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, ConfigFactoryInterface $config_factory, StorageInterface $config_storage, UuidInterface $uuid_service) {
+ public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, StorageInterface $config_storage, UuidInterface $uuid_service, LanguageManagerInterface $language_manager) {
parent::__construct($entity_type);
$this->idKey = $this->entityType->getKey('id');
@@ -86,6 +96,7 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
$this->configFactory = $config_factory;
$this->configStorage = $config_storage;
$this->uuidService = $uuid_service;
+ $this->languageManager = $language_manager;
}
/**
@@ -96,7 +107,8 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
$entity_type,
$container->get('config.factory'),
$container->get('config.storage'),
- $container->get('uuid')
+ $container->get('uuid'),
+ $container->get('language_manager')
);
}
@@ -228,7 +240,7 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
$class::preCreate($this, $values);
// Set default language to site default if not provided.
- $values += array('langcode' => language_default()->id);
+ $values += array('langcode' => $this->languageManager->getDefaultLanguage()->id);
$entity = new $class($values, $this->entityTypeId);
// Mark this entity as new, so isNew() returns TRUE. This does not check
@@ -331,9 +343,6 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
$config->save();
$entity->postSave($this, TRUE);
$this->invokeHook('update', $entity);
-
- // Immediately update the original ID.
- $entity->setOriginalId($entity->id());
}
else {
$return = SAVED_NEW;
@@ -343,6 +352,11 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
$this->invokeHook('insert', $entity);
}
+ // After saving, this is now the "original entity", and subsequent saves
+ // will be updates instead of inserts, and updates must always be able to
+ // correctly identify the original entity.
+ $entity->setOriginalId($entity->id());
+
unset($entity->original);
return $return;
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 2245ad7..534048c 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -314,7 +314,9 @@ abstract class Entity extends DependencySerialization implements EntityInterface
public function createDuplicate() {
$duplicate = clone $this;
$entity_type = $this->getEntityType();
+ // Reset the entity ID and indicate that this is a new entity.
$duplicate->{$entity_type->getKey('id')} = NULL;
+ $duplicate->enforceIsNew();
// Check if the entity type supports UUIDs and generate a new one if so.
if ($entity_type->hasKey('uuid')) {
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/Breakpoint.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/Breakpoint.php
index e200894..5fa0260 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/Breakpoint.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/Breakpoint.php
@@ -106,6 +106,18 @@ class Breakpoint extends ConfigEntityBase implements BreakpointInterface {
public $multipliers = array();
/**
+ * {@inheritdoc}
+ */
+ public function id() {
+ // If no ID is specified, build one from the properties that uniquely define
+ // this breakpoint.
+ if (!isset($this->id)) {
+ $this->id = $this->sourceType . '.' . $this->source . '.' . $this->name;
+ }
+ return $this->id;
+ }
+
+ /**
* Overrides Drupal\config\ConfigEntityBase::save().
*/
public function save() {
@@ -114,13 +126,6 @@ class Breakpoint extends ConfigEntityBase implements BreakpointInterface {
throw new InvalidBreakpointException('Invalid data detected.');
}
- // Build an id if none is set.
- // Since a particular name can be used by multiple theme/modules we need
- // to make a unique id.
- if (empty($this->id)) {
- $this->id = $this->sourceType . '.' . $this->source . '.' . $this->name;
- }
-
// Set the label if none is set.
if (empty($this->label)) {
$this->label = $this->name;
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php
index 9840657d..595ca76 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php
@@ -104,10 +104,19 @@ class BreakpointGroup extends ConfigEntityBase implements BreakpointGroupInterfa
if (!$this->isValid()) {
throw new InvalidBreakpointException('Invalid data detected.');
}
- if (empty($this->id)) {
+ parent::save();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function id() {
+ // If no ID is specified, build one from the properties that uniquely define
+ // this breakpoint group.
+ if (!isset($this->id)) {
$this->id = $this->sourceType . '.' . $this->source . '.' . $this->name;
}
- parent::save();
+ return $this->id;
}
/**
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php
index 85d0711..e132280 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointAPITest.php
@@ -46,7 +46,7 @@ class BreakpointAPITest extends BreakpointTestBase {
$this->assertTrue($exception, 'breakpoint_config_name: An exception is thrown when an invalid sourceType is entered.');
// Try an invalid source.
- $breakpoint->id = '';
+ $breakpoint = $breakpoint->createDuplicate();
$breakpoint->sourceType = Breakpoint::SOURCE_TYPE_USER_DEFINED;
$breakpoint->source = 'custom*_module source';
@@ -60,7 +60,7 @@ class BreakpointAPITest extends BreakpointTestBase {
$this->assertTrue($exception, 'breakpoint_config_name: An exception is thrown when an invalid source is entered.');
// Try an invalid name (make sure there is at least once capital letter).
- $breakpoint->id = '';
+ $breakpoint = $breakpoint->createDuplicate();
$breakpoint->source = 'custom_module';
$breakpoint->name = drupal_ucfirst($this->randomName());
@@ -74,7 +74,7 @@ class BreakpointAPITest extends BreakpointTestBase {
$this->assertTrue($exception, 'breakpoint_config_name: An exception is thrown when an invalid name is entered.');
// Try a valid breakpoint.
- $breakpoint->id = '';
+ $breakpoint = $breakpoint->createDuplicate();
$breakpoint->name = drupal_strtolower($this->randomName());
$breakpoint->mediaQuery = 'all';
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php
index 3751c81..7a563f6 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointGroupAPITest.php
@@ -49,6 +49,7 @@ class BreakpointGroupAPITest extends BreakpointGroupTestBase {
$this->assertTrue($exception, 'An exception is thrown when an invalid sourceType is entered.');
// Try an invalid source.
+ $breakpoint_group = $breakpoint_group->createDuplicate();
$breakpoint_group->name = '';
$breakpoint_group->sourceType = Breakpoint::SOURCE_TYPE_USER_DEFINED;
$breakpoint_group->source = 'custom*_module source';
@@ -63,6 +64,7 @@ class BreakpointGroupAPITest extends BreakpointGroupTestBase {
$this->assertTrue($exception, 'An exception is thrown when an invalid source is entered.');
// Try a valid breakpoint_group.
+ $breakpoint_group = $breakpoint_group->createDuplicate();
$breakpoint_group->name = 'test';
$breakpoint_group->source = 'custom_module_source';
diff --git a/core/modules/field/lib/Drupal/field/FieldConfigStorage.php b/core/modules/field/lib/Drupal/field/FieldConfigStorage.php
index 09fa119..79caf54 100644
--- a/core/modules/field/lib/Drupal/field/FieldConfigStorage.php
+++ b/core/modules/field/lib/Drupal/field/FieldConfigStorage.php
@@ -13,6 +13,7 @@ use Drupal\Core\Config\Entity\ConfigEntityStorage;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Language\LanguageManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\StorageInterface;
@@ -56,6 +57,8 @@ class FieldConfigStorage extends ConfigEntityStorage {
* The config storage service.
* @param \Drupal\Component\Uuid\UuidInterface $uuid_service
* The UUID service.
+ * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+ * The language manager.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Extension\ModuleHandler $module_handler
@@ -63,8 +66,8 @@ class FieldConfigStorage extends ConfigEntityStorage {
* @param \Drupal\Core\KeyValueStore\StateInterface $state
* The state key value store.
*/
- public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, StorageInterface $config_storage, UuidInterface $uuid_service, EntityManagerInterface $entity_manager, ModuleHandler $module_handler, StateInterface $state) {
- parent::__construct($entity_type, $config_factory, $config_storage, $uuid_service);
+ public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, StorageInterface $config_storage, UuidInterface $uuid_service, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager, ModuleHandler $module_handler, StateInterface $state) {
+ parent::__construct($entity_type, $config_factory, $config_storage, $uuid_service, $language_manager);
$this->entityManager = $entity_manager;
$this->moduleHandler = $module_handler;
$this->state = $state;
@@ -79,6 +82,7 @@ class FieldConfigStorage extends ConfigEntityStorage {
$container->get('config.factory'),
$container->get('config.storage'),
$container->get('uuid'),
+ $container->get('language_manager'),
$container->get('entity.manager'),
$container->get('module_handler'),
$container->get('state')
diff --git a/core/modules/field/lib/Drupal/field/FieldInstanceConfigStorage.php b/core/modules/field/lib/Drupal/field/FieldInstanceConfigStorage.php
index 1942ef5..9d9826a 100644
--- a/core/modules/field/lib/Drupal/field/FieldInstanceConfigStorage.php
+++ b/core/modules/field/lib/Drupal/field/FieldInstanceConfigStorage.php
@@ -12,6 +12,7 @@ use Drupal\Core\Config\Entity\ConfigEntityStorage;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Language\LanguageManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Component\Uuid\UuidInterface;
@@ -54,13 +55,15 @@ class FieldInstanceConfigStorage extends ConfigEntityStorage {
* The config storage service.
* @param \Drupal\Component\Uuid\UuidInterface $uuid_service
* The UUID service.
+ * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+ * The language manager.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\KeyValueStore\StateInterface $state
* The state key value store.
*/
- public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, StorageInterface $config_storage, UuidInterface $uuid_service, EntityManagerInterface $entity_manager, StateInterface $state) {
- parent::__construct($entity_type, $config_factory, $config_storage, $uuid_service);
+ public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, StorageInterface $config_storage, UuidInterface $uuid_service, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager, StateInterface $state) {
+ parent::__construct($entity_type, $config_factory, $config_storage, $uuid_service, $language_manager);
$this->entityManager = $entity_manager;
$this->state = $state;
}
@@ -74,6 +77,7 @@ class FieldInstanceConfigStorage extends ConfigEntityStorage {
$container->get('config.factory'),
$container->get('config.storage'),
$container->get('uuid'),
+ $container->get('language_manager'),
$container->get('entity.manager'),
$container->get('state')
);
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php
index 8320336..a645691 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php
@@ -41,9 +41,10 @@ class FilterCrudTest extends DrupalUnitTestBase {
$this->verifyTextFormat($format);
// Add another text format specifying all possible properties.
- $format = entity_create('filter_format');
- $format->format = 'custom_format';
- $format->name = 'Custom format';
+ $format = entity_create('filter_format', array(
+ 'format' => 'custom_format',
+ 'name' => 'Custom format',
+ ));
$format->setFilterConfig('filter_url', array(
'status' => 1,
'settings' => array(
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
index dcdfb76..a23f032 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
@@ -369,6 +369,8 @@ class ConfigEntityBaseUnitTest extends UnitTestCase {
$duplicate = $this->entity->createDuplicate();
$this->assertInstanceOf('\Drupal\Core\Entity\Entity', $duplicate);
$this->assertNotSame($this->entity, $duplicate);
+ $this->assertFalse($this->entity->isNew());
+ $this->assertTrue($duplicate->isNew());
$this->assertNull($duplicate->id());
$this->assertNull($duplicate->getOriginalId());
$this->assertNotEquals($this->entity->uuid(), $duplicate->uuid());
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
new file mode 100644
index 0000000..918e596
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
@@ -0,0 +1,723 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Config\Entity\ConfigEntityStorageTest.
+ */
+
+namespace Drupal\Tests\Core\Config\Entity {
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\Language;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Config\Entity\ConfigEntityStorage
+ *
+ * @group Drupal
+ */
+class ConfigEntityStorageTest extends UnitTestCase {
+
+ /**
+ * The entity type.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $entityType;
+
+ /**
+ * 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;
+
+ /**
+ * The config storage controller.
+ *
+ * @var \Drupal\Core\Config\Entity\ConfigEntityStorage
+ */
+ protected $entityStorage;
+
+ /**
+ * The config factory service.
+ *
+ * @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $configFactory;
+
+ /**
+ * The config storage service.
+ *
+ * @var \Drupal\Core\Config\StorageInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $configStorage;
+
+ /**
+ * The entity query.
+ *
+ * @var \Drupal\Core\Entity\Query\QueryInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $entityQuery;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getInfo() {
+ return array(
+ 'name' => 'ConfigEntityStorage unit test',
+ 'description' => 'Tests \Drupal\Core\Config\Entity\ConfigEntityStorage',
+ 'group' => 'Configuration',
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+
+ $this->entityType = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+ $this->entityType->expects($this->any())
+ ->method('getKey')
+ ->will($this->returnValueMap(array(
+ array('id', 'id'),
+ array('uuid', 'uuid'),
+ )));
+ $this->entityType->expects($this->any())
+ ->method('id')
+ ->will($this->returnValue('test_entity_type'));
+ $this->entityType->expects($this->any())
+ ->method('getConfigPrefix')
+ ->will($this->returnValue('the_config_prefix'));
+
+ $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->configStorage = $this->getConfigStorageStub(array());
+
+ $this->configFactory = $this->getMock('Drupal\Core\Config\ConfigFactoryInterface');
+
+ $this->entityQuery = $this->getMock('Drupal\Core\Entity\Query\QueryInterface');
+
+ $this->entityStorage = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigEntityStorage')
+ ->setConstructorArgs(array($this->entityType, $this->configFactory, $this->configStorage, $this->uuidService, $this->languageManager))
+ ->setMethods(array('getQuery'))
+ ->getMock();
+ $this->entityStorage->expects($this->any())
+ ->method('getQuery')
+ ->will($this->returnValue($this->entityQuery));
+ $this->entityStorage->setModuleHandler($this->moduleHandler);
+ }
+
+ /**
+ * @covers ::create()
+ */
+ public function testCreateWithPredefinedUuid() {
+ $this->entityType->expects($this->atLeastOnce())
+ ->method('getClass')
+ ->will($this->returnValue(get_class($this->getMockEntity())));
+
+ $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()
+ *
+ * @return \Drupal\Core\Entity\EntityInterface
+ */
+ public function testCreate() {
+ $this->entityType->expects($this->atLeastOnce())
+ ->method('getClass')
+ ->will($this->returnValue(get_class($this->getMockEntity())));
+
+ $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()
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *
+ * @return \Drupal\Core\Entity\EntityInterface
+ *
+ * @depends testCreate
+ */
+ public function testSaveInsert(EntityInterface $entity) {
+ $config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $config_object->expects($this->atLeastOnce())
+ ->method('isNew')
+ ->will($this->returnValue(TRUE));
+ $config_object->expects($this->exactly(4))
+ ->method('set');
+ $config_object->expects($this->once())
+ ->method('save');
+
+ $this->configFactory->expects($this->once())
+ ->method('get')
+ ->with('the_config_prefix.foo')
+ ->will($this->returnValue($config_object));
+
+ $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->entityQuery->expects($this->once())
+ ->method('condition')
+ ->with('uuid', 'bar')
+ ->will($this->returnSelf());
+ $this->entityQuery->expects($this->once())
+ ->method('execute')
+ ->will($this->returnValue(array()));
+
+ $return = $this->entityStorage->save($entity);
+ $this->assertSame(SAVED_NEW, $return);
+ return $entity;
+ }
+
+ /**
+ * @covers ::save()
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *
+ * @return \Drupal\Core\Entity\EntityInterface
+ *
+ * @depends testSaveInsert
+ */
+ public function testSaveUpdate(EntityInterface $entity) {
+ $config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $config_object->expects($this->atLeastOnce())
+ ->method('isNew')
+ ->will($this->returnValue(FALSE));
+ $config_object->expects($this->exactly(4))
+ ->method('set');
+ $config_object->expects($this->once())
+ ->method('save');
+
+ $this->configFactory->expects($this->exactly(2))
+ ->method('loadMultiple')
+ ->with(array('the_config_prefix.foo'))
+ ->will($this->returnValue(array()));
+ $this->configFactory->expects($this->once())
+ ->method('get')
+ ->with('the_config_prefix.foo')
+ ->will($this->returnValue($config_object));
+
+ $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_update');
+ $this->moduleHandler->expects($this->at(3))
+ ->method('invokeAll')
+ ->with('entity_update');
+
+ $this->entityQuery->expects($this->once())
+ ->method('condition')
+ ->with('uuid', 'bar')
+ ->will($this->returnSelf());
+ $this->entityQuery->expects($this->once())
+ ->method('execute')
+ ->will($this->returnValue(array($entity->id())));
+
+ $return = $this->entityStorage->save($entity);
+ $this->assertSame(SAVED_UPDATED, $return);
+ return $entity;
+ }
+
+ /**
+ * @covers ::save()
+ *
+ * @depends testSaveInsert
+ */
+ public function testSaveRename(ConfigEntityInterface $entity) {
+ $config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $config_object->expects($this->atLeastOnce())
+ ->method('isNew')
+ ->will($this->returnValue(FALSE));
+ $config_object->expects($this->exactly(4))
+ ->method('set');
+ $config_object->expects($this->once())
+ ->method('save');
+
+ $this->configFactory->expects($this->once())
+ ->method('rename')
+ ->will($this->returnValue($config_object));
+ $this->configFactory->expects($this->exactly(2))
+ ->method('loadMultiple')
+ ->with(array('the_config_prefix.foo'))
+ ->will($this->returnValue(array()));
+ $this->configFactory->expects($this->once())
+ ->method('get')
+ ->with('the_config_prefix.foo')
+ ->will($this->returnValue($config_object));
+
+ // 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());
+
+ $this->entityQuery->expects($this->once())
+ ->method('condition')
+ ->with('uuid', 'bar')
+ ->will($this->returnSelf());
+ $this->entityQuery->expects($this->once())
+ ->method('execute')
+ ->will($this->returnValue(array($entity->id())));
+
+ $return = $this->entityStorage->save($entity);
+ $this->assertSame(SAVED_UPDATED, $return);
+ $this->assertSame('bar', $entity->getOriginalId());
+ }
+
+ /**
+ * @covers ::save()
+ *
+ * @expectedException \Drupal\Core\Entity\EntityMalformedException
+ * @expectedExceptionMessage The entity does not have an ID.
+ */
+ public function testSaveInvalid() {
+ $entity = $this->getMockEntity();
+ $this->entityStorage->save($entity);
+ }
+
+ /**
+ * @covers ::save()
+ *
+ * @expectedException \Drupal\Core\Entity\EntityStorageException
+ */
+ public function testSaveDuplicate() {
+ $config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $config_object->expects($this->atLeastOnce())
+ ->method('isNew')
+ ->will($this->returnValue(FALSE));
+ $config_object->expects($this->never())
+ ->method('set');
+ $config_object->expects($this->never())
+ ->method('save');
+
+ $this->configFactory->expects($this->once())
+ ->method('get')
+ ->with('the_config_prefix.foo')
+ ->will($this->returnValue($config_object));
+
+ $entity = $this->getMockEntity(array('id' => 'foo'));
+ $entity->enforceIsNew();
+
+ $this->entityStorage->save($entity);
+ }
+
+ /**
+ * @covers ::save()
+ *
+ * @expectedException \Drupal\Core\Config\ConfigDuplicateUUIDException
+ * @expectedExceptionMessage when this UUID is already used for
+ */
+ public function testSaveMismatch() {
+ $config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $config_object->expects($this->atLeastOnce())
+ ->method('isNew')
+ ->will($this->returnValue(TRUE));
+ $config_object->expects($this->never())
+ ->method('save');
+
+ $this->configFactory->expects($this->once())
+ ->method('get')
+ ->with('the_config_prefix.foo')
+ ->will($this->returnValue($config_object));
+
+ $this->entityQuery->expects($this->once())
+ ->method('condition')
+ ->will($this->returnSelf());
+ $this->entityQuery->expects($this->once())
+ ->method('execute')
+ ->will($this->returnValue(array('baz')));
+
+ $entity = $this->getMockEntity(array('id' => 'foo'));
+ $this->entityStorage->save($entity);
+ }
+
+ /**
+ * @covers ::save()
+ */
+ public function testSaveNoMismatch() {
+ $config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $config_object->expects($this->atLeastOnce())
+ ->method('isNew')
+ ->will($this->returnValue(TRUE));
+ $config_object->expects($this->once())
+ ->method('save');
+
+ $this->configFactory->expects($this->once())
+ ->method('get')
+ ->with('the_config_prefix.baz')
+ ->will($this->returnValue($config_object));
+ $this->configFactory->expects($this->once())
+ ->method('rename')
+ ->will($this->returnValue($config_object));
+
+ $this->entityQuery->expects($this->once())
+ ->method('condition')
+ ->will($this->returnSelf());
+ $this->entityQuery->expects($this->once())
+ ->method('execute')
+ ->will($this->returnValue(array('baz')));
+
+ $entity = $this->getMockEntity(array('id' => 'foo'));
+ $entity->enforceIsNew();
+ $entity->setOriginalId('baz');
+ $this->entityStorage->save($entity);
+ }
+
+ /**
+ * @covers ::save()
+ *
+ * @expectedException \Drupal\Core\Config\ConfigDuplicateUUIDException
+ * @expectedExceptionMessage when this entity already exists with UUID
+ */
+ public function testSaveChangedUuid() {
+ $config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $config_object->expects($this->atLeastOnce())
+ ->method('isNew')
+ ->will($this->returnValue(FALSE));
+ $config_object->expects($this->never())
+ ->method('save');
+ $config_object->expects($this->exactly(2))
+ ->method('get')
+ ->will($this->returnValueMap(array(
+ array('', array('id' => 'foo')),
+ array('id', 'foo'),
+ )));
+
+ $this->configFactory->expects($this->at(1))
+ ->method('loadMultiple')
+ ->with(array('the_config_prefix.foo'))
+ ->will($this->returnValue(array()));
+ $this->configFactory->expects($this->at(2))
+ ->method('loadMultiple')
+ ->with(array('the_config_prefix.foo'))
+ ->will($this->returnValue(array($config_object)));
+ $this->configFactory->expects($this->once())
+ ->method('get')
+ ->with('the_config_prefix.foo')
+ ->will($this->returnValue($config_object));
+ $this->configFactory->expects($this->never())
+ ->method('rename')
+ ->will($this->returnValue($config_object));
+
+ $this->moduleHandler->expects($this->exactly(2))
+ ->method('getImplementations')
+ ->will($this->returnValue(array()));
+
+ $this->entityQuery->expects($this->once())
+ ->method('condition')
+ ->will($this->returnSelf());
+ $this->entityQuery->expects($this->once())
+ ->method('execute')
+ ->will($this->returnValue(array('foo')));
+
+ $entity = $this->getMockEntity(array('id' => 'foo'));
+ $this->entityType->expects($this->atLeastOnce())
+ ->method('getClass')
+ ->will($this->returnValue(get_class($entity)));
+
+ $entity->set('uuid', 'baz');
+ $this->entityStorage->save($entity);
+ }
+
+ /**
+ * @covers ::load()
+ * @covers ::postLoad()
+ * @covers ::buildQuery()
+ */
+ public function testLoad() {
+ $config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $config_object->expects($this->exactly(2))
+ ->method('get')
+ ->will($this->returnValueMap(array(
+ array('', array('id' => 'foo')),
+ array('id', 'foo'),
+ )));
+
+ $this->configFactory->expects($this->once())
+ ->method('loadMultiple')
+ ->with(array('the_config_prefix.foo'))
+ ->will($this->returnValue(array($config_object)));
+ $this->moduleHandler->expects($this->exactly(2))
+ ->method('getImplementations')
+ ->will($this->returnValue(array()));
+
+ $this->entityType->expects($this->atLeastOnce())
+ ->method('getClass')
+ ->will($this->returnValue(get_class($this->getMockEntity())));
+
+ $entity = $this->entityStorage->load('foo');
+ $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
+ $this->assertSame('foo', $entity->id());
+ }
+
+ /**
+ * @covers ::loadMultiple()
+ * @covers ::postLoad()
+ * @covers ::buildQuery()
+ */
+ public function testLoadMultipleAll() {
+ $foo_config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $foo_config_object->expects($this->exactly(2))
+ ->method('get')
+ ->will($this->returnValueMap(array(
+ array('', array('id' => 'foo')),
+ array('id', 'foo'),
+ )));
+ $bar_config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $bar_config_object->expects($this->exactly(2))
+ ->method('get')
+ ->will($this->returnValueMap(array(
+ array('', array('id' => 'bar')),
+ array('id', 'bar'),
+ )));
+
+ $this->configFactory->expects($this->once())
+ ->method('loadMultiple')
+ ->with(array())
+ ->will($this->returnValue(array($foo_config_object, $bar_config_object)));
+ $this->moduleHandler->expects($this->exactly(2))
+ ->method('getImplementations')
+ ->will($this->returnValue(array()));
+
+ $this->entityType->expects($this->atLeastOnce())
+ ->method('getClass')
+ ->will($this->returnValue(get_class($this->getMockEntity())));
+
+ $entities = $this->entityStorage->loadMultiple();
+ $expected['foo'] = 'foo';
+ $expected['bar'] = 'bar';
+ foreach ($entities as $id => $entity) {
+ $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
+ $this->assertSame($id, $entity->id());
+ $this->assertSame($expected[$id], $entity->id());
+ }
+ }
+
+ /**
+ * @covers ::loadMultiple()
+ * @covers ::postLoad()
+ * @covers ::buildQuery()
+ */
+ public function testLoadMultipleIds() {
+ $config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $config_object->expects($this->exactly(2))
+ ->method('get')
+ ->will($this->returnValueMap(array(
+ array('', array('id' => 'foo')),
+ array('id', 'foo'),
+ )));
+
+ $this->configFactory->expects($this->once())
+ ->method('loadMultiple')
+ ->with(array('the_config_prefix.foo'))
+ ->will($this->returnValue(array($config_object)));
+ $this->moduleHandler->expects($this->exactly(2))
+ ->method('getImplementations')
+ ->will($this->returnValue(array()));
+ $this->entityType->expects($this->atLeastOnce())
+ ->method('getClass')
+ ->will($this->returnValue(get_class($this->getMockEntity())));
+
+ $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->assertSame(FALSE, $this->entityStorage->loadRevision(1));
+ }
+
+ /**
+ * @covers ::deleteRevision()
+ */
+ public function testDeleteRevision() {
+ $this->assertSame(NULL, $this->entityStorage->deleteRevision(1));
+ }
+
+ /**
+ * @covers ::delete()
+ */
+ public function testDelete() {
+ $this->entityType->expects($this->atLeastOnce())
+ ->method('getClass')
+ ->will($this->returnValue(get_class($this->getMockEntity())));
+
+ $entities = array();
+ $configs = array();
+ $config_map = array();
+ foreach (array('foo', 'bar') as $id) {
+ $entity = $this->getMockEntity(array('id' => $id));
+ $entities[] = $entity;
+ $config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $config_object->expects($this->once())
+ ->method('delete');
+ $configs[] = $config_object;
+ $config_map[] = array("the_config_prefix.$id", $config_object);
+ }
+
+ $this->configFactory->expects($this->exactly(2))
+ ->method('get')
+ ->will($this->returnValueMap($config_map));
+
+ $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->entityStorage->delete($entities);
+ }
+
+ /**
+ * @covers ::delete()
+ */
+ public function testDeleteNothing() {
+ $this->moduleHandler->expects($this->never())
+ ->method($this->anything());
+ $this->configFactory->expects($this->never())
+ ->method('get');
+
+ $this->entityStorage->delete(array());
+ }
+
+ /**
+ * Creates an entity with specific methods mocked.
+ *
+ * @param array $values
+ * (optional) Values to pass to the constructor.
+ * @param array $methods
+ * (optional) The methods to mock.
+ *
+ * @return \Drupal\Core\Entity\EntityInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ public function getMockEntity(array $values = array(), $methods = array()) {
+ $methods[] = 'onSaveOrDelete';
+ $methods[] = 'onUpdateBundleEntity';
+ return $this->getMockForAbstractClass('Drupal\Core\Config\Entity\ConfigEntityBase', array($values, 'test_entity_type'), '', TRUE, TRUE, TRUE, $methods);
+ }
+
+}
+
+}
+namespace {
+ if (!defined('SAVED_NEW')) {
+ define('SAVED_NEW', 1);
+ }
+ if (!defined('SAVED_UPDATED')) {
+ define('SAVED_UPDATED', 2);
+ }
+}