Commit 19937298 authored by catch's avatar catch

Issue #2666798 by dawehner: Add an entity revision parameter converter & route enhancer

parent 85ef01de
<?php
/**
* @file
* Contains \Drupal\Core\ParamConverter\EntityRevisionParamConverter.
*/
namespace Drupal\Core\ParamConverter;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\Routing\Route;
/**
* Parameter converter for upcasting entity revision IDs to full objects.
*
* This is useful for pages which want to show a specific revision, like
* "/entity_example/{entity_example}/revision/{entity_example_revision}".
*
*
* In order to use it you should specify some additional options in your route:
* @code
* example.route:
* path: /foo/{entity_example_revision}
* options:
* parameters:
* entity_example_revision:
* type: entity_revision:entity_example
* @endcode
*/
class EntityRevisionParamConverter implements ParamConverterInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Creates a new EntityRevisionParamConverter instance.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public function convert($value, $definition, $name, array $defaults) {
list (, $entity_type_id) = explode(':', $definition['type'], 2);
$entity_storage = $this->entityTypeManager->getStorage($entity_type_id);
return $entity_storage->loadRevision($value);
}
/**
* {@inheritdoc}
*/
public function applies($definition, $name, Route $route) {
return isset($definition['type']) && strpos($definition['type'], 'entity_revision:') !== FALSE;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Routing\Enhancer\EntityRevisionRouteEnhancer.
*/
namespace Drupal\Core\Routing\Enhancer;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Adds _entity_revision to the request attributes, if possible.
*/
class EntityRevisionRouteEnhancer implements RouteEnhancerInterface {
/**
* {@inheritdoc}
*/
public function applies(Route $route) {
// Check whether there is any entity revision parameter.
$parameters = $route->getOption('parameters') ?: [];
foreach ($parameters as $info) {
if (isset($info['type']) && strpos($info['type'], 'entity_revision:') === 0) {
return TRUE;
}
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function enhance(array $defaults, Request $request) {
/** @var \Symfony\Component\Routing\Route $route */
$route = $defaults[RouteObjectInterface::ROUTE_OBJECT];
$options = $route->getOptions();
if (isset($options['parameters'])) {
foreach ($options['parameters'] as $name => $details) {
if (!empty($details['type']) && strpos($details['type'], 'entity_revision:') !== FALSE) {
$defaults['_entity_revision'] = $defaults[$name];
break;
}
}
}
return $defaults;
}
}
<?php
/**
* @file
* Contains Drupal\Tests\Core\Enhancer\EntityRevisionRouteEnhancerTest.
*/
namespace Drupal\Tests\Core\Enhancer;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Routing\Enhancer\EntityRevisionRouteEnhancer;
use Drupal\Tests\UnitTestCase;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* @coversDefaultClass \Drupal\Core\Routing\Enhancer\EntityRevisionRouteEnhancer
* @group Entity
*/
class EntityRevisionRouteEnhancerTest extends UnitTestCase {
/**
* @var \Drupal\entity\RouteEnhancer\EntityRevisionRouteEnhancer
*/
protected $routeEnhancer;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->routeEnhancer = new EntityRevisionRouteEnhancer();
}
/**
* @covers ::applies
* @dataProvider providerTestApplies
*/
public function testApplies(Route $route, $expected) {
$this->assertEquals($expected, $this->routeEnhancer->applies($route));
}
public function providerTestApplies() {
$data = [];
$data['no-parameter'] = [new Route('/test-path'), FALSE];
$data['none-revision-parameters'] = [new Route('/test-path/{entity_test}', [], [], ['parameters' => ['entity_test' => ['type' => 'entity:entity_test']]]), FALSE];
$data['with-revision-parameter'] = [new Route('/test-path/{entity_test_revision}', [], [], ['parameters' => ['entity_test_revision' => ['type' => 'entity_revision:entity_test']]]), TRUE];
return $data;
}
/**
* @covers ::enhance
*/
public function testEnhanceWithoutEntityRevision() {
$route = new Route('/test-path/{entity_test}', [], [], ['parameters' => ['entity_test' => ['type' => 'entity:entity_test']]]);
$request = Request::create('/test-path/123');
$entity = $this->prophesize(EntityInterface::class);
$defaults = [];
$defaults['entity_test'] = $entity->reveal();
$defaults[RouteObjectInterface::ROUTE_OBJECT] = $route;
$this->assertEquals($defaults, $this->routeEnhancer->enhance($defaults, $request));
}
/**
* @covers ::enhance
*/
public function testEnhanceWithEntityRevision() {
$route = new Route('/test-path/{entity_test_revision}', [], [], ['parameters' => ['entity_test_revision' => ['type' => 'entity_revision:entity_test']]]);
$request = Request::create('/test-path/123');
$entity = $this->prophesize(EntityInterface::class);
$defaults = [];
$defaults['entity_test_revision'] = $entity->reveal();
$defaults[RouteObjectInterface::ROUTE_OBJECT] = $route;
$expected = $defaults;
$expected['_entity_revision'] = $defaults['entity_test_revision'];
$this->assertEquals($expected, $this->routeEnhancer->enhance($defaults, $request));
}
}
<?php
/**
* @file
* Contains \Drupal\Tests\Core\ParamConverter\EntityRevisionParamConverterTest.
*/
namespace Drupal\Tests\Core\ParamConverter;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\ParamConverter\EntityRevisionParamConverter;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\Routing\Route;
/**
* @coversDefaultClass \Drupal\Core\ParamConverter\EntityRevisionParamConverter
* @group entity
*/
class EntityRevisionParamConverterTest extends UnitTestCase {
/**
* The tested entity revision param converter.
*
* @var \Drupal\entity\ParamConverter\EntityRevisionParamConverter
*/
protected $converter;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->converter = new EntityRevisionParamConverter($this->prophesize(EntityTypeManagerInterface::class)->reveal());
}
protected function getTestRoute() {
$route = new Route('/test/{test_revision}');
$route->setOption('parameters', [
'test_revision' => [
'type' => 'entity_revision:test',
],
]);
return $route;
}
/**
* @covers ::applies
*/
public function testNonApplyingRoute() {
$route = new Route('/test');
$this->assertFalse($this->converter->applies([], 'test_revision', $route));
}
/**
* @covers ::applies
*/
public function testApplyingRoute() {
$route = $this->getTestRoute();
$this->assertTrue($this->converter->applies($route->getOption('parameters')['test_revision'], 'test_revision', $route));
}
/**
* @covers ::convert
*/
public function testConvert() {
$entity = $this->prophesize(EntityInterface::class)->reveal();
$storage = $this->prophesize(EntityStorageInterface::class);
$storage->loadRevision(1)->willReturn($entity);
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
$entity_type_manager->getStorage('test')->willReturn($storage->reveal());
$converter = new EntityRevisionParamConverter($entity_type_manager->reveal());
$route = $this->getTestRoute();
$result = $converter->convert(1, $route->getOption('parameters')['test_revision'], 'test_revision', ['test_revision' => 1]);
$this->assertSame($entity, $result);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment