Problem/Motivation

If you have menu links on multiple levels they will fall to the first level on cache rebuild. This happens only for the menu links that are defined to link for a url. Issue doesn't exist for menu links linking for a node where autocomplete functionality has been used.

Proposed resolution

This seems to be caused by this piece of code in \Drupal\Core\Menu\MenuTreeStorage:

protected function saveRecursive($id, &$children, &$links) {
    if (!empty($links[$id]['parent']) && empty($links[$links[$id]['parent']])) {
      // Invalid parent ID, so remove it.
      $links[$id]['parent'] = '';
    }
}

Remaining tasks

-

User interface changes

-

API changes

-

Comments

sumitmadan’s picture

I found that issue is here in rebuild(array $definitions) method :

if ($definitions) {
  foreach ($definitions as $id => $link) {
    // Flag this link as discovered, i.e. saved via rebuild().
    $link['discovered'] = 1;
    if (!empty($link['parent'])) {
      $children[$link['parent']][$id] = $id;
    }
    else {
      // A top level link - we need them to root our tree.
      $top_links[$id] = $id;
      $link['parent'] = '';
    }
    $links[$id] = $link;
  }
}
foreach ($top_links as $id) {
  $this->saveRecursive($id, $children, $links);
}
// Handle any children we didn't find starting from top-level links.
foreach ($children as $orphan_links) {
  foreach ($orphan_links as $id) {
    // Force it to the top level.
    $links[$id]['parent'] = '';
    $this->saveRecursive($id, $children, $links);
  }
}
sumitmadan’s picture

Status: Active » Needs review
StatusFileSize
new988 bytes

Yeah the issue was on both the places. Created a patch hope this will work. :)

mark.’s picture

Status: Needs review » Reviewed & tested by the community

Applied patch and manually tested against a menu with a hierarchy 2, 3, and 4 levels deep. It works perfectly on my end. Nice work!

lauriii’s picture

Status: Reviewed & tested by the community » Needs work

Still needs tests

lauriii’s picture

So apparently because of the head wasn't broken (automated tests were passing), there wasn't test coverage for this exact issue and for that reason it has to be created so that the bug doesn't occur in future, or if it does it can be noticed because of failing tests.

sumitmadan’s picture

Assigned: Unassigned » sumitmadan

Creating a test case.

sumitmadan’s picture

Assigned: sumitmadan » Unassigned
Status: Needs work » Needs review
StatusFileSize
new4.33 KB
new2.64 KB

Written a test case "MenuParentChildRelationshipTest".

lauriii’s picture

Status: Needs review » Needs work
Issue tags: -Needs tests

Test overally looks good. Could you post a only test patch to see it is failing before the fix? These things shouldbe still checked:

  1. +++ b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php
    @@ -178,7 +178,6 @@ public function rebuild(array $definitions) {
         foreach ($children as $orphan_links) {
           foreach ($orphan_links as $id) {
             // Force it to the top level.
    -        $links[$id]['parent'] = '';
             $this->saveRecursive($id, $children, $links);
           }
         }
    

    Is this logic around the removed line still necessary? Does the comment still apply?

  2. +++ b/core/modules/system/src/Tests/Menu/MenuParentChildRelationshipTest.php
    @@ -0,0 +1,85 @@
    +  public static $modules = array('node', 'menu_link_content');
    ...
    +    entity_create('menu', array(
    ...
    +    $base_options = array(
    ...
    +    $node = entity_create('node', array('type' => 'page', 'title' => 'Test Title'));
    ...
    +    $child = $base_options + array(
    

    There is still mixing of old and new array syntax. Maybe we should use only the new syntax?

  3. +++ b/core/modules/system/src/Tests/Menu/MenuParentChildRelationshipTest.php
    @@ -0,0 +1,85 @@
    +   * Tests creating links with the path created using views and test relationship on rebuild.
    

    Comment has to be made shorter since its over 80 characters long

sumitmadan’s picture

Status: Needs work » Needs review
StatusFileSize
new0 bytes
new0 bytes

Uploaded test only to check fail test.

sumitmadan’s picture

StatusFileSize
new2.61 KB

Oops!! Sorry.

sumitmadan’s picture

StatusFileSize
new8.22 KB
new3.49 KB

Uploading the patch with short array syntax and comments fixing.

Reason to remove the following code :
$links[$id]['parent'] = '';

is that because it was setting all children's parent to blank string. The condition to unset parent is already checked in "saveRecursive" method.

The last submitted patch, 10: menu_rebuild_is_broken-2495641-10.patch, failed testing.

lauriii’s picture

Status: Needs review » Needs work

I think there is something wrong on the last patch? Seems like some of the changes are twice there or something?

sumitmadan’s picture

Status: Needs work » Needs review

I applied this patch and worked fine. Actually this is created using

git format-patch master --stdout > patchname.patch

Command. Which creates the patch like this.

tim.plunkett’s picture

Status: Needs review » Needs work

Please use git diff, format-patch is much harder to parse for a human.

Also, please revert the unnecessary array() to [] changes. We all prefer [] but that's out of scope for this issue.

lauriii’s picture

tim.plunkett: Thats what was the weird part on the patch. That file is created in that issue so its all new code and for that reason I wanted the array syntax to be the new one. On the patch it looks like its changing already existing file so its like patch and interdiff in one file?

sumitmadan’s picture

Status: Needs work » Needs review
StatusFileSize
new3.65 KB

Updated the patch with git diff command.

sumitmadan’s picture

StatusFileSize
new3.66 KB
new1.54 KB

Used Menu::create method instead of entity_create.

tim.plunkett’s picture

StatusFileSize
new3.91 KB
new2.79 KB

As a web test, this test takes 18 seconds. Rewritten to be a kernel test, it takes 2 seconds.
This was what I was trying to describe on IRC, sorry for just jumping in and doing it.

dawehner’s picture

Status: Needs review » Needs work

I know that we have an issue for that already ... and another one I can't find at the moment ... #2477337: Saving a menu item with internal path instead of entity reference loses hierarchy

Note: We need a better issue title here, its not helpful at all

  1. +++ b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php
    @@ -798,7 +796,7 @@ public function getExpanded($menu_name, array $parents) {
    -    if (!empty($links[$id]['parent']) && empty($links[$links[$id]['parent']])) {
    +    if (!empty($links[$id]['parent']) && empty($children[$links[$id]['parent']])) {
    

    That totally makes sense.

  2. +++ b/core/modules/system/src/Tests/Menu/MenuParentChildRelationshipTest.php
    @@ -0,0 +1,94 @@
    +  public function testParentChildRelationshipOnMenuRebuild() {
    

    Do you mind moving this directly to MenuTreeSTorageTest? It is already a kernel Test ....

  3. +++ b/core/modules/system/src/Tests/Menu/MenuParentChildRelationshipTest.php
    @@ -0,0 +1,94 @@
    +    $this->menuLinkManager->deleteLinksInMenu('menu_test');
    ...
    +    $node = Node::create(['type' => 'page', 'title' => 'Test Title']);
    +    $node->save();
    ...
    +    $child = $base_options + [
    +      'link' => [['uri' => 'internal:/admin/content/files']],
    +      'parent' => $links['parent'],
    

    seems not really needed here ...

  4. +++ b/core/modules/system/src/Tests/Menu/MenuParentChildRelationshipTest.php
    @@ -0,0 +1,94 @@
    +      'link' => [['uri' => 'entity:node/' . $node->id()]],
    

    it would be great if we could just use test entity types here?

sumitmadan’s picture

Status: Needs work » Needs review
StatusFileSize
new4.32 KB
new6.16 KB

Moved testParentChildRelationshipOnMenuRebuild() to MenuTreeSTorageTest.

Please let me know why following code is not needed? Without adding a child how we will test parent child relationship?

$child = $base_options + [
      'link' => [['uri' => 'internal:/admin/content/files']],
      'parent' => $links['parent'],
dawehner’s picture

I was more talking about the need to create a node.

sumitmadan’s picture

StatusFileSize
new549 bytes
new4.29 KB

Removed extra line of code.

dawehner’s picture

Adding tag to explain what is going on.

catch’s picture

Title: Menu rebuild is broken » Menu rebuild breaks link hierarchy
dawehner’s picture

Priority: Major » Critical

Isn't that issue pretty much the same as #2468713: Internal/custom menu links force-reset to the top of their menus on cache rebuild ?
Breaking link hierarchy is the same problem on the other issue so moving this to critical as well.

alexpott’s picture

Status: Needs review » Needs work

#2468713: Internal/custom menu links force-reset to the top of their menus on cache rebuild has changed some of the same lines - so this will need a reroll and confirmation that it is still an issue.

sumitmadan’s picture

Assigned: Unassigned » sumitmadan

I confirms that this is still and issue. I will update the patch.

sumitmadan’s picture

Assigned: sumitmadan » Unassigned
Status: Needs work » Needs review

Just checked. Patch applied cleanly. I believe there is no need to update the patch. Moving this again to needs review.

dawehner’s picture

StatusFileSize
new3.26 KB

This is a test only patch, I'm curious whether this still fails after the other issue.

If this doesn't fail, we are safe and can mark the issue as fixed.

jibran’s picture

Why not add this test anyway.

  1. +++ b/core/modules/system/src/Tests/Menu/MenuTreeStorageTest.php
    @@ -462,4 +486,38 @@ protected function assertMenuLink($id, array $expected_properties, array $parent
    +   * Tests creating links with the path created using views and test
    +   * relationship on rebuild.
    

    Should be online.

  2. +++ b/core/modules/system/src/Tests/Menu/MenuTreeStorageTest.php
    @@ -462,4 +486,38 @@ protected function assertMenuLink($id, array $expected_properties, array $parent
    +  public function testParentChildRelationshipOnMenuRebuild() {
    

    After rebuild sounds much better.

dawehner’s picture

Why not add this test anyway.

True, would be nice to not rely on nodes but rather on entity_test entities for example though.

testParentChildRelationshipOnMenuRebuild
It would be great to somehow communicate in the title that this is about parent entries being something else than static menu links.

Status: Needs review » Needs work

The last submitted patch, 31: 2495641-28.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new3.26 KB

Reupload the patch, might have been a random failure.

dawehner’s picture

Status: Needs review » Closed (duplicate)

This test is really basically the same as the new test in #2468713: Internal/custom menu links force-reset to the top of their menus on cache rebuild IMHO we could mark it as duplicate.

What about adding the contributors of this issue onto #2477337: Saving a menu item with internal path instead of entity reference loses hierarchy, that would be nice!

alexpott’s picture

I've added @sumitmadan and @tim.plunkett to the other commit message - if they comment on that issue I'll update the commit credit on issue too.