Issue #2808163 by amateescu, samuel.mortenson, dawehner: Support dynamic entity types in the EntityRevisionParamConverter

8.7.x
Nathaniel Catchpole 2019-03-04 16:47:25 +00:00
parent 53c6cc8a07
commit d649b7f45a
5 changed files with 123 additions and 43 deletions

View File

@ -0,0 +1,44 @@
<?php
namespace Drupal\Core\ParamConverter;
/**
* Provides a trait to replace dynamic entity types in routes.
*/
trait DynamicEntityTypeParamConverterTrait {
/**
* Determines the entity type ID given a route definition and route defaults.
*
* @param mixed $definition
* The parameter definition provided in the route options.
* @param string $name
* The name of the parameter.
* @param array $defaults
* The route defaults array.
*
* @return string
* The entity type ID.
*
* @throws \Drupal\Core\ParamConverter\ParamNotConvertedException
* Thrown when the dynamic entity type is not found in the route defaults.
*/
protected function getEntityTypeFromDefaults($definition, $name, array $defaults) {
$type_part = strstr($definition['type'], ':');
if (!$type_part) {
throw new ParamNotConvertedException(sprintf('The type definition "%s" is invalid. The expected format is "entity_revision:<entity_type_id>".', $definition['type']));
}
$entity_type_id = substr($type_part, 1);
// If the entity type is dynamic, it will be pulled from the route defaults.
if (strpos($entity_type_id, '{') === 0) {
$entity_type_slug = substr($entity_type_id, 1, -1);
if (!isset($defaults[$entity_type_slug])) {
throw new ParamNotConvertedException(sprintf('The "%s" parameter was not converted because the "%s" parameter is missing.', $name, $entity_type_slug));
}
$entity_type_id = $defaults[$entity_type_slug];
}
return $entity_type_id;
}
}

View File

@ -67,7 +67,9 @@ use Symfony\Component\Routing\Route;
* @see entities_revisions_translations
*/
class EntityConverter implements ParamConverterInterface {
use DeprecatedServicePropertyTrait;
use DynamicEntityTypeParamConverterTrait;
/**
* {@inheritdoc}
@ -236,36 +238,6 @@ class EntityConverter implements ParamConverterInterface {
return FALSE;
}
/**
* Determines the entity type ID given a route definition and route defaults.
*
* @param mixed $definition
* The parameter definition provided in the route options.
* @param string $name
* The name of the parameter.
* @param array $defaults
* The route defaults array.
*
* @return string
* The entity type ID.
*
* @throws \Drupal\Core\ParamConverter\ParamNotConvertedException
* Thrown when the dynamic entity type is not found in the route defaults.
*/
protected function getEntityTypeFromDefaults($definition, $name, array $defaults) {
$entity_type_id = substr($definition['type'], strlen('entity:'));
// If the entity type is dynamic, it will be pulled from the route defaults.
if (strpos($entity_type_id, '{') === 0) {
$entity_type_slug = substr($entity_type_id, 1, -1);
if (!isset($defaults[$entity_type_slug])) {
throw new ParamNotConvertedException(sprintf('The "%s" parameter was not converted because the "%s" parameter is missing', $name, $entity_type_slug));
}
$entity_type_id = $defaults[$entity_type_slug];
}
return $entity_type_id;
}
/**
* Returns a language manager instance.
*

View File

@ -2,8 +2,10 @@
namespace Drupal\Core\ParamConverter;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\TranslatableInterface;
use Symfony\Component\Routing\Route;
/**
@ -25,6 +27,8 @@ use Symfony\Component\Routing\Route;
*/
class EntityRevisionParamConverter implements ParamConverterInterface {
use DynamicEntityTypeParamConverterTrait;
/**
* The entity type manager.
*
@ -56,9 +60,16 @@ class EntityRevisionParamConverter implements ParamConverterInterface {
* {@inheritdoc}
*/
public function convert($value, $definition, $name, array $defaults) {
list (, $entity_type_id) = explode(':', $definition['type'], 2);
$entity_type_id = $this->getEntityTypeFromDefaults($definition, $name, $defaults);
$entity = $this->entityTypeManager->getStorage($entity_type_id)->loadRevision($value);
return $this->entityRepository->getTranslationFromContext($entity);
// If the entity type is translatable, ensure we return the proper
// translation object for the current context.
if ($entity instanceof EntityInterface && $entity instanceof TranslatableInterface) {
$entity = $this->entityRepository->getTranslationFromContext($entity, NULL, ['operation' => 'entity_upcast']);
}
return $entity;
}
/**

View File

@ -150,7 +150,7 @@ class EntityConverterTest extends UnitTestCase {
* Tests the convert() method with an invalid dynamic entity type.
*/
public function testConvertWithInvalidDynamicEntityType() {
$this->setExpectedException(ParamNotConvertedException::class, 'The "foo" parameter was not converted because the "invalid_id" parameter is missing');
$this->setExpectedException(ParamNotConvertedException::class, 'The "foo" parameter was not converted because the "invalid_id" parameter is missing.');
$this->entityConverter->convert('id', ['type' => 'entity:{invalid_id}'], 'foo', ['foo' => 'id']);
}

View File

@ -2,11 +2,12 @@
namespace Drupal\Tests\Core\ParamConverter;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\ParamConverter\EntityRevisionParamConverter;
use Drupal\Core\ParamConverter\ParamNotConvertedException;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\Routing\Route;
@ -19,7 +20,7 @@ class EntityRevisionParamConverterTest extends UnitTestCase {
/**
* The tested entity revision param converter.
*
* @var \Drupal\entity\ParamConverter\EntityRevisionParamConverter
* @var \Drupal\Core\ParamConverter\EntityRevisionParamConverter
*/
protected $converter;
@ -62,22 +63,74 @@ class EntityRevisionParamConverterTest extends UnitTestCase {
}
/**
* Tests the convert() method.
*
* @dataProvider providerTestConvert
*
* @covers ::convert
*/
public function testConvert() {
$entity = $this->prophesize(EntityInterface::class)->reveal();
public function testConvert($value, array $definition, array $defaults, $expected_result) {
$storage = $this->prophesize(EntityStorageInterface::class);
$storage->loadRevision(1)->willReturn($entity);
$storage->loadRevision('valid_id')->willReturn((object) ['revision_id' => 'valid_id']);
$storage->loadRevision('invalid_id')->willReturn(NULL);
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
$entity_type_manager->getStorage('test')->willReturn($storage->reveal());
$entity_type_manager->getStorage('entity_test')->willReturn($storage->reveal());
$entity_repository = $this->prophesize(EntityRepositoryInterface::class);
$entity_repository->getTranslationFromContext($entity)->willReturn($entity);
$converter = new EntityRevisionParamConverter($entity_type_manager->reveal(), $entity_repository->reveal());
$route = $this->getTestRoute();
$result = $converter->convert(1, $route->getOption('parameters')['test_revision'], 'test_revision', ['test_revision' => 1]);
$this->assertSame($entity, $result);
$result = $converter->convert($value, $definition, 'test_revision', $defaults);
$this->assertEquals($expected_result, $result);
}
/**
* Provides test data for testConvert
*/
public function providerTestConvert() {
$data = [];
// Existing entity type.
$data[] = ['valid_id', ['type' => 'entity_revision:entity_test'], ['test_revision' => 'valid_id'], (object) ['revision_id' => 'valid_id']];
// Invalid ID.
$data[] = ['invalid_id', ['type' => 'entity_revision:entity_test'], ['test_revision' => 'invalid_id'], NULL];
// Entity type placeholder.
$data[] = ['valid_id', ['type' => 'entity_revision:{entity_type}'], ['test_revision' => 'valid_id', 'entity_type' => 'entity_test'], (object) ['revision_id' => 'valid_id']];
return $data;
}
/**
* Tests the convert() method with an invalid entity type ID.
*
* @covers ::convert
*/
public function testConvertWithInvalidEntityType() {
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
$entity_type_manager->getStorage('invalid_entity_type_id')->willThrow(new InvalidPluginDefinitionException('invalid_entity_type_id'));
$entity_repository = $this->prophesize(EntityRepositoryInterface::class);
$converter = new EntityRevisionParamConverter($entity_type_manager->reveal(), $entity_repository->reveal());
$this->setExpectedException(InvalidPluginDefinitionException::class);
$converter->convert('valid_id', ['type' => 'entity_revision:invalid_entity_type_id'], 'foo', ['foo' => 'valid_id']);
}
/**
* Tests the convert() method with an invalid dynamic entity type ID.
*
* @covers ::convert
*/
public function testConvertWithInvalidType() {
$this->setExpectedException(ParamNotConvertedException::class, 'The type definition "entity_revision_{entity_type_id}" is invalid. The expected format is "entity_revision:<entity_type_id>".');
$this->converter->convert('valid_id', ['type' => 'entity_revision_{entity_type_id}'], 'foo', ['foo' => 'valid_id']);
}
/**
* Tests the convert() method with an invalid dynamic entity type ID.
*
* @covers ::convert
*/
public function testConvertWithInvalidDynamicEntityType() {
$this->setExpectedException(ParamNotConvertedException::class, 'The "foo" parameter was not converted because the "invalid_entity_type_id" parameter is missing.');
$this->converter->convert('valid_id', ['type' => 'entity_revision:{invalid_entity_type_id}'], 'foo', ['foo' => 'valid_id']);
}
}