diff --git a/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php new file mode 100644 index 0000000..b9667d2 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php @@ -0,0 +1,69 @@ +entityManager = $entity_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity') + ); + } + + /** + * Provides a page to render a single entity. + * + * @param \Drupal\Core\Entity\EntityInterface $_entity + * The Entity to be rendered. Note this variable is named $_entity rather + * than $entity to prevent collisions with other named placeholders in the + * route. + * @param string $view_mode + * (optional) The view mode that should be used to display the entity. + * Defaults to 'full'. + * @param string $langcode + * (optional) For which language the entity should be rendered, defaults to + * the current content language. + * + * @return array + * A render array as expected by drupal_render(). + */ + public function view(EntityInterface $_entity, $view_mode = 'full', $langcode = NULL) { + return $this->entityManager + ->getRenderController($_entity->entityType()) + ->view($_entity, $view_mode, $langcode); + } + +} diff --git a/core/lib/Drupal/Core/Entity/Enhancer/EntityRouteEnhancer.php b/core/lib/Drupal/Core/Entity/Enhancer/EntityRouteEnhancer.php index f2f6648..2cf8d47 100644 --- a/core/lib/Drupal/Core/Entity/Enhancer/EntityRouteEnhancer.php +++ b/core/lib/Drupal/Core/Entity/Enhancer/EntityRouteEnhancer.php @@ -9,6 +9,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Drupal\Core\ContentNegotiation; /** @@ -47,6 +48,54 @@ public function enhance(array $defaults, Request $request) { $defaults['entity_type'] = $defaults['_entity_list']; unset($defaults['_entity_list']); } + elseif (!empty($defaults['_entity_view'])) { + $defaults['_controller'] = 'controller.page:content'; + $defaults['_content'] = '\Drupal\Core\Entity\Controller\EntityViewController::view'; + if (strpos($defaults['_entity_view'], '.') !== FALSE) { + // The _entity_view entry is of the form entity_type.view_mode. + list($entity_type, $view_mode) = explode('.', $defaults['_entity_view']); + $defaults['view_mode'] = $view_mode; + } + else { + // Only the entity type is nominated, the view mode will use the + // default. + $entity_type = $defaults['_entity_view']; + } + // Set by reference so that we get the upcast value. + if (!empty($defaults[$entity_type])) { + $defaults['_entity'] = &$defaults[$entity_type]; + } + else { + // The entity is not keyed by its entity_type. Attempt to find it + // using a converter. + $route = $defaults[RouteObjectInterface::ROUTE_OBJECT]; + if ($route && is_object($route)) { + $options = $route->getOptions(); + if (isset($options['parameters'])) { + foreach ($options['parameters'] as $name => $details) { + if (!empty($details['type'])) { + $type = $details['type']; + // Type is of the form entity:{entity_type}. + $parameter_entity_type = substr($type, strlen('entity:')); + if ($entity_type == $parameter_entity_type) { + // We have the matching entity type. Set the '_entity' key + // to point to this named placeholder. The entity in this + // position is the one being rendered. + $defaults['_entity'] = &$defaults[$name]; + } + } + } + } + else { + throw new \RuntimeException(sprintf('Failed to find entity of type %s in route named %s', $entity_type, $defaults[RouteObjectInterface::ROUTE_NAME])); + } + } + else { + throw new \RuntimeException(sprintf('Failed to find entity of type %s in route named %s', $entity_type, $defaults[RouteObjectInterface::ROUTE_NAME])); + } + } + unset($defaults['_entity_view']); + } } return $defaults; } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewControllerTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewControllerTest.php new file mode 100644 index 0000000..502a206 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewControllerTest.php @@ -0,0 +1,71 @@ + 'Entity View Controller', + 'description' => 'Tests EntityViewController functionality.', + 'group' => 'Entity API', + ); + } + + function setUp() { + parent::setUp(); + // Create some dummy entity_test_render entities. + for ($i = 0; $i < 2; $i++) { + $random_label = $this->randomName(); + $data = array('bundle' => 'entity_test_render', 'name' => $random_label); + $entity_test = $this->container->get('plugin.manager.entity')->getStorageController('entity_test_render')->create($data); + $entity_test->save(); + $this->entities[] = $entity_test; + } + + } + + /** + * Tests EntityViewController. + */ + function testEntityViewController() { + foreach ($this->entities as $entity) { + $this->drupalGet('entity-test-render/' . $entity->id()); + $this->assertRaw($entity->label()); + $this->assertRaw('full'); + + $this->drupalGet('entity-test-render-converter/' . $entity->id()); + $this->assertRaw($entity->label()); + $this->assertRaw('full'); + + $this->drupalGet('entity-test-render-no-view-mode/' . $entity->id()); + $this->assertRaw($entity->label()); + $this->assertRaw('full'); + } + } +} diff --git a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml new file mode 100644 index 0000000..645fae1 --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml @@ -0,0 +1,24 @@ +entity_test_render: + pattern: '/entity-test-render/{entity_test_render}' + defaults: + _entity_view: 'entity_test_render.full' + requirements: + _access: 'TRUE' + +entity_test_render_options: + pattern: '/entity-test-render-converter/{foo}' + options: + parameters: + foo: + type: 'entity:entity_test_render' + defaults: + _entity_view: 'entity_test_render.full' + requirements: + _access: 'TRUE' + +entity_test_render_no_view_mode: + pattern: '/entity-test-render-no-view-mode/{entity_test_render}' + defaults: + _entity_view: 'entity_test_render' + requirements: + _access: 'TRUE' diff --git a/core/tests/Drupal/Tests/Core/Entity/Controller/EntityViewControllerTest.php b/core/tests/Drupal/Tests/Core/Entity/Controller/EntityViewControllerTest.php new file mode 100644 index 0000000..cfa1e29 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Entity/Controller/EntityViewControllerTest.php @@ -0,0 +1,62 @@ + 'Entity route enhancer test', + 'description' => 'Tests the entity route enhancer.', + 'group' => 'Entity' + ); + } + + /** + * Tests the enhancer method. + * + * @see \Drupal\Core\Entity\Controller\EntityViewController::view() + */ + public function testView() { + + // Mock a render controller. + $render_controller = $this->getMockBuilder('Drupal\entity_test\EntityTestRenderController') + ->disableOriginalConstructor() + ->getMock(); + $render_controller->expects($this->any()) + ->method('view') + ->will($this->returnValue('Output from rendering the entity')); + + // Mock an entity manager. + $entity_manager = $this->getMockBuilder('Drupal\Core\Entity\EntityManager') + ->disableOriginalConstructor() + ->getMock(); + $entity_manager->expects($this->any()) + ->method('getRenderController') + ->will($this->returnValue($render_controller)); + + // Mock an 'entity_test_render' entity. + $entity = $this->getMockBuilder('Drupal\entity_test\Plugin\Core\Entity\EntityTestRender') + ->disableOriginalConstructor() + ->getMock(); + + // Initialize the controller to test. + $controller = new EntityViewController($entity_manager); + + // Test the view method. + $this->assertEquals($controller->view($entity, 'full'), 'Output from rendering the entity'); + } +} diff --git a/core/tests/Drupal/Tests/Core/Entity/Enhancer/EntityRouteEnhancerTest.php b/core/tests/Drupal/Tests/Core/Entity/Enhancer/EntityRouteEnhancerTest.php index ffd60a1..17f454e 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Enhancer/EntityRouteEnhancerTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Enhancer/EntityRouteEnhancerTest.php @@ -10,6 +10,7 @@ use Drupal\Core\ContentNegotiation; use Drupal\Core\Entity\Enhancer\EntityRouteEnhancer; use Drupal\Tests\UnitTestCase; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; /** @@ -62,6 +63,52 @@ public function testEnhancer() { $this->assertEquals('\Drupal\Core\Entity\Controller\EntityListController::listing', $defaults['_content'], 'The entity list controller was not set.'); $this->assertEquals('entity_test.default', $defaults['entity_type']); $this->assertFalse(isset($defaults['_entity_list'])); + + // Set _entity_view and ensure that the entity view controller is set. + $defaults = array(); + $defaults['_entity_view'] = 'entity_test.full'; + $defaults['entity_test'] = 'Mock entity'; + $defaults = $route_enhancer->enhance($defaults, $request); + $this->assertEquals('controller.page:content', $defaults['_controller']); + $this->assertEquals('\Drupal\Core\Entity\Controller\EntityViewController::view', $defaults['_content'], 'The entity view controller was not set.'); + $this->assertEquals($defaults['_entity'], 'Mock entity'); + $this->assertEquals($defaults['view_mode'], 'full'); + $this->assertFalse(isset($defaults['_entity_view'])); + + // Set _entity_view and ensure that the entity view controller is set using + // a converter. + $defaults = array(); + $defaults['_entity_view'] = 'entity_test.full'; + $defaults['foo'] = 'Mock entity'; + // Add a converter. + $options['parameters']['foo'] = array('type' => 'entity:entity_test'); + // Set the route. + $route = $this->getMockBuilder('Symfony\Component\Routing\Route') + ->disableOriginalConstructor() + ->getMock(); + + $route->expects($this->any()) + ->method('getOptions') + ->will($this->returnValue($options)); + + $defaults[RouteObjectInterface::ROUTE_OBJECT] = $route; + $defaults = $route_enhancer->enhance($defaults, $request); + $this->assertEquals('controller.page:content', $defaults['_controller']); + $this->assertEquals('\Drupal\Core\Entity\Controller\EntityViewController::view', $defaults['_content'], 'The entity view controller was not set.'); + $this->assertEquals($defaults['_entity'], 'Mock entity'); + $this->assertEquals($defaults['view_mode'], 'full'); + $this->assertFalse(isset($defaults['_entity_view'])); + + // Set _entity_view without a view mode. + $defaults = array(); + $defaults['_entity_view'] = 'entity_test'; + $defaults['entity_test'] = 'Mock entity'; + $defaults = $route_enhancer->enhance($defaults, $request); + $this->assertEquals('controller.page:content', $defaults['_controller']); + $this->assertEquals('\Drupal\Core\Entity\Controller\EntityViewController::view', $defaults['_content'], 'The entity view controller was not set.'); + $this->assertEquals($defaults['_entity'], 'Mock entity'); + $this->assertTrue(empty($defaults['view_mode'])); + $this->assertFalse(isset($defaults['_entity_view'])); } }