diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/ConfigEntityAdapter.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/ConfigEntityAdapter.php new file mode 100644 index 0000000000..6199281b54 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/ConfigEntityAdapter.php @@ -0,0 +1,100 @@ +entity)) { + throw new MissingDataException("Unable to get property $property_name as no entity has been provided."); + } + return $this->getConfigTypedData()->get($property_name); + } + + /** + * {@inheritdoc} + */ + public function set($property_name, $value, $notify = TRUE) { + if (!isset($this->entity)) { + throw new MissingDataException("Unable to set property $property_name as no entity has been provided."); + } + $this->entity->set($property_name, $value, $notify); + } + + /** + * {@inheritdoc} + */ + public function getProperties($include_computed = FALSE) { + if (!isset($this->entity)) { + throw new MissingDataException('Unable to get properties as no entity has been provided.'); + } + return $this->getConfigTypedData()->getProperties($include_computed); + } + + /** + * {@inheritdoc} + */ + public function onChange($property_name) { + if (isset($this->entity)) { + // Let the entity know of any changes. + $this->getConfigTypedData()->onChange($property_name); + } + } + + /** + * {@inheritdoc} + */ + public function getIterator() { + if (isset($this->entity)) { + return $this->getConfigTypedData()->getIterator(); + } + return new \ArrayIterator([]); + } + + /** + * Gets the typed data manager. + * + * @return \Drupal\Core\Config\TypedConfigManagerInterface + * The typed data manager. + */ + public function getTypedDataManager() { + if (empty($this->typedDataManager)) { + $this->typedDataManager = \Drupal::service('config.typed'); + } + + return $this->typedDataManager; + } + + /** + * {@inheritdoc} + */ + public function applyDefaultValue($notify = TRUE) { + throw new \BadMethodCallException('Method not supported'); + } + + /** + * Get typed data for config entity. + * + * @return \Drupal\Core\TypedData\ComplexDataInterface + * The typed data. + */ + protected function getConfigTypedData() { + return $this->getTypedDataManager()->createFromNameAndData($this->entity->getConfigDependencyName(), $this->entity->toArray()); + } + +} diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/EntityDeriver.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/EntityDeriver.php index 98be20e04e..d02d2a8a66 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/EntityDeriver.php +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/EntityDeriver.php @@ -2,8 +2,11 @@ namespace Drupal\Core\Entity\Plugin\DataType\Deriver; +use Drupal\Core\Config\Entity\ConfigEntityTypeInterface; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; +use Drupal\Core\Entity\Plugin\DataType\ConfigEntityAdapter; +use Drupal\Core\Entity\Plugin\DataType\EntityAdapter; use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -86,7 +89,9 @@ public function getDerivativeDefinitions($base_plugin_definition) { $this->derivatives[''] = $base_plugin_definition; // Add definitions for each entity type and bundle. foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) { + $class = $entity_type instanceof ConfigEntityTypeInterface ? ConfigEntityAdapter::class : EntityAdapter::class; $this->derivatives[$entity_type_id] = [ + 'class' => $class, 'label' => $entity_type->getLabel(), 'constraints' => $entity_type->getConstraints(), ] + $base_plugin_definition; @@ -95,6 +100,7 @@ public function getDerivativeDefinitions($base_plugin_definition) { foreach ($this->bundleInfoService->getBundleInfo($entity_type_id) as $bundle => $bundle_info) { if ($bundle !== $entity_type_id) { $this->derivatives[$entity_type_id . ':' . $bundle] = [ + 'class' => $class, 'label' => $bundle_info['label'], 'constraints' => $this->derivatives[$entity_type_id]['constraints'] ] + $base_plugin_definition; diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php index db194b5485..537bc63c72 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php @@ -94,8 +94,6 @@ public function set($property_name, $value, $notify = TRUE) { throw new MissingDataException("Unable to set property $property_name as no entity has been provided."); } if (!$this->entity instanceof FieldableEntityInterface) { - // @todo: Add support for config entities in - // https://www.drupal.org/node/1818574. throw new \InvalidArgumentException("Unable to set unknown property $property_name."); } // This will throw an exception for unknown fields. @@ -111,8 +109,6 @@ public function getProperties($include_computed = FALSE) { throw new MissingDataException('Unable to get properties as no entity has been provided.'); } if (!$this->entity instanceof FieldableEntityInterface) { - // @todo: Add support for config entities in - // https://www.drupal.org/node/1818574. return []; } return $this->entity->getFields($include_computed); diff --git a/core/tests/Drupal/KernelTests/Core/Entity/ConfigEntityAdapterTest.php b/core/tests/Drupal/KernelTests/Core/Entity/ConfigEntityAdapterTest.php new file mode 100644 index 0000000000..4d24e9511a --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/ConfigEntityAdapterTest.php @@ -0,0 +1,146 @@ +installConfig(static::$modules); + + // ConfigTest::create doesn't work with the following exception: + // "Multiple entity types found for Drupal\config_test\Entity\ConfigTest." + $this->entity = \Drupal::entityTypeManager()->getStorage('config_test')->create([ + 'id' => 'system', + 'label' => 'foobar', + 'weight' => 1, + ]); + } + + /** + * Tests that the entity deriver sets the correct class for config entities. + */ + public function testEntityDeriver() { + $definition = \Drupal::typedDataManager()->getDefinition('entity:config_test'); + $this->assertEquals(ConfigEntityAdapter::class, $definition['class']); + } + + /** + * Tests validating a config entity via the adapter. + */ + public function testValidate() { + $adapter = ConfigEntityAdapter::createFromEntity($this->entity); + $violations = $adapter->validate(); + $this->assertEmpty($violations); + $this->entity = \Drupal::entityTypeManager()->getStorage('config_test')->create([ + 'id' => 'system', + 'label' => 'foobar', + // Set weight to be a string which should not validate. + 'weight' => 'very heavy', + ]); + $adapter = ConfigEntityAdapter::createFromEntity($this->entity); + $violations = $adapter->validate(); + $this->assertCount(1, $violations); + $violation = $violations->get(0); + $this->assertEquals('This value should be of the correct primitive type.', $violation->getMessage()); + $this->assertEquals('weight', $violation->getPropertyPath()); + } + + /** + * Tests getting config entity properties via the adapter. + */ + public function testGetProperties() { + $expected_properties = [ + 'uuid' => StringData::class, + 'langcode' => StringData::class, + 'status' => BooleanData::class, + 'dependencies' => Mapping::class, + 'id' => StringData::class, + 'label' => StringData::class, + 'weight' => IntegerData::class, + 'style' => StringData::class, + 'size' => StringData::class, + 'size_value' => StringData::class, + 'protected_property' => StringData::class, + ]; + $properties = ConfigEntityAdapter::createFromEntity($this->entity)->getProperties(); + $keys = []; + foreach ($properties as $key => $property) { + $keys[] = $key; + $this->assertInstanceOf($expected_properties[$key], $property); + } + $this->assertSame(array_keys($expected_properties), $keys); + } + + /** + * Tests getting a config entity property via the adapter. + */ + public function testGetProperty() { + $adapter = ConfigEntityAdapter::createFromEntity($this->entity); + $this->assertEquals($this->entity->weight, $adapter->get('weight')->getValue()); + $this->assertEquals($this->entity->id(), $adapter->get('id')->getValue()); + $this->assertEquals($this->entity->label, $adapter->get('label')->getValue()); + } + + /** + * Tests setting a config entity property via the adapter. + */ + public function testSetProperty() { + $adapter = ConfigEntityAdapter::createFromEntity($this->entity); + // Get the value via typed data to ensure that the typed representation is + // updated correctly when the value is set. + $this->assertEquals(1, $adapter->get('weight')->getValue()); + + $adapter->set('weight', 2); + $this->assertEquals(2, $this->entity->weight); + // Ensure the typed data is updated via the set too. + $this->assertEquals(2, $adapter->get('weight')->getValue()); + } + + /** + * Tests getting the config entity as a string via the adapter. + */ + public function testGetString() { + $adapter = ConfigEntityAdapter::createFromEntity($this->entity); + $this->assertEquals('foobar', $adapter->getString()); + } + + /** + * Tests applying default values to the config entity is not supported. + */ + public function testApplyDefaultValue() { + $this->setExpectedException(\BadMethodCallException::class, 'Method not supported'); + $adapter = ConfigEntityAdapter::createFromEntity($this->entity); + $adapter->applyDefaultValue(); + } + +}