Problem/Motivation

We generate links for the HTML head rather ad hoc for entities right now. However, we should be able to do that automatically in a unified fashion.

Proposed resolution

Automatically generate links for entity canonical pages, and then remove the various one-offs.

Remaining tasks

Get tests passing and commit.

User interface changes

None.

API changes

Fewer one-offs for entity routes, but that's not an API change.

CommentFileSizeAuthor
#73 2282029-73.patch24.15 KBidebr
#73 interdiff-63-73.txt4.05 KBidebr
#65 interdiff-59-63.txt2.4 KBacbramley
#63 interdiff-59-63.patch2.4 KBacbramley
#63 2282029-63.patch21.72 KBacbramley
#59 2282029-59.patch21.74 KBwim leers
#59 interdiff.txt2.75 KBwim leers
#57 interdiff-2282029-55-57.txt673 bytestedbow
#57 2282029-57.patch21.36 KBtedbow
#55 2282029-55.patch21.31 KBtedbow
#55 interdiff-2282029-50-55.txt1.4 KBtedbow
#50 2282029-50.patch21.26 KBtedbow
#50 interdiff-2282029-46-50.txt1.54 KBtedbow
#46 2282029-46.patch21.04 KBwim leers
#46 interdiff.txt900 byteswim leers
#45 2282029-45.patch20.99 KBtedbow
#45 interdiff-2282029-43-45.txt1.24 KBtedbow
#43 2282029-43.patch20.93 KBwim leers
#43 interdiff.txt1.4 KBwim leers
#41 2282029-41.patch19.91 KBtedbow
#41 interdiff-2282029-36-41.txt2.51 KBtedbow
#36 auto_links_all_entity_types-2282029-36.patch18.95 KBwim leers
#36 interdiff.txt674 byteswim leers
#34 auto_links_all_entity_types-2282029-34.patch18.31 KBwim leers
#26 auto_links_all_entity_types-2282029-26.patch18.26 KBwim leers
#26 interdiff.txt1.24 KBwim leers
#25 auto_links_all_entity_types-2282029-25.patch17.74 KBwim leers
#25 interdiff.txt1.8 KBwim leers
#24 auto_links_all_entity_types-2282029-24.patch16.03 KBwim leers
#24 interdiff.txt1.44 KBwim leers
#15 auto_links_all_entity_types-2282029-14.patch14.63 KBwim leers
#15 interdiff.txt1.63 KBwim leers
#13 auto_links_all_entity_types-2282029-13.patch14.65 KBwim leers
#13 interdiff.txt2.5 KBwim leers
#12 auto_links_all_entity_types-2282029-12.patch12.22 KBwim leers
#12 interdiff.txt5.63 KBwim leers
#11 auto_links_all_entity_types-2282029-11.patch17.8 KBwim leers
#11 interdiff.txt5.02 KBwim leers
#10 interdiff.txt5.93 KBwim leers
#10 auto_links_all_entity_types-2282029-10.patch13.78 KBwim leers
#8 auto_links_all_entity_types-2282029-8.patch7.92 KBwim leers
auto-links.patch7.07 KBCrell

Comments

Status: Needs review » Needs work

The last submitted patch, auto-links.patch, failed testing.

Crell’s picture

Issue summary: View changes
Parent issue: » #2259445: Entity Resource unification

Many of the remaining issues are caused by using a simple string check for finding link elements in tests. The fix is to switch to the new cssSelect() method. Will return to this.

Crell’s picture

Version: 8.0.x-dev » 8.1.x-dev
Component: base system » entity system

This still should be done, but the above patch is completely not relevant anymore. I believe this is also 8.1-safe unless someone wants to do it sooner. It probably needs an alter hook or something now.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Crell’s picture

Adding link (no pun intended).

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

wim leers’s picture

Status: Needs work » Needs review
StatusFileSize
new7.92 KB

This is as straight a rebase as is possible.

--- a/core/modules/node/node.routing.yml
+++ b/core/modules/node/node.routing.yml
@@ -47,7 +47,7 @@ node.add:
 node.view:
   path: '/node/{node}'
   defaults:
-    _content: '\Drupal\node\Controller\NodeViewController::view'
+    _entity_view: 'node.full'
     _title_callback: '\Drupal\node\Controller\NodeViewController::title'
   requirements:

This change is omitted in this rebase, since this route is now defined in NodeRouteProvider and will require additional changes.

See next comment/patch.

wim leers’s picture

Title: Automate link relationship headers » Automate link relationship headers for all entity types, not just nodes

Better title.

wim leers’s picture

StatusFileSize
new13.78 KB
new5.93 KB

So the route change mentioned in #8 can no longer be done in the same way. That route is now being defined in \Drupal\node\Entity\NodeRouteProvider::getRoutes() and looks like this:

    $route = (new Route('/node/{node}'))
      ->addDefaults([
        '_controller' => '\Drupal\node\Controller\NodeViewController::view',
        '_title_callback' => '\Drupal\node\Controller\NodeViewController::title',
      ])
      ->setRequirement('node', '\d+')
      ->setRequirement('_entity_access', 'node.view');

So we can't do what this patch is doing: move away from NodeViewcontroller::view() to _entity_view: 'node.full'. Or can we?

Turns out we can!

  • Turns out NodeViewController::title() is doing the functional equivalent of the default title callback that \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider::getCanonicalRoute() sets: \Drupal\Core\Entity\Controller\EntityController::title() does effectively the same.
  • Turns out that NodeViewController::view() just calls EntityViewController::view() … and then just adds the link relations. That's all it does.
  • In other words: it's trivial to move the link relation logic out of NodeViewController and into EntityViewController… or potentially a subscriber, like this patch is currently doing.

    And in fact, simply moving the logic from NodeViewController to EntityViewController seems to be the most prudent approach. It means no extra overhead (unlike an event subscriber). It means less BC break (because $build['#attached'] still contains the links). And it's the simplest approach.


    So this:

  1. makes NodeRouteProvider extend DefaultHtmlRouteProvider
  2. moves the logic from NodeViewController to EntityViewController
wim leers’s picture

StatusFileSize
new5.02 KB
new17.8 KB

core/modules/node/src/Controller/NodeViewController.php can now simply be removed.

wim leers’s picture

StatusFileSize
new5.63 KB
new12.22 KB

The link_relationship_subscriber service definition and its code (core/lib/Drupal/Core/EventSubscriber/LinkRelationshipSubscriber.php) can now also simply be removed.

wim leers’s picture

StatusFileSize
new2.5 KB
new14.65 KB

UserRouteProvider also isn't inheriting from DefaultHtmlRouteProvider, and should be. It'll then be consistent with NodeRouteProvider.

The last submitted patch, 8: auto_links_all_entity_types-2282029-8.patch, failed testing.

wim leers’s picture

Issue tags: +Needs tests, +API-First Initiative
StatusFileSize
new1.63 KB
new14.63 KB

And then some final cleanup.

This still needs tests though.

The last submitted patch, 10: auto_links_all_entity_types-2282029-10.patch, failed testing.

The last submitted patch, 11: auto_links_all_entity_types-2282029-11.patch, failed testing.

The last submitted patch, 12: auto_links_all_entity_types-2282029-12.patch, failed testing.

The last submitted patch, 13: auto_links_all_entity_types-2282029-13.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 15: auto_links_all_entity_types-2282029-14.patch, failed testing.

Crell’s picture

Wim: Whee!

My one concern with this approach is that EntityViewController may be called at all sorts of times other than the entity canonical URL. Eg, make a View of nodes and show the "full" view mode, and poof, you now have all the links for all nodes showing on a single view page, which is definitely wrong.

We need a way to add links only if the page being viewed is the canonical URI. Doing it in a routing controller makes that quite easy, but then we have to do so for each entity type, which is what we're trying to get away from.

Or am I misunderstanding which controller we're dealing with here?

wim leers’s picture

Or am I misunderstanding which controller we're dealing with here?

Yes, you are :)

You're confusing EntityViewController (which is a controller used only for the canonical route) with EntityViewBuilder (which is used to render (build a view of) an entity, for every place that entity may be displayed).

wim leers’s picture

Apparently it's taxonomy_page_attachments_alter() that's adding link relation headers for the canonical Term route! We'll need to refactor that away here too.

wim leers’s picture

Status: Needs work » Needs review
StatusFileSize
new1.44 KB
new16.03 KB

Well there's nothing to move, it was doing exactly the same as NodeViewController, and that now lives in EntityViewController, so it was already working for taxonomy terms :)

wim leers’s picture

StatusFileSize
new1.8 KB
new17.74 KB

This should fix some of the test failures.

wim leers’s picture

StatusFileSize
new1.24 KB
new18.26 KB

And this should fix the remainder.

dawehner’s picture

People already complained hugely about those header links being there. They seem to have huge SEO issues about that. Given that it feels like these links should be enableable, but maybe disabled by default.

One way to solve that could be its own module for that. Otherwise maybe some configuration per entity type could be done.

The last submitted patch, 24: auto_links_all_entity_types-2282029-24.patch, failed testing.

The last submitted patch, 25: auto_links_all_entity_types-2282029-25.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 26: auto_links_all_entity_types-2282029-26.patch, failed testing.

wim leers’s picture

They seem to have huge SEO issues about that.

That was because they were all exposed always, even to anonymous users. Thanks to #2406533: edit-form, delete-form etc. <link> tags added on /node/{node} are invalid according to W3C Validator, that's no longer the case. :)

Crell’s picture

Wim: Ah ha! I am happy to be wrong in this case. Do carry on. (I may have been thinking of an earlier iteration of the code before there was a base class for entity route controllers.)

tedbow’s picture

@Wim Leers I was looking into the test failure in \Drupal\system\Tests\Entity\EntityViewControllerTest::testEntityViewController
$this->assertRaw($get_label_markup($entity->label()));
I think the problem is

+++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php
@@ -103,13 +103,13 @@ public function buildTitle(array $page) {
+    $page['entity']['#pre_render'][] = [$this, 'buildTitle'];
...
+    $page['entity']['#' . $page['entity']['#entity_type']] = $_entity;
 

I think \Drupal\Core\Entity\Controller\EntityViewController::buildTitle also needs to be update to match this new nesting. I tried to figure this out but

if (isset($page[$label_field])) {
  $page['#title'] = $this->renderer->render($page[$label_field]);
}

Here is $page[$label_field] is not set so this is never called.

If look at \Drupal\entity_test\EntityTestViewBuilder::buildComponents
it adds keys to the build which seems to only happen if the entity is not nested under 'entity' in the page. I can't get these keys to be added otherwise :(

wim leers’s picture

Status: Needs work » Needs review
StatusFileSize
new18.31 KB
wim leers’s picture

#2293697: EntityResource POST routes all use the confusing default: use entity types' https://www.drupal.org/link-relations/create link template if available improved \Drupal\Core\Entity\Entity::uriRelationships(): it now only returns those link relationships for which a URL actually exists for that entity type (i.e. it automatically omits those link relationships that are defined but for which no actual route exists).

That might have fixed some of the failures. If it did not, then it at least made it more reliable.

wim leers’s picture

StatusFileSize
new674 bytes
new18.95 KB

Here's a fix for all the failures due to

 Uncaught PHP Exception Drupal\Core\Entity\EntityMalformedException: "The "node" entity cannot have a URI as it does not have an ID" at /var/www/html/core/lib/Drupal/Core/Entity/Entity.php line 179

Hopefully somebody else can push it over the finish line! Note that #33 has not yet been addressed.

wim leers’s picture

The last submitted patch, 34: auto_links_all_entity_types-2282029-34.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 36: auto_links_all_entity_types-2282029-36.patch, failed testing.

tedbow’s picture

Assigned: Unassigned » tedbow

Looking at #33 agian.

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new2.51 KB
new19.91 KB
+++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php
@@ -91,13 +103,52 @@ public function buildTitle(array $page) {
-    $page = $this->entityManager
+    $page['entity'] = $this->entityManager
       ->getViewBuilder($_entity->getEntityTypeId())
       ->view($_entity, $view_mode);
 
-    $page['#pre_render'][] = [$this, 'buildTitle'];
-    $page['#entity_type'] = $_entity->getEntityTypeId();
-    $page['#' . $page['#entity_type']] = $_entity;
+    $page['entity']['#pre_render'][] = [$this, 'buildTitle'];

Ok this what was causing the problem EntityViewControllerTest::testEntityViewController not being able to find the title on the page.

Shifting
$page['#pre_render'][] = [$this, 'buildTitle'];
down a level to
$page['entity']['#pre_render'][] = [$this, 'buildTitle'];
Means it can't actually affect the title of the page because it won't have $page top level element.

But if you try to set it back to
$page['#pre_render'][] = [$this, 'buildTitle'];
Then the entity won't actually be built yet because

$this->entityManager
       ->getViewBuilder($_entity->getEntityTypeId())
       ->view($_entity, $view_mode);

Actually set a #pre_render callback to EntityViewBuilder::build() originally
$page['#pre_render'][] = [$this, 'buildTitle'];
was setting the #pre_render to run right after
EntityViewBuilder::build()

Because they aren't on the same level any more this is not possible.

I got around this in this patch by build the entity directly in EntityViewController::buildTitle()

This fixed at least 2 test for me locally but it feels heavy handed.

Status: Needs review » Needs work

The last submitted patch, 41: 2282029-41.patch, failed testing.

wim leers’s picture

Status: Needs work » Needs review
StatusFileSize
new1.4 KB
new20.93 KB

From 15 to 9 failures, great! :)

+++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php
@@ -73,16 +74,25 @@ public static function create(ContainerInterface $container) {
+      $view_mode = isset($entity_output['#view_mode']) ? $entity_output['#view_mode'] : 'full';
+      $displays = EntityViewDisplay::collectRenderDisplays([$entity], $view_mode);
+      $builds = [$entity_output];
+      $this->entityManager
+        ->getViewBuilder($entity->getEntityTypeId())
+        ->buildComponents($builds, [$entity], $displays, $view_mode);
+      $entity_build = reset($builds);

Adding all this indeed does feel very heavy-handed…

Is it because we shifted things down a level (under the 'entity' key), and hence the things that buildTitle() was expecting to already been done, have actually not yet been done at this time, and hence you need to do it manually here? (Hence doing much of the work multiple times?)


Node.Drupal\node\Tests\PagePreviewTest
✗	
doGenerate
exception: [Uncaught exception] Line 202 of core/lib/Drupal/Core/Routing/UrlGenerator.php:
Symfony\Component\Routing\Exception\InvalidParameterException: Parameter "node_revision" for route "entity.node.revision" must match "[^/]++" ("" given) to generate a corresponding URL. in Drupal\Core\Routing\UrlGenerator->doGenerate() (line 202 of /var/www/html/core/lib/Drupal/Core/Routing/UrlGenerator.php). Drupal\Core\Routing\UrlGenerator->doGenerate(Array, Array, Array, Array, Array, 'entity.node.revision') (Line: 249)

Hm, this one is interesting. It's because Node has this:

 *   links = {
 *     "canonical" = "/node/{node}",
 *     "delete-form" = "/node/{node}/delete",
 *     "edit-form" = "/node/{node}/edit",
 *     "version-history" = "/node/{node}/revisions",
 *     "revision" = "/node/{node}/revisions/{node_revision}/view",
 *     "create" = "/node",
 *   }

Note how all of those only require {node}… except the revision link template, which requires {node_revision}.

Normally this edge case is taken care of by this code in Entity::toUrl():

    // Links pointing to the current revision point to the actual entity. So
    // instead of using the 'revision' link, use the 'canonical' link.
    if ($rel === 'revision' && $this instanceof RevisionableInterface && $this->isDefaultRevision()) {
      $rel = 'canonical';
    }

But, \Drupal\node\Tests\PagePreviewTest::testPagePreviewWithRevisions() does this after testing a "normal" new revision as created through the UI:

    // Check that previewing a forward revision of a node works. This can not be
    // accomplished through the UI so we have to use API calls.
    // @todo Change this test to use the UI when we will be able to create
    // forward revisions in core.
    // @see https://www.drupal.org/node/2725533
    $node->setNewRevision(TRUE);
    $node->isDefaultRevision(FALSE);

    /** @var \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver */
    $controller_resolver = \Drupal::service('controller_resolver');
    $node_preview_controller = $controller_resolver->getControllerFromDefinition('\Drupal\node\Controller\NodePreviewController::view');
    $node_preview_controller($node, 'full');

… and that is something that hasn't been taken into account yet: in that case, you do need {node_revision}. But we can't provide it, because we're previewing an unsaved revision! Therefore generating the revision link relationship in this case does not make sense, and we just need to handle an additional edge case in Entity::toUrl().

This should fix one more failure!

Status: Needs review » Needs work

The last submitted patch, 43: 2282029-43.patch, failed testing.

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new1.24 KB
new20.99 KB

Ok the change in #43 broke UuidFormatterTest because of the way \Drupal\Core\Field\Plugin\Field\FieldFormatter\StringFormatter::viewElements() handles the 'link_to_entity' setting.

 if ($this->getSetting('link_to_entity')) {
      // For the default revision this falls back to 'canonical'
      $url = $items->getEntity()->urlInfo('revision');
    }

I changed to \Drupal\Core\Entity\Entity::toUrl() to first check $this->isDefaultRevision() first before it throws an exception.
Because at that point it should be using 'canonical'

wim leers’s picture

StatusFileSize
new900 bytes
new21.04 KB

And this should fix the remaining failures (all in NodeCacheTagsTest). The reason it was failing is that NodeCacheTagsTest::getDefaultCacheContexts() used to contain a work-around for the fact that NodeViewController adds a user.roles:anonymous cache context. But it's meant to only apply to the entire entity page… yet it was being applied to the rendered entity (in full view mode) itself! Therefore it was actually doing cache pollution.

That's why it's important that EntityViewController moved the rendering of the entity in full view mode down a level: then the $page['#cache']['contexts'][] = 'user.roles:anonymous'; no longer affects it: cache pollution solved! And hence our tests are passing again, because they were actually asserting this cache pollution, and that's now been fixed :)

The last submitted patch, 45: 2282029-45.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 46: 2282029-46.patch, failed testing.

tedbow’s picture

Assigned: tedbow » Unassigned

Unassigning myself but still need to answer from #43

Is it because we shifted things down a level (under the 'entity' key), and hence the things that buildTitle() was expecting to already been done, have actually not yet been done at this time, and hence you need to do it manually here? (Hence doing much of the work multiple times?)

Yes, it is because it is under the new key 'entity'

\Drupal\Core\Entity\Controller\EntityViewController::view

$page['entity'] = $this->entityManager
      ->getViewBuilder($_entity->getEntityTypeId())
      ->view($_entity, $view_mode);

Actually adds the build() method of Entity View builder to the #pre_render callback.
@see \Drupal\Core\Entity\EntityViewBuilder::view()

When this was the $page level it would guarantee that buildTitle() was called after build().
But not now we can't set:
$page['entity']['#pre_render'][] = [$this, 'buildTitle'];
Because it won't have access to $page['#title'] and therefore it could not affect the page title.

So we set:
$page['#pre_render'][] = [$this, 'buildTitle'];
But then the Entity View builder build() method has not be called yet.

So hence we have to build the entity ourselves.

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new1.54 KB
new21.26 KB

#46 had only coding standards voilations, unused use statments, fixing

Status: Needs review » Needs work

The last submitted patch, 50: 2282029-50.patch, failed testing. View results

wim leers’s picture

Failing tests are not showing up at https://www.drupal.org/pift-ci-job/688355

Here they are, extracted from the log:

00:04:26.064 Drupal\comment\Tests\CommentInterfaceTest                    420 passes   2 fails                            
00:04:58.596 Drupal\comment\Tests\CommentPagerTest                        362 passes   8 fails                            
wim leers’s picture

I debugged \Drupal\comment\Tests\CommentInterfaceTest::testCommentInterface() some. The root cause is not what I thought it first was: a render cache item not being invalidated when it should. Because the entity_view:node:1:full:[languages:language_interface]=… render cache item does have the node:1 cache tag, and that is being invalidated when posting a comment on node 1.

So the problem must be related to how this patch changes the rendering of full node pages and that somehow affects the rendering of the "comment" field.

tedbow’s picture

@Wim Leers yeah manually tested the problem here
CommentInterfaceTest::testCommentInterface()

// Confirm a new comment is posted to the correct page.
    $this->setCommentsPerPage(2);
    $comment_new_page = $this->postComment($this->node, $this->randomMachineName(), $this->randomMachineName(), TRUE);
    $this->assertTrue($this->commentExists($comment_new_page), 'Page one exists. %s');

When I post the 3rd comment on the node it does put the correct parameters in the query string to show the me the new page of comments but it still renders the first 2 comments.

UPDATE: Yes it looks like the problem is with \Drupal\Core\Entity\Controller\EntityViewController::buildTitle
and the fact it also builds the entity.
So \Drupal\comment\Plugin\Field\FieldFormatter\CommentDefaultFormatter::viewElements is called twice. The first time it loads the correct comment and the 2nd time it loads the incorrect comments, the first 2 on the node.

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new1.4 KB
new21.31 KB

Regarding my comment in #54 this patch doesn't build the entity in \Drupal\Core\Entity\Controller\EntityViewController::buildTitle() but rather just builds the field with the options from the display.

This fix the 2 test mentioned in #52

Also posted this potential bug for DrupalCI #2884084: Test failures not showing up on test results page

Status: Needs review » Needs work

The last submitted patch, 55: 2282029-55.patch, failed testing. View results

tedbow’s picture

Status: Needs work » Needs review
StatusFileSize
new21.36 KB
new673 bytes

So QuickEdit related tests failed because in quickedit_preprocess_field()

if ($element['#view_mode'] === '_custom') {
  return;
}

Because in #55 in \Drupal\Core\Entity\Controller\EntityViewController::buildTitle() it was using \Drupal\Core\Entity\EntityViewBuilderInterface::viewField to build the field the view mode was '_custom'.
So we just have to set the view mode because we know it at that point.

This should fix that test failures in #55. Hoping it doesn't cause any more!

dawehner’s picture

+++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php
@@ -61,16 +74,26 @@ public static function create(ContainerInterface $container) {
+      if ($component = $display->getComponent($label_field)) {
+        $field_build = $this->entityManager
+          ->getViewBuilder($entity->getEntityTypeId())
+          ->viewField($entity->{$label_field}, $component);

I'm wondering whether someone profiled this change. In views we did a LOT of changes to avoid calling viewField, given its really costly for rendering a single field. It could be to ensure in the ::view function that the label will actually be rendered, so you can always pick it out.

wim leers’s picture

StatusFileSize
new2.75 KB
new21.74 KB

It could be better to ensure in the ::view function that the label will actually be rendered, so you can always pick it out.

I added that underlined + strong text. I think that's what you meant to say? I agree with that concern — I also raised it at the beginning of my comment in #43, where I asked:

Is it because we shifted things down a level (under the 'entity' key), and hence the things that buildTitle() was expecting to already been done, have actually not yet been done at this time, and hence you need to do it manually here? (Hence doing much of the work multiple times?)

However, I'm not concerned about the performance impact here, because the result is cached by Dynamic Page Cache anyway, so subsequent visits to the page won't run that code. I'm only concerned because the increase in complexity is massive:

+++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php
@@ -61,16 +74,26 @@ public static function create(ContainerInterface $container) {
-      if (isset($page[$label_field])) {
-        $page['#title'] = $this->renderer->render($page[$label_field]);
+      $view_mode = isset($entity_output['#view_mode']) ? $entity_output['#view_mode'] : 'full';
+      $displays = EntityViewDisplay::collectRenderDisplays([$entity], $view_mode);
+      /** @var \Drupal\Core\Entity\Entity\EntityViewDisplay $display */
+      $display = array_values($displays)[0];
+      if ($component = $display->getComponent($label_field)) {
+        $field_build = $this->entityManager
+          ->getViewBuilder($entity->getEntityTypeId())
+          ->viewField($entity->{$label_field}, $component);
+        $field_build['#view_mode'] = $view_mode;
+        $page['#title'] = $this->renderer->render($field_build);

I stepped through this with a debugger, and I'm now pretty sure that my best-guess reasoning in #43 is actually correct.

Note that I am the one who moved shifted things down a level in #26, from $page = $view_builder->view($entity, 'full') to $page['entity'] = $view_builder->view($entity, 'full').
I did so to fix most [entity_type]CacheTagsTest tests. (Compare the failing tests in #25 vs #26.)
And given the additional insight I gained in #46, this is AFAICT a necessity. The old code was effectively polluting the render cache for nodes (see NodeViewControll::view(), which we are now bringing to all entity types).
This change is the solution.

I definitely would like to see this get a simpler solution though. After some tinkering, I think that's actually possible: what if we ensure that before buildTitle() is called, $page['entity'] has its #pre_render callbacks executed, so that $page['entity']['title'] exists, so that buildTitle() can continue to work as it does in HEAD?
EDIT: re-reading this, I think this is exactly what @dawehner was suggesting? :)

Status: Needs review » Needs work

The last submitted patch, 59: 2282029-59.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

acbramley’s picture

Assigned: Unassigned » acbramley
  1. +++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php
    @@ -91,13 +129,53 @@ public function buildTitle(array $page) {
    +        $page['#attached']['html_head_link'][] = [
    +          [
    +            'rel' => $rel,
    +            'href' => $url->toString(),
    +          ],
    +          TRUE,
    

    I was always under the impression that canonical rel links should use the unaliased version of the url as it is the canonical source of the content?

  2. +++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php
    @@ -91,13 +129,53 @@ public function buildTitle(array $page) {
    +        // Set the non-aliased canonical path as a default shortlink.
    +        $page['#attached']['html_head_link'][] = [
    +          [
    +            'rel' => 'shortlink',
    +            'href' => $url->setOption('alias', TRUE)->toString(),
    +          ],
    +          TRUE,
    +        ];
    

    Comment says non-aliased, but we are setting the alias option to TRUE?

  3. +++ b/core/modules/taxonomy/taxonomy.module
    @@ -96,37 +95,6 @@ function taxonomy_term_uri($term) {
    -function taxonomy_page_attachments_alter(array &$page) {
    

    Awesome, this is causing problems with the Metatag module by adding duplicate canonical rel links.

    Question is, will this new approach play nice with the module? Seems like the existing approach for Node works fine.
    EDIT: Answer is, yes! metatag_entity_view_alter removes core's tags if metatag is adding them itself. Since taxonomy is doing it's own thing at the moment, it doesn't catch them.

Going to take a stab at fixing the tests.

acbramley’s picture

Status: Needs work » Needs review
StatusFileSize
new21.72 KB
new2.4 KB

Fixes some PHPCS and adds a check to $page['entity']['#pre_render']

Status: Needs review » Needs work

The last submitted patch, 63: interdiff-59-63.patch, failed testing. View results

acbramley’s picture

Status: Needs work » Needs review
StatusFileSize
new2.4 KB

Woops, wasn't meant to be a patch file.

The last submitted patch, 63: 2282029-63.patch, failed testing. View results

borisson_’s picture

Status: Needs review » Needs work

Back to needs work to fix the test-failures, we should also add new tests for this functionality (or remove the needs tests tag).

  1. +++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php
    @@ -35,10 +43,14 @@ class EntityViewController implements ContainerInjectionInterface {
    +   *   The current user. For backwards compatibility this is optional, however
    +   *   this will be removed before Drupal 9.0.0.
    

    This comment doesn't make sense, the parameter won't be removed, the optionality (is this a word?) of it will be removed. We should clarify this comment.

  2. +++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php
    @@ -52,6 +64,32 @@ public static function create(ContainerInterface $container) {
    +    $this->controllerResolver = \Drupal::service('controller_resolver');
    

    Should we also make this an optional constructor argument? At minimum we should declare the controllerResolver as a protected variable.

  3. +++ b/core/lib/Drupal/Core/Entity/Entity.php
    @@ -183,10 +183,16 @@ public function toUrl($rel = 'canonical', array $options = []) {
    +        // Unsaved revisions cannot be linked to.
    

    This comment is useless as it is, it duplicates the message in the exception, we should either document why this is - or remove the entire thing.

  4. +++ b/core/lib/Drupal/Core/Entity/Entity.php
    @@ -183,10 +183,16 @@ public function toUrl($rel = 'canonical', array $options = []) {
    +        throw new EntityMalformedException(sprintf('The "%s" entity cannot have a revision URI as it is an unsaved revision yet.', $this->getEntityTypeId()));
    

    /s/yet//

acbramley’s picture

Assigned: acbramley » Unassigned
wim leers’s picture

#2791571: Automatically supply contextual links for entities just landed, would be great to now get this one to completion too :)

andypost’s picture

  1. +++ b/core/lib/Drupal/Core/Entity/Controller/EntityViewController.php
    @@ -35,10 +43,14 @@ class EntityViewController implements ContainerInjectionInterface {
    +  public function __construct(EntityManagerInterface $entity_manager, RendererInterface $renderer, AccountInterface $current_user = NULL) {
    ...
    +    $this->currentUser = $current_user ?: \Drupal::currentUser();
    
    @@ -91,13 +131,53 @@ public function buildTitle(array $page) {
    +      if ($this->currentUser->isAuthenticated() || $url->access($this->currentUser)) {
    

    I'd better move this to protected method

  2. +++ b/core/lib/Drupal/Core/Entity/Entity.php
    @@ -183,10 +183,16 @@ public function toUrl($rel = 'canonical', array $options = []) {
    +      elseif ($this->isNewRevision()) {
    +        // Unsaved revisions cannot be linked to.
    +        throw new EntityMalformedException(sprintf('The "%s" entity cannot have a revision URI as it is an unsaved revision yet.', $this->getEntityTypeId()));
    

    new exception should be documented & probably better to not throw the exception

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

idebr’s picture

Status: Needs work » Needs review
StatusFileSize
new4.05 KB
new24.15 KB

Reroll against 8.7.x with a few minor changes:

#67.1 Updated the __construct comment to indicate the parameters will be required in Drupal 9.0.0.

#67.2 Moved the declaration to the __construct method.

#67.3 Added Wim's reasoning in #43 to the comment.

#67.4 Fixed.

#70.1 Not sure what part you would move? This is a cut/paste operation from the old NodeViewController.

#70.2 Not sure how to fix, but I'm open for suggestions.

#73.1 Fixed the test failures in ContentTranslation by adding the new user.role.anonymous cache context to the assertions

The failure in ModerationFormTest is due to the fact that hook_entity_view is now called twice for each entity rendering. This results in new element names being generated, eg. edit-new-state becomes edit-new-state--2. Not sure how to fix this.

Also the test coverage needs to be extended for different entity types.

Status: Needs review » Needs work

The last submitted patch, 73: 2282029-73.patch, failed testing. View results

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

berdir’s picture

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

catch’s picture

Status: Needs work » Closed (won't fix)