Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
In EntityViewController::view() we have the following:
// If the entity's label is rendered using a field formatter, set the
// rendered title field formatter as the page title instead of the default
// plain text title. This allows attributes set on the field to propagate
// correctly (e.g. RDFa, in-place editing).
if ($_entity instanceof FieldableEntityInterface) {
$label_field = $_entity->getEntityType()->getKey('label');
if ($label_field && $_entity->getFieldDefinition($label_field)->getDisplayOptions('view')) {
// We must render the label field, because rendering the entity may be
// a cache hit, in which case we can't extract the rendered label field
// from the $page renderable array.
$build = $this->entityManager->getTranslationFromContext($_entity)
->get($label_field)
->view($view_mode);
$page['#title'] = $this->renderer->render($build);
}
}
When profiling, I'm seeing this take anything up to 40ms on node/1 (anonymous, logged out, no page cache)
Proposed resolution
Move the setting of #title to a #pre_render callback, so that it gets cached along with the rest of the render array.
Remaining tasks
None.
User interface changes
None.
API changes
Yes - #title won't be set on the render array until it's actually been rendered. Only entity view controllers that override the view method will run into this (see the changes to NodeViewController and NodePreviewController).
Beta phase evaluation
Issue category | Bug because performance is unnecessarily slow and we're doing double work |
---|---|
Issue priority | Critical because this is > 10ms with warm caches, is an improvement for every entity view page, and involves a small API change which at least we'd need to defer until a minor release |
Comment | File | Size | Author |
---|---|---|---|
#19 | 2498849-18.patch | 7.17 KB | catch |
#18 | interdiff.txt | 1.9 KB | catch |
#18 | 2498849-18.patch | 0 bytes | catch |
#17 | interdiff.txt | 2.58 KB | dawehner |
#17 | 2498849-17.patch | 7.66 KB | dawehner |
Comments
Comment #1
catchHere's a patch and xhprof screenshots.
I don't get reliable timings on my system for this, but the work we're saving comes up as 8-10% of request time - would be great to confirm that.
See what the bot thinks.
Comment #2
catchSmall update: render the title field from $build instead of building it all over again. This avoids double-rendering the title on cache misses too.
Comment #3
Berdirleft-over? no idea why you had to make the php_value change, maybe an old file that you copied in? :)
Comment #4
catchWhoops...
Comment #5
dawehnerI'm curious, does this works for view displays, which don't have their label field configured as to be shown?
Let's ensure we don't loose the documentation here ... I think its not obvious why you can't just use
$entity->label()
and be doneComment #6
catch#1 - in HEAD when we render the field separately, we use the same view mode being requested - so it ought to be the same, but could use a check.
If it's broke for some reason we could go back to the initial patch on here, but would be nice to keep the cold cache optimisation if we can.
#2 - added the docs back (no other changes in the patch).
This is more than 10ms improvement with a warm cache on our most commonly requested pages, and it's an albeit small API change for EntityViewController::view() implementors/callers (#title not being set since it's in the pre_render now). So bumping to critical.
Comment #7
dawehnerSo the node title is configured as be possible. What about checking for isset() and otherwise fallback to $entity->label() directly?
$view_mode
is not used.Comment #8
catchThe title callback already does $entity->label() so we're fine here if #title never gets set. isset() check is reasonable though.
$view_mode is cruft - will remove on next update.
Comment #13
dawehnerTrying to fix things.
I removed EntityViewControllerTest, because all what its doing is to setup a bunch of mocks, IMHO there is no logic tested in there.
Comment #14
Wim LeersWow, amazing find!
Comment #15
Wim LeersA *page* render array.
Let's use the
[]
syntax?Besides being very weird… (is there really no other way?) this will fail to execute any
#pre_render
callbacks beyond the first two.Comment #16
plachWon't this prevent us from having an empty title? What if
$page[$label_field]['#markup']
was explicitly set to the empty string?This does not look reliable at first sight, it could at least use an inline comment.
Edit: oops, missed Wim's review :)
Comment #17
dawehnerMh I thought this fixed something, nevermind let's try it different.
Used foreach and added some comment.
Comment #18
catchI think the preview fix can be simpler - just skip that extra array level as we do for the main node view controller.
Comment #19
catch0 byte patch, classy.
Comment #20
dawehnerOh nice fix!
Comment #21
dawehnerHere is the RTBC
Comment #23
catchComment #24
plachRTBC +1
(lol @ #18 failing badly :D)
Comment #25
alexpottCommitted 4fb37aa and pushed to 8.0.x. Thanks!
Thanks for adding the beta evaluation.
I agree this test does not look useful.
Comment #27
tim.plunkettOpened a follow-up #2507967: \Drupal\Core\Entity\Controller\EntityViewController::buildTitle() assumes the $entity is in the render array