Problem/Motivation

Cache of entities are not invalidated when the entity label changes.

Steps to reproduce for nodes & breadcrumbs (from #3):

  1. Create a node with title 'Title A'.
  2. Change the title to 'Title B' and check the 'Create new revision'.
  3. Open the Revisions tab > Breadcrumb shows the right title (Title B).
  4. Change the title to 'Title C' and check the 'Create new revision'.
  5. Open the Revisions tab.

Expected: The title in the breadcrumb is 'Title C'.
Actual: the title in the breadcrumb is 'Title B'.

Steps to reproduce for users (from #48):

  1. Log in
  2. Edit your username and save.
  3. Return to the edit screen for your user account.

Expected: Breadcrumb shows your new username
Actual: You'll see your old username is still utilised in the breadcrumb.

Proposed resolution

Allow attaching cacheability metadata to titles with a new CacheableTitleResolverInterface that returns a static or dynamic title (with cacheable metadata).

CommentFileSizeAuthor
#186 breadcrumb-render-cache--10.2.x--2607920--186.patch49.76 KBsker101
#176 breadcrumb-render-cache--10.2.x--2607920--175.patch26.44 KBsker101
#172 breadcrumb-render-cache--10.2.x--2607920--172.patch26.41 KBsker101
#171 breadcrumb-render-cache--10.2.x--2607920--169.patch25.11 KBstomusic
#166 2607920-166-breadcrumb-render-cache-10.x-fail.patch5.15 KBbhanu951
#164 2607920-breadcrumb-render-cache-10.x-fail.patch4.75 KBbhanu951
#162 2607920-162-test.png138.1 KBbhanu951
#157 breadcrumb-render-cache--9.5.x--2607920--157.patch23.51 KBsker101
#156 breadcrumb-render-cache--9.5.x--2607920--156.patch23.52 KBsker101
#152 breadcrumb-render-cache--9.4.x--2607920--152.patch23.53 KBpenyaskito
#152 breadcrumb-render-cache--9.4.x--2607920.interdiff.150-152.txt2.64 KBpenyaskito
#150 breadcrumb-render-cache--9.4.x--2607920--150.patch23.43 KBpenyaskito
#150 breadcrumb-render-cache--9.4.x--2607920.interdiff.148-150.txt533 bytespenyaskito
#148 breadcrumb-render-cache--9.4.x--2607920.interdiff.147-148.txt448 bytespenyaskito
#148 breadcrumb-render-cache--9.4.x--2607920--148.patch23.19 KBpenyaskito
#147 breadcrumb-render-cache--9.4.x--2607920--147.patch23.17 KBpenyaskito
#147 breadcrumb-render-cache--9.4.x--2607920.interdiff.145-147.txt2.62 KBpenyaskito
#145 breadcrumb-render-cache--9.4.x--2607920--145.patch22.94 KBpenyaskito
#142 interdiff_141-142.txt448 bytesranjith_kumar_k_u
#142 2607920-142.patch25.4 KBranjith_kumar_k_u
#139 2607920-Breadcrumb-screenshot.png81.33 KBbhanu951
#136 after.png58.07 KBrinku jacob 13
#136 before.png57.49 KBrinku jacob 13
#135 breadcrumb-render-cache--9.1.x--2607920--134.patch25.53 KBhershey.k
#131 interdiff-106-131.txt3.6 KBhershey.k
#131 breadcrumb-render-cache--9.1.x--2607920--131.patch25.54 KBhershey.k
#130 breadcrumb-render-cache--9.1.x--2607920--130.patch25.55 KBhershey.k
#129 breadcrumb-render-cache--9.1.x--2607920--128.patch25.55 KBhershey.k
#127 interdiff-106-127.txt3.6 KBhershey.k
#127 breadcrumb-render-cache--9.1.x--2607920--127.patch25.8 KBhershey.k
#125 9.2.x-2607920-124.patch42.4 KBgovind.maloo
#123 9.2.x-2607920-123.patch42.65 KBgovind.maloo
#122 interdiff_114_122.txt29.99 KBgovind.maloo
#122 9.2.x-2607920-122.patch44.62 KBgovind.maloo
#114 breadcrumb_shows_the_9.1.x-2607920-114.patch28 KBayushmishra206
#109 Admin Breadcrumb AFTER applying the patch.PNG9.04 KBayushmishra206
#109 Admin Breadcrumb BEFORE applying the patch.PNG9.34 KBayushmishra206
#109 Title Breadcrumb AFTER applying the patch.PNG22.21 KBayushmishra206
#109 Title Breadcrumb BEFORE applying the patch.PNG21.69 KBayushmishra206
#106 interdiff-99-106.txt2.62 KBhugovk
#106 breadcrumb_shows_the_8.9.2-2607920-106.patch25.76 KBhugovk
#104 breadcrumb_shows_the_8.9.2-2607920-104.patch25.78 KBhugovk
#104 interdiff-99-104.txt2.64 KBhugovk
#103 Screen Shot 2020-07-19 at 7.53.28 PM.png308.24 KBtanubansal
#102 Correct breadcrumb without cache clr_afterPatch.JPG25.6 KBpankaj.singh
#102 correct breadcrum only after cache clr_beforePatch.JPG19.51 KBpankaj.singh
#102 User_incorrect breadcrumb_beforePatch.JPG47.68 KBpankaj.singh
#102 correct breadcrumb without cache clear_afterPatch.JPG81.62 KBpankaj.singh
#102 Got corrected only after clearing the cache_BeforePatch.JPG50.76 KBpankaj.singh
#102 incorrect breadcrumb_BeforePatch.JPG73.56 KBpankaj.singh
#102 before Editing the title_BeforePatch.JPG47.58 KBpankaj.singh
#99 interdiff-98-99.txt2.95 KBhchonov
#99 2607920-99.patch25.79 KBhchonov
#99 2607920-99-debug-access.png1017.62 KBhchonov
#98 interdiff-97-98.txt698 bytesgease
#98 breadcrumb_shows_the_8.9.x-2607920-98.patch24.51 KBgease
#97 breadcrumb_shows_the_8.9.x-2607920-97.patch24.48 KBgease
#95 breadcrumb_shows_the_8.8.x-2607920-95.patch24.46 KBbadrange
#88 interdiff-85-88.txt4.69 KBbadrange
#88 breadcrumb_shows_the_8.8.x-2607920-88.patch24.46 KBbadrange
#86 breadcrumb_shows_the_8.8.x-2607920-85.patch24.48 KBbadrange
#86 interdiff-81-85.txt1.1 KBbadrange
#81 interdiff-77-81.txt7.42 KBbadrange
#81 breadcrumb_shows_the_8.8.x-2607920-81.patch24.72 KBbadrange
#81 interdiff-80-81.txt1.28 KBbadrange
#80 interdiff-77-80.txt6.48 KBbadrange
#80 breadcrumb_shows_the_8.8.x-2607920-80.patch24.94 KBbadrange
#77 interdiff-66-77.txt2.69 KBbadrange
#77 breadcrumb_shows_the_8.8.x-2607920-77.patch22.15 KBbadrange
#68 interdiff-8.7-54-68.txt754 bytesbadrange
#68 breadcrumb_shows_the_8.7.x-2607920-68.patch20.19 KBbadrange
#67 interdiff-64-66.txt753 bytesbadrange
#66 breadcrumb_shows_the_8.8.x-2607920-66.patch19.78 KBbadrange
#63 interdiff-60-62.txt1.74 KBbadrange
#63 breadcrumb_shows_the_8.8.x-2607920-62.patch20.21 KBbadrange
#60 breadcrumb_shows_the_8.8.x-2607920-60.patch19.59 KBbadrange
#54 breadcrumb_shows_the_8.7.x-2607920-54.patch20.15 KBmaks.khotynetskyi
#51 2607920-51.patch19.78 KBtstoeckler
#51 2607920-49-51-interdiff.txt4.52 KBtstoeckler
#49 2607920-49.patch17.87 KBtstoeckler
#49 2607920-44-49-interdiff.txt6.15 KBtstoeckler
#44 interdiff-42-44.txt1.65 KBkfritsche
#44 2607920-44.patch16.27 KBkfritsche
#42 2607920-42.patch14.62 KBkfritsche
#42 interdiff-40-42.txt2.75 KBkfritsche
#40 2607920-40.patch14.69 KBtstoeckler
#40 2607920-40--new-approach-do-not-test.patch11.4 KBtstoeckler
#40 2607920-35-40--tests-interdiff.txt629 byteststoeckler
#35 interdiff-27-35.txt2 KBkfritsche
#35 2607920-35.patch5.55 KBkfritsche
#27 interdiff.txt1.87 KBaludescher
#27 2607920-27.patch3.04 KBaludescher
#26 ControlleHandler_files.png108.36 KBhimanshugautam
#23 interdiff-2607920-21-23.txt702 bytesvg3095
#23 addCacheableDependency-2607920-23.patch2.19 KBvg3095
#21 interdiff-2607920-17-21.txt1.26 KBvg3095
#21 breadcrumb-EntityAccessControlHandler-2607920-21.patch2.77 KBvg3095
#17 breadcrumb_shows_the-2607920-17.patch1.92 KBsidharthap
#13 After_patch.JPG111.56 KBTrupti Bhosale
#13 Patch_applied.JPG34.05 KBTrupti Bhosale
#13 Before_patch.JPG104.32 KBTrupti Bhosale
#12 breadcrumb_shows_the-2607920-12.patch1.88 KBjuampynr
#12 interdiff.txt719 bytesjuampynr
#10 breadcrumb_shows_the-2607920-10.patch1.89 KBjuampynr
#8 breadcrumb_shows_the-2607920-8.patch1.23 KBjuampynr
#3 Selection_005.png65.19 KBjuampynr
#2 Screenshot from 2015-11-07 23:45:20.png49.23 KBmatsbla
Screenshot from 2015-11-04 06:44:44.png58.73 KBmatsbla

Issue fork drupal-2607920

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

matsbla created an issue. See original summary.

matsbla’s picture

StatusFileSize
new49.23 KB

I noticed that the breadcrumb actually change until you enable content translation + add a new language + enable content type translation.

I did this:
- Created a node with title "Test Diff module 1"
- Made a new revision and changes title to "Test Diff module 2"
- Enabled content translation + added a new language + enabled content type translation
- Made a new revision and changes title to "Test Diff module 3"

The breadcrumb title will now always stay as "Test Diff module 2"
(Check attached new image)

In fact it seems like the revision tab is not displayed until:
- Content translation module is enable
- At least 2 languages are enabled
- The content type translation is enabled
I'm not sure if this is related?

juampynr’s picture

Title: Diff module breadcrumb always display title from first revision » Breadcrumb shows the previous node revision's title
Project: Diff » Drupal core
Version: 8.x-1.x-dev » 8.1.x-dev
Component: Code » path.module
Issue summary: View changes
StatusFileSize
new65.19 KB

I could reproduce this in Drupal core by following these steps:

1. Create a node with title 'Title A'.
2. Change the title to 'Title B' and check the 'Create new revision'.
3. Open the Revisions tab > Breadcrumb shows the right title (Title B).
4. Change the title to 'Title C' and check the 'Create new revision'.
5. Open the Revisions tab.

Expected: the title in the breadcrumb is 'Title C'.
Actual: the title in the breadcrumb is 'Title B'.

Here is a screenshot where the breadcrumb shows 'Title I' while the page title shows 'Title J':

Breadcrumb shows the previous revision's title

dawehner’s picture

Maybe we should add the cacheability metadata of each upcasted object, given that the output might differ depending on it.

juampynr’s picture

@dawehner suggested in IRC to set the cache tag somewhere at PathBasedBreadcrumbBuilder.

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.

juampynr’s picture

Status: Active » Needs review
StatusFileSize
new1.23 KB

Here is a patch containing a test that demonstrates the steps that I listed at comment #3.

Status: Needs review » Needs work

The last submitted patch, 8: breadcrumb_shows_the-2607920-8.patch, failed testing.

juampynr’s picture

Status: Needs work » Needs review
StatusFileSize
new1.89 KB

This patch fixes it when I test it manually but I must be doing something horribly wrong because the test that I wrote never ends.

berdir’s picture

Status: Needs review » Needs work

Yes, we had a similar bug with local task access when the user had this permission.

While this might make sense, you should use addCacheableDependency($node). I'm also not sure if this is conceptually the right fix or if it's just fixing the problem as a side effect.

juampynr’s picture

Status: Needs work » Needs review
StatusFileSize
new719 bytes
new1.88 KB

Thanks @berdir. Here is a new patch with your suggestion.

Trupti Bhosale’s picture

StatusFileSize
new104.32 KB
new34.05 KB
new111.56 KB

Verified the patch 'breadcrumb_shows_the-2607920-12.patch' in comment #12.Below are the observations
1.Reproduced the issue in drupal 8.2.x site
2.Applied the patch 'breadcrumb_shows_the-2607920-12.patch'
3.Cleared cache
4.Create a node with title 'Test 1.
5. Change the title to 'Test 2' and check the 'Create new revision'.
6. Open the Revisions tab > Breadcrumb shows the right title (Test 2).
7. Change the title to 'Test 3' and check the 'Create new revision'.
8. Open the Revisions tab, the breadcrumb now displayed right title (Test 3)

Attached snapshot for reference.

Trupti Bhosale’s picture

Status: Needs review » Reviewed & tested by the community
catch’s picture

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

Could still go into 8.0.x, didn't fully review yet.

catch’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Novice
+++ b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
@@ -381,4 +381,33 @@ function testBreadCrumbs() {
+    $this->assertNoText('Article Bar');

This could use a positive assertion - i.e. looking for 'Article Baz' as well as the negative one. If we had a bug where breadcrumbs didn't show on cache hits it'd pass too at the moment.

Tagging novice since that should be a one-line addition to the test.

sidharthap’s picture

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

Updated patch as per #16.

dawehner’s picture

+++ b/core/modules/node/src/NodeAccessControlHandler.php
@@ -64,6 +64,7 @@ public function access(EntityInterface $entity, $operation, AccountInterface $ac
+      $result->addCacheableDependency($entity);

I was wondering whether actually this should be added to EVERY EntityAccessControlHandler case instead.

berdir’s picture

Yes, I think we should do exactly that.

dawehner’s picture

Status: Needs review » Needs work

.

vg3095’s picture

Status: Needs work » Needs review
StatusFileSize
new2.77 KB
new1.26 KB

Added CacheableDependency on every EntityAccessControlHandler case

Status: Needs review » Needs work

The last submitted patch, 21: breadcrumb-EntityAccessControlHandler-2607920-21.patch, failed testing.

vg3095’s picture

Status: Needs work » Needs review
StatusFileSize
new2.19 KB
new702 bytes
dawehner’s picture

Status: Needs review » Needs work

This doesn't address #18 yet at all, sorry.

vg3095’s picture

Since I am new at this , can you please elaborate , what needs to be done.

himanshugautam’s picture

StatusFileSize
new108.36 KB

@dawehner Is comment #18 talking about adding+ $result->addCacheableDependency($entity); to all the EntityAccessControlHandler files?

aludescher’s picture

Version: 8.0.x-dev » 8.1.x-dev
Component: path.module » entity system
Status: Needs work » Needs review
Issue tags: +DCTransylvania
StatusFileSize
new3.04 KB
new1.87 KB

This is a proposed fix by adding the addCacheableDependency() call to the parent access() method.
The access() method is only overridden by the NodeAccessControlHandler class. But the checkAccess() method is implemented by most / all of them and addCacheableDependency() is not called for every AccessResult return object. What determines the use of addCacheableDependency() and why is it not called every time the $entity object exists?

    // Administrators can view/update/delete all user profiles.
    if ($account->hasPermission('administer users')) {
      return AccessResult::allowed()->cachePerPermissions();
    }

    switch ($operation) {
      case 'view':
        // Only allow view access if the account is active.
        if ($account->hasPermission('access user profiles') && $entity->isActive()) {
          return AccessResult::allowed()->cachePerPermissions()->addCacheableDependency($entity);

Status: Needs review » Needs work

The last submitted patch, 27: 2607920-27.patch, failed testing.

catch’s picture

stpaultim’s picture

Issue summary: View changes
Issue tags: -Novice

Removing "novice" tag. It looks like the "novice" task mentioned in comment #16 was dealt with and the issue has gotten more complicated again.

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

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should 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.

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

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should 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.

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

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should 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.

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

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should 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.

kfritsche’s picture

Status: Needs work » Needs review
StatusFileSize
new5.55 KB
new2 KB

Re-rolling 27.
Applying patch from #2513570: Changing name (label) of content type is not reflected in breadcrumb link text as mentioned in #29.

Tested manually and it works now.

Reviewing the patch also looks good to me.

Lets see what Testbot thinks about it.

jonathanshaw’s picture

The problem seems to be more generic than the original issue report? Issue needs retitling.

Status: Needs review » Needs work

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

tstoeckler’s picture

Status: Needs work » Needs review

Was about to RTBC but the test failures made me think about this a bit harder. And now I'm convinced the fix is actually incorrect.

The bug that is being fixed here is that if the path-based breadcrumb comes across an entity view page, and so the entity's label ends up being part of the breadcrumb, that breadcrumb is not invalidated when the entity, and in particular the entity label, changes.

What the current patch does is to declare that when checking access for an entity the result of that access-checking always depends on the entity itself. This fixes the bug because part of the breadcrumb building involves access checking for the respective routes, which in turn checks access on the respective entities when encountering any entity view pages.

Fundamentally the assumption the patch makes, however, is wrong. The access result does not necessarily depend on the entity itself. In fact the most common case is that it solely depends on a permission. An example of an access result that does depend on the entity is the status field of nodes. But when checking that in ::checkAccess() NodeAccessControlHandler already adds the entity as cacheability metadata as it should. In the particular case of this bug, the access result actually does not change (!), it's TRUE/allowed both before and after saving the node. So when looking at the access checking in isolation it is actually incorrect to invalidate the cache. In other words the patch introduces completely unwarranted invalidation and it just happens to be the case that that unwarranted invalidation solves the bug where the actual cached content (not the access) of the breadcrumb becomes stale.

So now turning to the breadcrumb bug itself and ignoring the access checking what happens is that when fetching the entity label for the breadcrumb, that entity is not attached as cacheability metadata to that breadcrumb. That is the actual bug and what should be fixed here.

Taking a deeper look at that that is going to be a "fun" problem to solve. The entity label is the result of TitleResolver::getTitle() which does all sorts of magic (from a predictability/cacheability point of view) involving the global request object, so not really sure how we are going to be able to do this. We need some way for (in this specific case) EntityController::title() to provide cacheability metadata about what it is doing and for that do end all the up the stack in PathBasedBreadcrumbBuilder::build(). That will require some creative thinking, I think, at least I'm coming up blank right now.

tstoeckler’s picture

Status: Needs review » Needs work

Oops, did not mean to change status.

tstoeckler’s picture

Status: Needs work » Needs review
StatusFileSize
new629 bytes
new11.4 KB
new14.69 KB

So here's something that seems to work: Since the argument resolver looks into $request->attributes for any arguments, simply putting a cacheable_metadata key there will allow title callbacks to have such an argument where they can pass along the data. This passes at least the tests added here (although there was one broken assertion in the test, see the respective interdiff).

Notes:

  • It's not possible with this approach for title callbacks to declare the $cacheable_metadata argument by reference, which is a bit unfortunate
  • Possibly we should clone the request object before adding the cacheable metadata so it doesn't leak into other places.

Status: Needs review » Needs work

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

kfritsche’s picture

Status: Needs work » Needs review
StatusFileSize
new2.75 KB
new14.62 KB

Thanks for the feedback and fixing it. Sounds good for me.

Some minor remarks and the fix for these attached. This should fix most of the failing tests.

  1. +++ b/core/lib/Drupal/Core/Entity/Controller/EntityController.php
    @@ -220,12 +221,15 @@ public function addBundleTitle(RouteMatchInterface $route_match, $entity_type_id
    +      $cacheable_metadata->addCacheableDependency($entity);
    

    isset check needed

  2. +++ b/core/modules/node/src/Controller/NodeViewController.php
    @@ -101,11 +102,15 @@ public function view(EntityInterface $node, $view_mode = 'full', $langcode = NUL
    +    $cacheable_metadata->addCacheableDependency($node);
    

    isset check needed

  3. +++ b/core/modules/system/src/PathBasedBreadcrumbBuilder.php
    @@ -174,7 +176,12 @@ public function build(RouteMatchInterface $route_match) {
    +          $breadcrumb
    +            ->addCacheContexts($cacheable_metadata->getCacheContexts())
    +            ->addCacheTags($cacheable_metadata->getCacheTags())
    +            ->mergeCacheMaxAge($cacheable_metadata->getCacheMaxAge());
    

    This could be done as a one liner with addCacheableDependency. This method takes care of all of these merges.

Status: Needs review » Needs work

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

kfritsche’s picture

Status: Needs work » Needs review
StatusFileSize
new16.27 KB
new1.65 KB

Fixing the mock in the PathBasedBreadcrumbBuilderTest, tests should pass now.

+++ b/core/lib/Drupal/Core/Entity/Controller/EntityController.php
@@ -290,8 +296,12 @@ protected function doGetEntity(RouteMatchInterface $route_match, EntityInterface
+      // @todo Add cacheable metadata here.

+++ b/core/modules/node/src/Controller/NodeViewController.php
@@ -101,11 +102,17 @@ public function view(EntityInterface $node, $view_mode = 'full', $langcode = NUL
+      // @todo Add cacheable metadata for ::getTranslationFromContext()

Open then these two todos. Any ideas?

tstoeckler’s picture

Open then these two todos. Any ideas?

Not really. I was too afraid to look into EntityRepository::getTranslationContext() this morning. Did so now.

So there's already this in there:

$langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
$entity->addCacheContexts(['languages:' . LanguageInterface::TYPE_CONTENT]);

So if we simply move the ::addCacheableDependency() call after the ::getTranslationFromContext() call we should be fine.

Then there's also this:

$candidates = $this->languageManager->getFallbackCandidates($context);

That ends up invoking hook_language_fallback_candidates(_OPERATION|)_alter(). So those hook implementations would need some way to pass cacheable metadata back up the stack, as well. The good thing is that these hooks already get a $context parameter, so I guess we can just add the CacheableMetadata object there.

Interestingly, because Content Translation actually implements that hook, we should be able to reproduce a bug with the missing cache metadata there as well. Let's say there are two languages A and B and A has a higher weight than B and an entity with translations in both languages. If the translation in A is unpublished then any breadcrumb including the view page of that entity should show the title in language B. Then if the translation A is published, the cache will not be invalidated, so the breadcrumb will incorrectly still show the title in language B until the next cache clear. So we should be able to add a test that does exactly that, when fixing this.

All that said, maybe we can make the whole language fallback thing a separate follow-up issue, since this already fixes a legitimate bug.

dawehner’s picture

+++ b/core/lib/Drupal/Core/Controller/CacheableTitleResolverInterface.php
@@ -0,0 +1,37 @@
+   * @return array|string|null
+   *   The cacheable title for the route.
+   */
+  public function getCacheableTitle(Request $request, Route $route, CacheableMetadata &$cacheable_metadata);

I'm wondering whether we should have a CacheableString or similar class which encapsulates the result of a computation together with its cacheable metadata, much like we do in other places.

chrisolof’s picture

Title: Breadcrumb shows the previous node revision's title » Entity breadcrumb doesn't update when entity title changes (stale cache)
Version: 8.5.x-dev » 8.6.x-dev

This issue is present in 8.6.x and is not isolated to nodes. I just verified it happens with user entities as well. To reproduce just log in, edit your username, and save. Then return to the edit screen for your user account. You'll see your old username is still utilized in the breadcrumb.

tstoeckler’s picture

Issue tags: +Needs change record, +Needs followup
StatusFileSize
new6.15 KB
new17.87 KB

Opened #3005360: Determining language fallback candidates does not account for cacheable metadata for #45.

The patch itself needed a rebase for the ControllerResolverArgumentsResolver change in TitleResolver.

I then fixed the getTranslationFromContext() thing mentioned in #45 and also added a @todo referencing the aforementioned issue.

Then I added a CacheableTitle class per #47.

So this issue needs review again. In addition to the issue summary update I guess we also need a change record here to advertise the new method and also a follow-up to convert all remaining title callbacks in core to attach cacheability metadata. Possibly we also want to deprecate TitleResolver::getTitle() (but - if so - certainly in a follow-up).

Status: Needs review » Needs work

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

tstoeckler’s picture

Status: Needs work » Needs review
StatusFileSize
new4.52 KB
new19.78 KB

Fixed coding standards violations and PathBasedBreadcrumBuilderTest.

honyik’s picture

Can somebody please change the patch to work for 8.7.x? I still have this issue (for CT Articles)

jonathanshaw’s picture

Version: 8.6.x-dev » 8.7.x-dev
maks.khotynetskyi’s picture

StatusFileSize
new20.15 KB

Here is the patch for 8.7.x

Status: Needs review » Needs work

The last submitted patch, 54: breadcrumb_shows_the_8.7.x-2607920-54.patch, failed testing. View results

OlgaRabodzei’s picture

Status: Needs work » Reviewed & tested by the community

Hi!
Thanks to @makskhotynetskyi, patch above works good.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 54: breadcrumb_shows_the_8.7.x-2607920-54.patch, failed testing. View results

hugovk’s picture

Status: Needs work » Reviewed & tested by the community

Confirmed patch #52 works with latest Drupal 8.7.5.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 54: breadcrumb_shows_the_8.7.x-2607920-54.patch, failed testing. View results

badrange’s picture

Version: 8.7.x-dev » 8.8.x-dev
StatusFileSize
new19.59 KB

This is also an issue with 8.8.x. Attaching a patch, hoping that the tests will pass.

It looks to me like the tests for the 8.7.x patch fails because of a change in how the testbot handles calling of deprecated code. I wonder if it is even possible to make the tests pass for 8.7 as it is.

badrange’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 60: breadcrumb_shows_the_8.8.x-2607920-60.patch, failed testing. View results

badrange’s picture

StatusFileSize
new20.21 KB
new1.74 KB

Fix test fails. The change in core/modules/node/src/Controller/NodeViewController.php need a special notice - tests failed due to a deprecation notice that I removed here. I could not find a separate issue for this, please let me know if I should make one.

badrange’s picture

Status: Needs work » Needs review
chris matthews’s picture

Tested the patch in #63 and it works for me with the latest Drupal 8.7.6. Leaving the status as 'Needs review' for now in case someone else would like to review/test before changing to RTBC.

badrange’s picture

StatusFileSize
new19.78 KB

Fix a typo in the patch, @return stringe -> @return string.

Note: I was wrong in my last comment, the deprecation change I thought I added here has already been committed to Drupal Core in #2691675, the test failures happened because the patch did not have that deprecation change in it. This problem wouldn't have happened if we were using pull requests.

badrange’s picture

StatusFileSize
new753 bytes
badrange’s picture

Attaching a patch with the fix for 8.7.x too.

hugovk’s picture

Status: Needs review » Reviewed & tested by the community

Confirmed patch #68 works with Drupal 8.7.5. Thanks!

jonathanshaw’s picture

Status: Reviewed & tested by the community » Needs work

Nice to see this coming along.

We need an issue summary update, change record and follow up, so NW by definition.

badrange’s picture

Title: Entity breadcrumb doesn't update when entity title changes (stale cache) » Cache not invalidated when entity label changes
Issue summary: View changes
Status: Needs work » Needs review

Attempting to update issue title and summary. Feedback welcome.

hugovk’s picture

@badrange Thanks for updating the title and summary! I've removed the "Needs issue summary update" tag.

@jonathanshaw Please can you point us towards what's needed for a change record? And what does "needs followup" need, specifically?

jonathanshaw’s picture

dpi’s picture

Title: Cache not invalidated when entity label changes » Breadcrumb render cache not invalidated when entity label changes

This is all about breadcrumbs.

hugovk’s picture

Issue tags: -Needs change record

Thanks, added a draft change record: https://www.drupal.org/node/3075372.

What's next?

badrange’s picture

Status: Needs review » Reviewed & tested by the community

Setting this to RTBC because this issue has already been set to RTBC in #14, #56 and #69. The patch has also been confirmed to work in #65, and the issue summary has been updated and a change record created as requested in #70.

badrange’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new22.15 KB
new2.69 KB

Before anybody complains about the patch not fixing the bug with outdated breadcrumbs after editing username, here is a fix for 8.8.x with interdiff.

jonathanshaw’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs followup
Related issues: +#3075840: Consider deprecating TitleResolver::getTitle()

User fix looks good.

Follow-up created:
#3075840: Consider deprecating TitleResolver::getTitle()

larowlan’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/lib/Drupal/Core/Controller/CacheableTitle.php
    @@ -0,0 +1,37 @@
    +  public function setTitle($title) {
    +    $this->title = $title;
    

    Do we need this as part of the API? I don't think we do - the only two calls are immediately after a call to new CacheableTitle, should we just make the constructor require a title instead?

    Less api to maintain then

  2. +++ b/core/lib/Drupal/Core/Controller/TitleResolver.php
    @@ -48,12 +49,47 @@ public function __construct(ControllerResolverInterface $controller_resolver, Tr
    +  protected function doGetTitle(Request $request, Route $route, CacheableMetadata &$cacheable_metadata = NULL) {
    

    I don't think we need the & here, objects are always passed by reference

  3. +++ b/core/lib/Drupal/Core/Entity/Controller/EntityController.php
    @@ -301,7 +307,7 @@ public function deleteTitle(RouteMatchInterface $route_match, EntityInterface $_
    +  protected function doGetEntity(RouteMatchInterface $route_match, EntityInterface $_entity = NULL, CacheableMetadata &$cacheable_metadata = NULL) {
    

    same here for the &

  4. +++ b/core/lib/Drupal/Core/Entity/Controller/EntityController.php
    @@ -313,6 +319,9 @@ protected function doGetEntity(RouteMatchInterface $route_match, EntityInterface
    +        $cacheable_metadata->addCacheContexts(['route']);
    

    Are we sure this doesn't force the title to vary on each page its generated on?

    e.g. we generate a title for a breadcrumb in multiple places - if we cache by route does that mean we're not re-using?

    e.g

    node/1/edit
    node/1/revisions

    Does the title resolved for node/1 get recalculated on both pages because the route is different?

  5. +++ b/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
    @@ -252,6 +258,15 @@ public function testBuildWithThreePathElements() {
    +    $this->titleResolver->expects($this->at(0))
    ...
    +    $this->titleResolver->expects($this->at(1))
    

    can we use willReturnMap here instead of ->at, at is brittle - or even willReturnCallback

    actually, we return the same thing both times, so do we even need the at() calls?

badrange’s picture

Status: Needs work » Needs review
StatusFileSize
new24.94 KB
new6.48 KB

Hi larowlan and thank you for the feedback! Here is an updated patch with some responses to your comments, and two more issues added as a bonus.

Note that I have added a new patch, 81, with interdiff from both 80 and 77 for your convenience (regarding constructor).

  1. UPDATED: Please see comment #81 for this
  2. Super, fixed
  3. Super, fixed
  4. You're absolutely right and I have to admit that I have no idea how to fix this properly. Any suggestions are welcome.
  5. Again, I need help. I don't think I understand what you mean in practice by "do we even need the at() calls", if I change core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
        $this->titleResolver
          ->method('getCacheableTitle')
          ->with($this->anything(), $route_1)
          ->will($this->returnValue($cacheable_title));
        $this->titleResolver
          ->method('getCacheableTitle')
          ->with($this->anything(), $route_2)
          ->will($this->returnValue($cacheable_title));
    

    the tests will fail. So I'm stuck with this too.

  6. --- a/core/lib/Drupal/Core/Entity/Controller/EntityController.php
    +++ b/core/lib/Drupal/Core/Entity/Controller/EntityController.php
    @@ -302,12 +302,14 @@ public function deleteTitle(RouteMatchInterface $route_match, EntityInterface $_
        * @param \Drupal\Core\Entity\EntityInterface $_entity
        *   (optional) The entity, set in
        *   \Drupal\Core\Entity\Enhancer\EntityRouteEnhancer.
    +   * @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata
    +   *   The cacheable metadata to attach the cacheable metadata of the title to.

    Oops the parameter was missing from the docblock

  7. --- a/core/modules/system/src/PathBasedBreadcrumbBuilder.php
    +++ b/core/modules/system/src/PathBasedBreadcrumbBuilder.php
    @@ -107,8 +107,8 @@ class PathBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface {
        *   The inbound path processor.
        * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
        *   The config factory service.
    -   * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver
    -   *   The title resolver service.
    +   * @param \Drupal\Core\Controller\CacheableTitleResolverInterface $title_resolver
    +   *   The cacheable title resolver service.

    Ouch, we hadn't updated the parameter in the docblock

  8. +++ b/core/modules/taxonomy/tests/src/Functional/TermTest.php
    @@ -605,6 +605,23 @@ public function testTermBreadcrumbs() {
         $this->assertIdentical($breadcrumbs[0]->getText(), 'Home', 'First breadcrumb text is Home');
         $this->assertIdentical($breadcrumbs[1]->getText(), $term->label(), 'Second breadcrumb text is term name on term delete page.');
         $this->assertEscaped($breadcrumbs[1]->getText(), 'breadcrumbs displayed and escaped.');
    +
    +    // Edit the term and verify that breadcrumb is updated on term edit and
    +    // delete pages.
    +    $editedTerm = [
    +      'name[0][value]' => $this->randomMachineName(14),
    +      'description[0][value]' => $this->randomMachineName(100),
    +      'parent[]' => [0],
    +    ];
    +    $this->drupalPostForm('taxonomy/term/' . $term->id() . '/edit', $edit, t('Save'));
    +    $this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
    +    $breadcrumbs = $this->getSession()->getPage()->findAll('css', 'nav.breadcrumb ol li a');
    +    $this->assertIdentical($breadcrumbs[1]->getText(), $editedTerm['name[0][value]'], 'Edited breadcrumb text is updated on term edit page.');
    +
    +    $this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
    +    $breadcrumbs = $this->getSession()->getPage()->findAll('css', 'nav.breadcrumb ol li a');
    +    $this->assertIdentical($breadcrumbs[1]->getText(), $editedTerm['name[0][value]'], 'Edited breadcrumb text is updated on term edit page.');
    +

    Oh my this is also a problem with term entities. And so far I haven't figured out how to fix it, but this will at least make the tests fail until we nail this. Any pointers?

badrange’s picture

StatusFileSize
new1.28 KB
new24.72 KB
new7.42 KB

I added a solution for 1, I hope you intended something like this.

The last submitted patch, 80: breadcrumb_shows_the_8.8.x-2607920-80.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 81: breadcrumb_shows_the_8.8.x-2607920-81.patch, failed testing. View results

larowlan’s picture

Replies:

1) yep, that's what I meant, do it in the constructor 😎
4) I'd ask for assistance from @WimLeers, I'll ping him in slack for his input
5) just remove the with() from both? it always returns the same thing regardless, so I think that is fine

Good catch on the terms, haven't looked into where to fix that yet.

jibran’s picture

4. 'url.path.parent' might work. This allows for caching based on the path, excluding everything after the last forward slash.

badrange’s picture

StatusFileSize
new1.1 KB
new24.48 KB

1,2,3 should be ok

4) Waiting to hear from WimLeers or others. I'm in the Drupal Slack under the same handle as here, badrange

5) Was this how you meant? (see attached patch + interdiff). There was no need for two titleresolver calls anymore after removing with. I really don't know what I'm doing here.

6,7 should be ok

8) What about creating a separate follow up issue for this, scope creep is the best way to ensure that this issue doesn't get merged before Greenland melts.

jonathanshaw’s picture

#80.8

I think the relvant taxonomy code is in core/modules/taxonomy/src/Controller/TaxonomyController.php

  public function termTitle(TermInterface $taxonomy_term) {
    return ['#markup' => $taxonomy_term->getName(), '#allowed_tags' => Xss::getHtmlTagList()];
  }

However, the problem goes far beyond taxonomy terms. In fact it seems to apply in most places that use title callbacks.

A few minutes searching for _title_callback led me to the following (at least) that depend on an entity and probably need to be adjusted for cacheability:
node/src/Controller/NodePreviewController::title
taxonomy\Controller\TaxonomyController::vocabularyTitle
node\Controller\NodeController::revisionPageTitle
filter\Controller\FilterController::getLabel
comment\Controller\CommentController::commentPermalinkTitle
menu_ui\Controller\MenuController::menuTitle

An interesting different case is tracker\Controller\TrackerUserTab::getTitle which has

  /**
   * Title callback for the tracker.user_tab route.
   */
  public function getTitle(UserInterface $user) {
    return $user->getAccountName();
  }

All of this makes me wonder if the fix proposed here is too gentle, relies too much on developers opting in to it.

Could we instead or also intercept the title callback and ALWAYS add the first argument supplied to the callback method as a cacheable dependency? Developers would be free to remove the dependency in the vanishingly unlikely event it wasn't needed, but we wouldn't have hundreds of contrib modules forgetting to add it.

badrange’s picture

Status: Needs work » Needs review
StatusFileSize
new24.46 KB
new4.69 KB

First: Thanks to jibran for the 'url.path.parent' suggestion, it seems to work!

Second: I realised that the change in #81 where we removed setTitle() in favour of a constructor broke the patch: The title stopped updating itself. I reverted that change, and while looking into this realised that the function getCacheableTitle in core/lib/Drupal/Core/Controller/TitleResolver.phpwas actually calling doGetTitle with the parameters

    $title = $this->doGetTitle($request, $route, $cacheable_title);

, so I updated the parameter naming and docblock for doGetTitle().

Why did the conversion from setTitle to __constructor break this? Well, for nodes as an example, public function title() in core/modules/node/src/Controller/NodeViewController.php adds a cacheable dependency on the node in question to $cacheable_metadata. If we use the constructor way, that information gets lost. I don't know what I'm doing wrong.

Third: It does make sense to intercept the title callback as jonathanshaw proposes, although that probably requires comments and feedback from a lot more people (could there any bc breaks?). Wonder if it would be a good idea to split this into several steps so that we can move forward.

Status: Needs review » Needs work

The last submitted patch, 88: breadcrumb_shows_the_8.8.x-2607920-88.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

wim leers’s picture

Issue tags: +D8 cacheability

#79.4 wrote:

Are we sure this doesn't force the title to vary on each page its generated on?

Does the title resolved for node/1 get recalculated on both pages because the route is different?

4 years and 9 days ago, #2483183: Make breadcrumb block cacheable introduced class Breadcrumb implements RenderableInterface, RefinableCacheableDependencyInterface, which brought cacheability to breadcrumbs 😊 #2699627: url.path cache context for breadcrumbs is unnecessarily granular improved its cacheability.

That same issue made the different breadcrumb builders associate different cacheability, based on their strategy:

  • \Drupal\book\BookBreadcrumbBuilder associates the route.book_navigation cache context (because it applies only to Node routes that are part of books) and the cache tag of the book
  • \Drupal\taxonomy\TermBreadcrumbBuilder associates the route cache context (it applies only to canonical Term routes) and the cache tags of each term in the lineage
  • \Drupal\system\PathBasedBreadcrumbBuilder can be used for any route: like it's name says, it's only path-based. Hence it varies by url.path.parent and url.path.is_front plus whatever cacheability route access returns
  • … and more

I'm surprised by the bug report and its steps to reproduce, because PathBasedBreadcrumbBuilder does this:

        $route_match = RouteMatch::createFromRequest($route_request);
        $access = $this->accessManager->check($route_match, $this->currentUser, NULL, TRUE);
        // The set of breadcrumb links depends on the access result, so merge
        // the access result's cacheability metadata.
        $breadcrumb = $breadcrumb->addCacheableDependency($access);
        if ($access->isAllowed()) {
          $title = $this->titleResolver->getTitle($route_request, $route_match->getRouteObject());

and while the title indeed doesn't carry cacheability (and this is indeed a problem), the $access alone should still have the node's cache tag. Every node save, for a new revision or not, triggers an invalidation of the node's cache tag. Yet that is somehow insufficient here. Why is that?! 🤔

  1. +++ b/core/lib/Drupal/Core/Controller/CacheableTitle.php
    @@ -0,0 +1,46 @@
    +class CacheableTitle extends BubbleableMetadata {
    

    🤔 Can a title also contain attachments, such as assets or HTTP response headers? I don't think so?

    Hence I think this should implement CacheableDependencyInterface and use CacheableDependencyTrait.

  2. +++ b/core/lib/Drupal/Core/Controller/TitleResolver.php
    @@ -48,12 +49,47 @@ public function __construct(ControllerResolverInterface $controller_resolver, Tr
    +   * @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata
    +   *   The cacheable metadata to attach the cacheable metadata of the title to.
    ...
    +        $request->attributes->set('cacheable_metadata', $cacheable_metadata);
    

    Let's name this $cacheability instead, like we do in many places. Related: #2561773: CacheableMetadata is misnamed.

    Also, let's typehint to RefinableCacheableDependencyInterface, not to a concrete class.

  3. +++ b/core/lib/Drupal/Core/Controller/TitleResolver.php
    @@ -68,6 +104,9 @@ public function getTitle(Request $request, Route $route) {
    +        if ($cacheable_metadata) {
    +          $cacheable_metadata->addCacheContexts(['route']);
    +        }
    

    🤔 Ideally, we'd typehint to CacheableDependencyInterface instead of what I wrote in the previous bullet, but this makes that impossible. And this is what I was asked to investigate.

    Every title is dependent on the route it's being generated for. But the code computing a title is not asked to compute a title for many things; it is always given a concrete route match already.

    Cacheability metadata (https://www.drupal.org/docs/8/api/cache-api/cache-api#cacheability-metadata) is meant to indicate that given a certain input, what variations of the input cause a different output. Varying by route only makes sense if the input consists of multiple routes. That's not the case here.

    The path-based breadcrumb builder is what should be doing the necessary varying. And there is a known bug there, that has not seen much movement because clear steps to reproduce are missing: #2719721: BreadcrumbBuilder::applies() mismatch with cacheability metadata. I think this line may be necessary here because that issue has not yet been fixed. Let's fix that first? :)

  4. +++ b/core/lib/Drupal/Core/Entity/Controller/EntityController.php
    @@ -243,12 +244,17 @@ public function addBundleTitle(RouteMatchInterface $route_match, $entity_type_id
    +   * @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata
    +   *   (optional) The cacheable metadata for the title callback.
    

    Instead of receiving an optional object by reference, only to modify it to pass out-of-band information as a side effect, I think it would be better to follow the pattern that \Drupal\Core\Access\AccessibleInterface::access() also uses: a $return_as_object boolean, defaulting to FALSE, but when opting in to this by passing TRUE instead, it would make this code return a CacheableTitle object.

jonathanshaw’s picture

Looks like intercepting the title callback might be straightforward: in TitleResolver::doGetTitle() maybe we could do something like:

    if ($callback = $route->getDefault('_title_callback')) {
      $callable = $this->controllerResolver->getControllerFromDefinition($callback);
      $arguments = $this->argumentResolver->getArguments($request, $callable);
      $route_title = call_user_func_array($callable, $arguments);
      foreach($arguments as $argument) {
        $routeTitle->addCacheableDependency($argument);
      }
    }

And this seems not unreasonable because the arguments here are not specified by the caller but instead are requested by the callback provider; if a method is requesting an argument then it seems reasonable to assume that the title it calculates varies depending on the arguments is has requested.

badrange’s picture

Thank you for the extra information, Wim Leers, and for the feedback from you and jonathanshaw. I checked out the 8.8.x branch on my machine, set some breakpoints and stepped through the code trying to figure out why the $access didn't carry the cache tag as it should, and started wondering if I was on to something when I encountered these lines of code in core/modules/node/src/NodeAccessControlHandler.php's public function access():

    if ($account->hasPermission('bypass node access')) {
      $result = AccessResult::allowed()->cachePerPermissions();
      return $return_as_object ? $result : $result->isAllowed();
    }

Neither AccessResult::allowed() nor cachePerPermissions() keep the $entity's cache tag right?

If I add (false &&) to the if so that the if always fails, the error goes away for nodes. If I log in as an editor without the bypass node access permission, the breadcrumb changes itself as it should.

The bug affects both my editor user and administrator/user 1 when editing terms and user accounts. Are we on to something here?

badrange’s picture

I want to be fully transparent: I'm not able to continue with this patch as I started in a new project at work. I hope (the magic) someone is able to pick up where I left off. I'm sorry about this, I wanted to get it all the way to the end but it turned out to be way out of my league.

larowlan’s picture

@badrange - no need to be sorry - thanks for everything you've done so far! 94 comments indicates this isn't a simple fix, and you've keep this moving, someone else will pick up the baton from here

🏆

badrange’s picture

StatusFileSize
new24.46 KB

Here is a reroll of #88 after a change to function signature in core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php

Poor man's interdiff:

-+   * @var \Drupal\Core\Controller\CacheableTitleResolverInterface|\PHPUnit_Framework_MockObject_MockObject
++   * @var \Drupal\Core\Controller\CacheableTitleResolverInterface|\PHPUnit\Framework\MockObject\MockObject

I still hope "someone" will be able to take Wim Leers comments in #90 and jonathanshaw's in #91 into account.

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.

gease’s picture

StatusFileSize
new24.48 KB

The patch from #95 wasn't applicable, so a simple reroll.

gease’s picture

StatusFileSize
new24.51 KB
new698 bytes

Fixed unit test.

hchonov’s picture

Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new1017.62 KB
new25.79 KB
new2.95 KB

I'm surprised by the bug report and its steps to reproduce, because PathBasedBreadcrumbBuilder does this:

        $route_match = RouteMatch::createFromRequest($route_request);
        $access = $this->accessManager->check($route_match, $this->currentUser, NULL, TRUE);
        // The set of breadcrumb links depends on the access result, so merge
        // the access result's cacheability metadata.
        $breadcrumb = $breadcrumb->addCacheableDependency($access);
        if ($access->isAllowed()) {
          $title = $this->titleResolver->getTitle($route_request, $route_match->getRouteObject());

and while the title indeed doesn't carry cacheability (and this is indeed a problem), the $access alone should still have the node's cache tag. Every node save, for a new revision or not, triggers an invalidation of the node's cache tag. Yet that is somehow insufficient here. Why is that?! 🤔

@Wim Leers, I just checked what happens in \Drupal\Core\Access\AccessManager::check() when called from within \Drupal\system\PathBasedBreadcrumbBuilder::build() during the generation of the breadcrumb on the term/edit page. See the screenshot from the debugger. It looks like the term/view is represented by a view (views.view.taxonomy_term) even though it has also an own route in case views is not installed. The view itself has only a permission access, which is the reason why the access result does not carry any term cacheability metadata. I am not familiar with views to comment on our possible options from here.

Here is the screenshot:

I've also managed to find out why the term breadcrumb test is failing. The usage post values variable was inconsistent in the test, but the most important part is that the title was retrieved through \Drupal\taxonomy\Controller\TaxonomyController::termTitle(), which didn't had yet the new parameter $cacheable_metadata.

I haven't fully reviewed the patch yet, but it would be great if we could think of a more general solution. I think that the current approach requires too much knowledge when defining a title callback.

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.

pankaj.singh’s picture

Assigned: Unassigned » pankaj.singh
pankaj.singh’s picture

Tested the patch given in #99 on 9.1.x-dev. It's working fine on my end.
Please refer to the SS for ref. RTBC+1.

tanubansal’s picture

StatusFileSize
new308.24 KB

I can't see breadcrumb via authenticated user
Any patch available for 9.1?

hugovk’s picture

Issue summary: View changes
StatusFileSize
new2.64 KB
new25.78 KB

Here's a reroll of #99 for Drupal 8.9.2.

Status: Needs review » Needs work

The last submitted patch, 104: breadcrumb_shows_the_8.9.2-2607920-104.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

hugovk’s picture

Fixed re-roll of #99 in #104.

hugovk’s picture

Status: Needs work » Needs review
ayushmishra206’s picture

Version: 9.1.x-dev » 8.9.x-dev
Assigned: Unassigned » ayushmishra206
ayushmishra206’s picture

Tested the patch #106 on 8.9.x-dev. It's working fine, i am attaching screenshots.

ayushmishra206’s picture

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

Needs a reroll for 9.1.x-dev

ayushmishra206’s picture

Status: Needs review » Needs work
hugovk’s picture

Status: Needs work » Reviewed & tested by the community

Thank you for testing! Updating as RTBC.

jonathanshaw’s picture

Status: Reviewed & tested by the community » Needs review

The discussion in #90 and #99 is not remotely resolved. The patch may seem to work, but there are major doubts about the correct approach.

ayushmishra206’s picture

larowlan’s picture

Issue tags: +Bug Smash Initiative

Tagging this for visibility

manish-31’s picture

@ hugovk patch #106 failed to apply while patch #99 applied successfully on my drupal 8.9.x site can you please let me know whether it is good to use #99 ? Thanks in advance!

hugovk’s picture

@manish-31 We're using patch #106 on our 8.9.5 site, and it's applying successfully. I originally made #106 as a re-roll of #99 for 8.9.2 (most likely because #99 didn't apply for 8.9.2).

thalles’s picture

After apply #114 my Drupal 8 environment broken.

Fatal error: Interface 'Drupal\Core\Controller\CacheableTitleResolverInterface' not found in /app/core/lib/Drupal/Core/Controller/TitleResolver.php on line 14

Call Stack:
    0.0008     412520   1. {main}() /app/index.php:0
    0.0073     536640   2. Drupal\Core\DrupalKernel->handle() /app/index.php:19
    0.0268    1612488   3. Stack\StackedHttpKernel->handle() /app/core/lib/Drupal/Core/DrupalKernel.php:706
    0.0268    1612488   4. Drupal\Core\StackMiddleware\NegotiationMiddleware->handle() /app/vendor/stack/builder/src/Stack/StackedHttpKernel.php:23
    0.0269    1613184   5. Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle() /app/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php:52
    0.0269    1613184   6. Drupal\page_cache\StackMiddleware\PageCache->handle() /app/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php:47
    0.0272    1615800   7. Drupal\page_cache\StackMiddleware\PageCache->pass() /app/core/modules/page_cache/src/StackMiddleware/PageCache.php:85
    0.0272    1615800   8. Drupal\Core\StackMiddleware\KernelPreHandle->handle() /app/core/modules/page_cache/src/StackMiddleware/PageCache.php:106
    0.0333    1750448   9. Drupal\Core\StackMiddleware\Session->handle() /app/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php:47
    0.0380    1852520  10. Symfony\Component\HttpKernel\HttpKernel->handle() /app/core/lib/Drupal/Core/StackMiddleware/Session.php:57
    0.0380    1853576  11. Symfony\Component\HttpKernel\HttpKernel->handleRaw() /app/vendor/symfony/http-kernel/HttpKernel.php:80
    0.1283    7703808  12. Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch() /app/vendor/symfony/http-kernel/HttpKernel.php:163
    0.1286    7779568  13. call_user_func:{/app/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php:142}() /app/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php:142
    0.1286    7779568  14. Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray() /app/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php:142
    0.1286    7779568  15. Drupal\Core\DependencyInjection\ClassResolver->getInstanceFromDefinition() /app/core/lib/Drupal/Core/EventSubscriber/MainContentViewSubscriber.php:89
    0.1286    7779568  16. Drupal\Core\DependencyInjection\Container->get() /app/core/lib/Drupal/Core/DependencyInjection/ClassResolver.php:20
    0.1287    7785224  17. Drupal\Core\DependencyInjection\Container->createService() /app/core/lib/Drupal/Component/DependencyInjection/Container.php:171
    0.1287    7785224  18. Drupal\Core\DependencyInjection\Container->resolveServicesAndParameters() /app/core/lib/Drupal/Component/DependencyInjection/Container.php:235
    0.1287    7785224  19. Drupal\Core\DependencyInjection\Container->get() /app/core/lib/Drupal/Component/DependencyInjection/Container.php:432
    0.1287    7788432  20. Drupal\Core\DependencyInjection\Container->createService() /app/core/lib/Drupal/Component/DependencyInjection/Container.php:171
    0.1287    7788872  21. spl_autoload_call() /app/core/lib/Drupal/Component/DependencyInjection/Container.php:257
    0.1287    7788936  22. Composer\Autoload\ClassLoader->loadClass() /app/core/lib/Drupal/Component/DependencyInjection/Container.php:257
    0.1287    7789048  23. Composer\Autoload\includeFile() /app/vendor/composer/ClassLoader.php:322
    0.1290    7790032  24. include('/app/core/lib/Drupal/Core/Controller/TitleResolver.php') /app/vendor/composer/ClassLoader.php:444
thalles’s picture

Status: Needs review » Needs work

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.

larowlan’s picture

Issue tags: +DrupalGov 2020
govind.maloo’s picture

Status: Needs work » Needs review
StatusFileSize
new44.62 KB
new29.99 KB

Reroll for 9.2.x

govind.maloo’s picture

StatusFileSize
new42.65 KB

Status: Needs review » Needs work

The last submitted patch, 123: 9.2.x-2607920-123.patch, failed testing. View results

govind.maloo’s picture

StatusFileSize
new42.4 KB
govind.maloo’s picture

Status: Needs work » Needs review
hershey.k’s picture

Version: 9.2.x-dev » 9.1.x-dev
StatusFileSize
new25.8 KB
new3.6 KB

Reroll for latest 9.1.x (9.1.3 as of this writing). All previous patches from #106 have line conflicts in one of two test files.

(Patch #114 is missing new Interface, hence error outlined in #118).

hershey.k’s picture

Version: 9.1.x-dev » 9.2.x-dev
hershey.k’s picture

StatusFileSize
new25.55 KB
hershey.k’s picture

StatusFileSize
new25.55 KB
hershey.k’s picture

StatusFileSize
new25.54 KB
new3.6 KB
sker101’s picture

Status: Needs review » Reviewed & tested by the community

#131 works for me on 9.1.x branch
Moving it to RTBC

jonathanshaw’s picture

Status: Reviewed & tested by the community » Needs review

The discussion in #90 and #99 is not remotely resolved. The patch may seem to work, but there are major doubts about the correct approach.

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.

hershey.k’s picture

StatusFileSize
new25.53 KB

Re-rolling existing patch for latest stable 9.1.10 release.

rinku jacob 13’s picture

StatusFileSize
new57.49 KB
new58.07 KB

Verified and tested patch#135 on the drupal 9.3.x-dev version. Patch applied successfully and looks good to me.Need +1 RTBC.

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.

bhanu951’s picture

Seems the patch is not working when there is a `_title_callback:` key in `*.routing.yml` title breadcrumb is not changed after title callback is changed.


my_module.response_controller:
  path: '/my-module-api/get/response/{api_id}'
  defaults:
    _title_callback: '\Drupal\my_module\Controller\MyModuleController::getTitle'
    _controller: '\Drupal\my_module\Controller\MyModuleController::build'
    api_id: '12345685'
  requirements:
    _permission: 'administer my-module api configuration'

/**
   * Builds the API response.
   */
  public function getTitle(string $api_id) {
    return $this->t('API Response for : @data', ['@data' => $api_id]);
  }

Title Response Breadcrumb

bhanu951’s picture

StatusFileSize
new81.33 KB

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

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

ranjith_kumar_k_u’s picture

StatusFileSize
new25.4 KB
new448 bytes

Status: Needs review » Needs work

The last submitted patch, 142: 2607920-142.patch, failed testing. View results

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

penyaskito’s picture

Status: Needs work » Needs review
StatusFileSize
new22.94 KB

Rerolled #135. I worked against 9.4.x because that's what my project is using, so testing the patch against that for now.

I did a couple of changes, explained next comment.

penyaskito’s picture

  1. +++ b/core/lib/Drupal/Core/Controller/CacheableTitleResolverInterface.php
    @@ -0,0 +1,35 @@
    +interface CacheableTitleResolverInterface extends TitleResolverInterface {
    

    I made the cacheable title resolver interface extend title resolver interface, to ensure we can replace that one without API changes.

  2. +++ b/core/modules/system/src/PathBasedBreadcrumbBuilder.php
    @@ -7,6 +7,7 @@
    @@ -174,7 +175,14 @@ public function build(RouteMatchInterface $route_match) {
    
    @@ -174,7 +175,14 @@ public function build(RouteMatchInterface $route_match) {
    +          if ($this->titleResolver instanceof CacheableTitleResolverInterface) {
    +            $cacheable_title = $this->titleResolver->getCacheableTitle($route_request, $route_match->getRouteObject());
    +            $breadcrumb->addCacheableDependency($cacheable_title);
    +            $title = $cacheable_title->getTitle();
    +          }
    

    We cannot change the interface being injected for BC, so we need this check here and if someone replaced the injected service use the previous getTitle method.

  3. +++ b/core/modules/user/src/Controller/UserController.php
    @@ -304,7 +305,10 @@ public function userEditPage() {
    +    if ($user && $cacheable_metadata) {
    

    Simplified this clause.

I still think this needs work, as some namespaces don't make sense to me, but maybe only moving things around.

penyaskito’s picture

penyaskito’s picture

Status: Needs review » Needs work
penyaskito’s picture

Status: Needs work » Needs review
StatusFileSize
new533 bytes
new23.43 KB

This was missing a use statement.

Status: Needs review » Needs work
penyaskito’s picture

Status: Needs work » Needs review
StatusFileSize
new2.64 KB
new23.53 KB

Removed deprecations that made tests fail.

Status: Needs review » Needs work
penyaskito’s picture

Version: 10.1.x-dev » 9.5.x-dev

The failure is a known random failure, so tests are green-ish.

This is a bug, so I think it can be targeted to 9.5.x

However, #90 and #99 still need to be addressed.

gustavowal’s picture

We have the same issue on fairly recent built website with D9 from the get-go, this issue only seems to be happening when using render cache to cache.backend.memcache, this happens on both memcache 1.5 and 1.6 with default memcache parameters on AWS.

We tried patch #142 and #152, but without success.

The issue is very straightforward to replicate, on an admin logged session editing the title that also changes the breadcrumb and going to a non-logged session, the title will reflect the change and also the URL, but the breadcrumb will stay the same. Viewing the page on the logged-in session shows up the updated breadcrumb right away, due to logged-in not being cacheable.

Using telnet to login into the memcache instance and running a full flush (FLUSH_ALL) and refreshing the non-logged-in session, the breadcrumb will update to reflect the correct changes.

Using render cache set to cache.backend.memory or even cache.backend.null the issue is not present.

sker101’s picture

Rerolling #152 for Drupal 9.5.x

sker101’s picture

StatusFileSize
new23.51 KB

Fixes CS error, how was coding standard test passing with patch #152?

bhanu951’s picture

Re Rolled MR #3196 for 10.x branch and added TimeInterface argument for constructor and depreciation for REQUEST_TIME .

bhanu951’s picture

Version: 9.5.x-dev » 10.1.x-dev
bhanu951’s picture

Status: Needs work » Needs review
bhanu951’s picture

StatusFileSize
new138.1 KB

Seems the patch is not resolving the issue, I created a test controller based on #138 refer image below.
Title Response Text but the issue is not resolved. Still different breadcrumb than that of title.

smustgrave’s picture

Status: Needs review » Needs work

MR has many test failures.

Also I tried to reproduce following the IS on Drupal 10 and was not able to replicate. Title updates just fine for me, but maybe it's just me. Will rely on the tests to prove me wrong.

bhanu951’s picture

Status: Needs work » Needs review
StatusFileSize
new4.75 KB

Created a test fail patch based on #138 to prove issue exists.

smustgrave’s picture

Status: Needs review » Needs work

Added some feedback.

Rule of thumb I've been trying to use for new functions on interfaces or classes we should try to typehint.

Tests look good!

bhanu951’s picture

Fixed phpstan-baseline error in the fail test patch in #164.

Interdiff-164-166


diff --git a/core/phpstan-baseline.neon b/core/phpstan-baseline.neon
index 1e5797ced0..7ca7501bfd 100644
--- a/core/phpstan-baseline.neon
+++ b/core/phpstan-baseline.neon
@@ -2177,7 +2177,7 @@ parameters:
 
 		-
 			message: "#^Variable \\$goto in isset\\(\\) always exists and is not nullable\\.$#"
-			count: 1
+			count: 2
 			path: modules/system/tests/src/Functional/Menu/AssertBreadcrumbTrait.php
 
bhanu951’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work
bhanu951’s picture

As @gustavowal mentioned in #155

The issue is very straightforward to replicate, on an admin logged session editing the title that also changes the breadcrumb and going to a non-logged session, the title will reflect the change and also the URL, but the breadcrumb will stay the same. Viewing the page on the logged-in session shows up the updated breadcrumb right away, due to logged-in not being cacheable.

I think we need to update fail test patch to consider logged in user as well. Which is currently not included.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

stomusic’s picture

Re-roll for 10.2.x

sker101’s picture

Rerolling again for latest 10.2.x

Bhanu951 changed the visibility of the branch 2607920-breadcrumb-render-cache to hidden.

bhanu951’s picture

bhanu951’s picture

sker101’s picture

Recreated rerolling patch for 10.2.x since the "$title" property in the "CacheableTitle" class could be array type as well and throwing the error after adding the type hint.

brandonlira’s picture

I tested patch #176 for version 10.2.5 and it worked correctly.

bhanu951’s picture

Bhanu951 changed the visibility of the branch 2607920-breadcrumb-render-cache-10.x to hidden.

Bhanu951 changed the visibility of the branch 2607920-breadcrumb-render-cache-10.x to active.

Bhanu951 changed the visibility of the branch 11.x to hidden.

bhanu951 changed the visibility of the branch 2607920-breadcrumb-render-cache-fail-test to hidden.

ammaletu made their first commit to this issue’s fork.

ammaletu’s picture

That this fundamental bug is still open after 9 years is... oh well. Are we all just using "Easy Breacrumb" which handles this correctly?

Looking at the MR, there is the request to add type hints in some places. Makes sense, I added the requested type hints. Looking at the code, I also wonder:

  • Shouldn't variables use camel case instead of snake case? Should it therefore be $cacheableTitle for local variables in TitleResolver?!
  • Is Drupal\Core\Controller a good namespace for the new CacheableTitle class? I would have expected only controllers in this namespace. Then again, the TitleResolver is not a controller and is living there as well.

I could change the variable cases if somebody agrees this should be done. Then lets see what the pipeline says. I see the last run failed with stuff that has nothing to do with this MR (linting and spelling in JavaScipt files). Maybe somebody who knows how this works needs to merge the current 11.x state in here?!

sker101’s picture

Adding a static patch file here for the latest merge request changes. The type declarations added from the previous merge request commits for the "$title" property was breaking our site because the property could also receive an array as its value.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.