Updated: Comment #0

Problem/Motivation

We have _entity_list, _entity_form already, lets add _entity_view to cut down on boilerplate code for viewing an entity

Proposed resolution

Add an _entity_view option for routing of the form entity_type.view_mode

Remaining tasks

Write the patch
Tests
Review

User interface changes

None

API changes

No need for boilerplate code for viewing an entity

#1987898: Convert user_view_page() to a new style controller
#2008356: Provide a _entity_list route default to replace Controller\EntityListController and mimic _entity_form
#731724: Convert comment settings into a field to make them work with CMI and non-node entities

Files: 
CommentFileSizeAuthor
#29 _entity_view-2040265.29.interdiff.txt867 byteslarowlan
#29 _entity_view-2040265.29.patch13.76 KBlarowlan
PASSED: [[SimpleTest]]: [MySQL] 57,889 pass(es).
[ View ]
#27 _entity_view-2040265.27.interdiff.txt6.98 KBlarowlan
#27 _entity_view-2040265.27.patch13.76 KBlarowlan
FAILED: [[SimpleTest]]: [MySQL] 57,882 pass(es), 1 fail(s), and 0 exception(s).
[ View ]
#19 _entity_view-2040265.19.real_.interdiff.txt6.52 KBlarowlan
#19 _entity_view-2040265.19.patch12.43 KBlarowlan
PASSED: [[SimpleTest]]: [MySQL] 57,560 pass(es).
[ View ]
#17 _entity_view-2040265.19.fail_.patch11.37 KBlarowlan
FAILED: [[SimpleTest]]: [MySQL] 57,525 pass(es), 4 fail(s), and 0 exception(s).
[ View ]
#17 _entity_view-2040265.19.pass_.patch11.41 KBlarowlan
FAILED: [[SimpleTest]]: [MySQL] 57,436 pass(es), 5 fail(s), and 2 exception(s).
[ View ]
#17 _entity_view-2040265.19.interdiff.txt3.97 KBlarowlan
#9 _entity_view-2040265.9.interdiff.txt5.72 KBlarowlan
#9 _entity_view-2040265.9.patch10.99 KBlarowlan
PASSED: [[SimpleTest]]: [MySQL] 57,481 pass(es).
[ View ]
#3 _entity_view-2040265.1.patch6.66 KBlarowlan
PASSED: [[SimpleTest]]: [MySQL] 56,789 pass(es).
[ View ]
#1 _entity_view-2040265.patch0 byteslarowlan
PASSED: [[SimpleTest]]: [MySQL] 57,262 pass(es).
[ View ]

Comments

larowlan’s picture

Status:Active» Needs review
StatusFileSize
new0 bytes
PASSED: [[SimpleTest]]: [MySQL] 57,262 pass(es).
[ View ]

It pains me to add another web test, but I think we have to given we're concerned with rendering output here.

jibran’s picture

Empty patch

larowlan’s picture

StatusFileSize
new6.66 KB
PASSED: [[SimpleTest]]: [MySQL] 56,789 pass(es).
[ View ]

doh!

Berdir’s picture

+++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.phpundefined
@@ -0,0 +1,66 @@
+  public function view(EntityInterface $entity, $view_mode, $langcode = NULL) {
+    return $this->entityManager
+      ->getRenderController($entity->entityType())
+      ->view($entity, $view_mode, $langcode);
+  }

Having $langcode here seems useless, when you can't set it?

I'd rather either set the langcode to anguageManager->getLanguage(Language::TYPE_CONTENT)->id or actually just get the corresponding translation ($entity->getTranslation(.. content langcode..) if $entity implements the TranslatableInterface and hasTranslation (content langcode).

You want to check with @plach what makes sense. But I think we want to get deprecate $langcode arguments in favor of passing the right entity translation around. But I guess we can't actually remove those arguments anymore?

larowlan’s picture

@berdir you can set the langcode in your routing.yml

some_route:
  pattern: '/some/callback/{some_entity_type}'
  defaults:
    _entity_view: 'some_entity_type.full'
    langcode: fr
  requirements:
    _permissions: 'administer foobars'
dawehner’s picture

+++ b/core/lib/Drupal/Core/Entity/Enhancer/EntityRouteEnhancer.phpundefined
@@ -47,6 +47,15 @@ public function enhance(array $defaults, Request $request) {
+        // Set by reference so that we get the upcast value.
+        $defaults['entity'] = &$defaults[$entity_type];

This works in most cases, but what happens if you have specified a different variable name? See entityConverter

+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewControllerTest.phpundefined
@@ -0,0 +1,63 @@
+class EntityViewControllerTest extends WebTestBase {

Is there a specific reason why you haven't wrote a unit test?

larowlan’s picture

Is there a specific reason why you haven't wrote a unit test?

Well I figured render array = web test. Also I really want this route for #731724: Convert comment settings into a field to make them work with CMI and non-node entities so we can use entity_test_render instead of user for our 'non-node' comment tests. To be honest, when that goes in, this test can become a unit test instead. So I guess it could be just a unit test.

Good call on the variable names. I will incorporate that too.

andypost’s picture

Issue tags:+D8MI, +language-content

awesome idea. let the scope of patch focus on route and probably language for follow-up

larowlan’s picture

StatusFileSize
new10.99 KB
PASSED: [[SimpleTest]]: [MySQL] 57,481 pass(es).
[ View ]
new5.72 KB

So this
a) handles converters (eg $options['converters'])
b) adds test coverage for converters
c) adds a unit test for the controller

Note the web test is retained but can/should be removed once we start using _entity_view in web tests in #731724: Convert comment settings into a field to make them work with CMI and non-node entities with a comment field attached to an entity_test_render entity.

dawehner’s picture

tim.plunkett’s picture

#9: _entity_view-2040265.9.patch queued for re-testing.

tim.plunkett’s picture

Priority:Normal» Critical
Status:Needs review» Reviewed & tested by the community
Issue tags:+blocker

It didn't break because it just adds onto EntityRouteEnhancer instead of adding a new top level one.

This blocks a half dozen conversions in #1971384: [META] Convert page callbacks to controllers, which is a critical, so this is as well.
(Think /node//%, user/%, etc)

alexpott’s picture

Status:Reviewed & tested by the community» Needs review
Issue tags:-blocker
+++ b/core/lib/Drupal/Core/Entity/Enhancer/EntityRouteEnhancer.phpundefined
@@ -47,6 +48,29 @@ public function enhance(array $defaults, Request $request) {
+          $defaults['entity'] = &$defaults[$entity_type];
...
+              $defaults['entity'] = $defaults[$flipped[$entity_type]];

Why do only set be reference in the first instance?

alexpott’s picture

+++ b/core/lib/Drupal/Core/Entity/Enhancer/EntityRouteEnhancer.phpundefined
@@ -47,6 +48,29 @@ public function enhance(array $defaults, Request $request) {
+          // The entity is not keyed by its entity_type. Attempt to find it
+          // using a converter.
+          $route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT);
+          $options = $route->getOptions();
+          if (isset($options['converters'])) {
+            $flipped = array_flip($options['converters']);
+            if (isset($flipped[$entity_type]) && !empty($defaults[$flipped[$entity_type]])) {
+              $defaults['entity'] = $defaults[$flipped[$entity_type]];
+            }
+          }

And do we have any test coverage of this code?

larowlan’s picture

1) you're right they should both be by reference
2) only a unit test, but I will expand the Web test to cover that scenario too

tim.plunkett’s picture

Title:Add a _entity_view routing default for viewing an entity.» Add a _entity_view routing default for viewing an entity
Issue tags:+blocker
larowlan’s picture

StatusFileSize
new3.97 KB
new11.41 KB
FAILED: [[SimpleTest]]: [MySQL] 57,436 pass(es), 5 fail(s), and 2 exception(s).
[ View ]
new11.37 KB
FAILED: [[SimpleTest]]: [MySQL] 57,525 pass(es), 4 fail(s), and 0 exception(s).
[ View ]

Fail, Pass, Interdiff
Web tests++
Turns out it isn't $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT) but $defaults[RouteObjectInterface::ROUTE_OBJECT]
So unit tests passed because they assumed the same thing (wrongly).

Status:Needs review» Needs work

The last submitted patch, _entity_view-2040265.19.pass_.patch, failed testing.

larowlan’s picture

StatusFileSize
new12.43 KB
PASSED: [[SimpleTest]]: [MySQL] 57,560 pass(es).
[ View ]
new6.52 KB

pro tip: when running tests locally, be sure to run them after merging HEAD, not before.
#1943846: Improve ParamConverterManager and developer experience for ParamConverters turned this on its head.
Should be green now.

larowlan’s picture

Status:Needs work» Needs review
andypost’s picture

Why default value is not bound to 'default'? For the most of cases we will use default view mode of entity assuming it's full
1) do we need conversion of default to full as defaults?
2) do we need tests for that too?

+++ b/core/lib/Drupal/Core/Entity/Enhancer/EntityRouteEnhancer.phpundefined
@@ -47,6 +48,46 @@ public function enhance(array $defaults, Request $request) {
+        $defaults['view_mode'] = $view_mode;
+        unset($defaults['_entity_view']);

what is a dafults for now? full/default

related: #2006348: Remove default/fallback entity form operation

larowlan’s picture

Hi @andypost
I don't understand your question.
I think you mean you want to be able to use

  _entity_view: node

And have the view mode select the default?

andypost’s picture

Yes, I suppose this needs fallback to 'default' view mode

andypost’s picture

Discussed in IRC - the route should use 'full' because it's default in EntityRenderControllerInterface::view()
Also it would be great to have tests for none-existent view mode

+++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.phpundefined
@@ -0,0 +1,68 @@
+  public function view(EntityInterface $_entity, $view_mode, $langcode = NULL) {

so $view_mode needs 'full' as default

+++ b/core/lib/Drupal/Core/Entity/Enhancer/EntityRouteEnhancer.phpundefined
@@ -47,6 +48,46 @@ public function enhance(array $defaults, Request $request) {
+        $defaults['view_mode'] = $view_mode;

Or maybe $defaults['view_mode'] = empty($view_mode) ? 'full' : $view_mode;

andypost’s picture

+++ b/core/tests/Drupal/Tests/Core/Entity/Enhancer/EntityRouteEnhancerTest.phpundefined
@@ -62,6 +63,41 @@ public function testEnhancer() {
+    $defaults['_entity_view'] = 'entity_test.default';

this uses 'default' view mode that does not exists

larowlan’s picture

Status:Needs review» Needs work

using default is ok in a unittest, will add a comment as such.
@andypost++

larowlan’s picture

Status:Needs work» Needs review
StatusFileSize
new13.76 KB
FAILED: [[SimpleTest]]: [MySQL] 57,882 pass(es), 1 fail(s), and 0 exception(s).
[ View ]
new6.98 KB

Adds the changes discussed at #21 through #26

Status:Needs review» Needs work

The last submitted patch, _entity_view-2040265.27.patch, failed testing.

larowlan’s picture

Status:Needs work» Needs review
StatusFileSize
new13.76 KB
PASSED: [[SimpleTest]]: [MySQL] 57,889 pass(es).
[ View ]
new867 bytes

phpunit strict warnings ey, wonder why our phpunit config doesn't throw those locally, regardless - fixed

Status:Needs review» Needs work
Issue tags:-D8MI, -language-content, -blocker

The last submitted patch, _entity_view-2040265.29.patch, failed testing.

larowlan’s picture

Status:Needs work» Needs review
Issue tags:+D8MI, +language-content, +blocker

#29: _entity_view-2040265.29.patch queued for re-testing.

larowlan’s picture

Rest/NodeTest random fails again

tim.plunkett’s picture

Status:Needs review» Reviewed & tested by the community

Ah, that last interdiff is interesting :)
The changes in #27 look good to me, as do the ones before that.

andypost’s picture

+1 tortbc

alexpott’s picture

Title:Add a _entity_view routing default for viewing an entity» Change notice: Add a _entity_view routing default for viewing an entity
Status:Reviewed & tested by the community» Active
Issue tags:+Needs change record

Committed 1deffa8 and pushed to 8.x. Thanks!

Similar to https://drupal.org/node/1799642 I think we need a change notice.

yched’s picture

Issue tags:-Needs change record

Sweet - so, isn't there some existing code in HEAD that can be removed now ?

yched’s picture

Issue tags:+Needs change record

oh, d.o ...

tim.plunkett’s picture

larowlan’s picture

Status:Active» Needs review
Berdir’s picture

Don't think it's necessary to put in a whole entity annotation? Should be enough to just say given you have an entity type named whatever?

Can we maybe put in an example of how you'd do it in 7.x?

tstoeckler’s picture

+++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php
@@ -0,0 +1,69 @@
+   * @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.
...
+  public function view(EntityInterface $_entity, $view_mode = 'full', $langcode = NULL) {

I don't want to argue in favor of rolling this back or anything, but I just want to note that using the underscore in this way sort of conflicts with the fact that we use the underscore to separate magic attributes like '_account' from actual route parameters. I brought that up there and was told that prefixing arguments with underscores is so far-fetched we shouldn't worry about it. For instance if we were to introduce a menu_get_object()-style '_entity' attribute for routes where it would make sense, this would break.
I don't want to open a whole discussion about this or the (arguably poor) use-case, I just wanted to point out that this is less than ideal.
I also don't have a better suggestion, however.

Crell’s picture

Actually... wha? Why is that property $_ prefixed? That doesn't make sense at all...

tim.plunkett’s picture

same reason it's $_content, $_controller, $_form, $_entity_list...

EDIT: I misread the question, I thought it was about $_entity_view. If you look in EntityRouteEnhancer::enhance(), there is a whole section on normalizing what the entity is.

If you have /foo/{account} to mean the User entity type, it has to use converters to figure that out. I'm not 100% sure why that's done here, or why we don't turn it back into the named param you specify at the end.

larowlan’s picture

Title:Change notice: Add a _entity_view routing default for viewing an entity» Add a _entity_view routing default for viewing an entity
Status:Needs review» Fixed
Issue tags:-Needs change record

Updated change notice https://drupal.org/node/2056839

Automatically closed -- issue fixed for 2 weeks with no activity.

Anonymous’s picture

Issue summary:View changes

Updated issue summary.