This is a cross-post of https://www.drupal.org/node/2691929 since the issue lies in Pathauto rather than Token.
As described on that issue, re: using a Pathauto pattern with ':menu-link:parent' token/s, when updating the alias for an entity that has a menu link that is a parent, the aliases for the child menu links are not updated.
The attached patch adds a call at the end of PathautoGenerator::createEntityAlias() for insert or update operations (if alias changed) to update the aliases for any child menu link/s (if any) of the entity's menu link/s (if any). It only runs this update if any saved pathauto pattern contains ':menu-link:parent/s' tokens. Perhaps an admin setting for this might be preferred?
NB this only operates on entity insert/update, not on menu link hierarchy restructuring.
NB tested successfully with pattern '[node:menu-link:parent:url:relative]/[node:title]'. The pattern '[node:menu-link:parents:join-path]/[node:title]' seemed to replace with stale aliases, not sure of the issue.
Issue fork pathauto-3016532
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
Comment #2
bgilhome commentedComment #3
bgilhome commentedComment #4
ducktape commentedThe use case of menu hierarchy changes need to be covered as well.
I don't think the for-each will scale very well, maybe switch to a queue for processing.
Comment #5
mpp commentedHi @bgilhome, thanks for your patch.
I consider this a major bug (or missing feature). Changing the priority.
Like @ducktape mentioned:
Note that the foreach code in #2 will cause performance issues for sites with large menu's (I've seen sites with 50k+ menu items) where a webmaster can move a large subtree in the menu. I would consider using a queue for this: fill it up with all the children when a node's parent menu is moved, then process a configurable amount on cron.
Comment #6
ducktape commentedI rerolled that patch against the latest dev. I have also added support for menu tree restructuring or menu link updates.
I have not added a queue yet, as the processing seems to be fine, even for large trees, however I did not have a 50k+ menu to my disposal to test. @mpp, can you have that tested? :)
Comment #7
fernly commentedThe patch in #6 outputs a fatal error when there is no parsable request url because the exception class in the catch statement is not recognised, namespacewise (Exception => \Exception).
Added an updated patch.
Comment #8
fernly commentedRemoved the txt extension from the patch file.
Comment #9
gremy commentedWe were were running into this problem with one of our clients, and decided to write a custom module to solve this issue. After running into some issues with our custom module, we found this issue and tried pathauto-update_child_aliases-3016532-8.patch instead of our module.
It works great! Thank you!
Comment #10
0livier commentedPatch updated for 8.x-1.5
Comment #11
berdirThis is quite a lot of non-trivial code, I didn't review in depth yet but some test coverage would be very helpful in getting this committed. to ensure that this doesn't get broken in the future.
Comment #12
almunningsGetting some strange behaviour with this patch with pathauto 1.8 & Drupal 8.9.6
Removing patch #10 stops this happening.
On creating a new node:
Select 'Provide a menu link' and save.
Node saves, but the link in the menu gets the url node/[uuid] rather than the alias. Menu seems to save incorrectly.
So not sure whats going on here. Race condition?
Edit. My issue appears to be unrelated to this patch. Issue could be memcached 2.2.
https://www.drupal.org/project/memcache/issues/3176451
Comment #13
0livier commentedFix child recursivity loop
Comment #14
sylvain lavielle commentedThe #13 patch worked for me but I had to do something more to make the whole thing to work in my case.
The problem that I faced was the Pathauto generation for content related to menu item was triggered to early while menu item update and before the menu tree was updated. Hence, the ":menu-link:parent" pathauto token generated an old version of the alias due to a non-updated menu tree structure.
I fixed it in our project by applying a patch to the core in the menu link manager (core/lib/Drupal/Core/Menu/MenuLinkManager.php), updating the
updateDefinitionthis way :I'm aware that is certainly not the good manner. That's only a quick fix in order to make it work. But there is somehow a tricky point there that appears only in case of using
:menu-link:parenttoken/s :Is pathauto have to change the way it works to handle it ?
Is the core have to change something about the way it works while updating menu tree / menu items ?
an what is the best way to do this (or that)
I thing I'm not deeply aware about those subjects to handle this further right now.
I let you - the aware guys :) - react on it and suggest the best solution
Comment #15
sylvain lavielle commentedComment #16
kevineinarsson commentedIn #13, the logic that determines whether or not to update a child looks like this:
($child_entity->id() !== $entity->id && $child_entity->getEntityTypeId() !== $entity->getEntityTypeId())By changing the condition to be an
||instead makes more sense to me. That would mean entities of different types with the same ID would evaluate to true as well as entities of the same type but with different ID:s.Comment #17
kevineinarsson commentedComment #18
fengtanA combination of #16 with #3181070-2: Possible need to change the way MenuLinkManager update menu-item and menu-tree seems to work.
However, given that the latter requires to patch core, we implemented the following workaround instead as suggested by #3068943-2: Menu navigation change does not affect node path:
The paths get updated only on cron (instead of right after updating a node or menu item), but that is acceptable for us.
Comment #19
daniel korteI’m seeing duplicate path aliases created when updating a node by adding it to a menu via the “Menu settings” fieldset on the node edit page. This is because there is an “insert” operation on the
MenuLinkContentInterfaceentity which preventscreateEntityAlias()from setting$existing_aliascausing a duplicate alias to be created.Changing to “Needs work” because tests are still needed and I’m not sure that hardcoding the operation to “update” here is the best solution.
Comment #20
brandon mok commentedIn the patch from #16, I noticed the check that determines if a child needs to be updated was always evaluating to TRUE for this particular condition
$child_entity->id() !== $entity->id. The accessing of the entity's id through$entity->idwas observed to return a NULL value every time despite there being a parent entity; thereby, resulting in a truthy check.Based on the #16 patch, the included patch and interdiff updates the condition so that the accessing of the entity's id is through the
id()method of the EntityInterface:$entity->id().Comment #21
penyaskitoI don't know why #20 ignored the patch at #19 (thought they might be crossposting, but they have more than 1 year between them), but with #20 I also get duplicated path aliases all over the place.
Comment #22
sjpeters79 commentedUpdated patch to work with latest pathauto dev release
Comment #23
mably commentedMR analysis summary
When a node's alias changes (insert or update) and the node is referenced by a menu link, child menu links' target entities are not re-aliased — even when their pathauto pattern includes
:menu-link:parenttokens. This means the child nodes keep stale aliases that reference the old parent path.Changes in
src/PathautoGenerator.php1. Child menu link alias propagation (
createEntityAlias())After saving an alias, if the operation is
insertorupdate(with a changed alias), and at least one pathauto pattern contains:menu-link:parenttokens, the new methodupdateEntityMenuLinkChildAliases()is called.2. New
updateEntityMenuLinkChildAliases()methodMenuLinkManager::loadLinksByRoute().getChildIds().entity.*route), loads the target entity.:menu-link:parenttokens before regenerating its alias.3. New
getPatternsWithMenuTokens()methodQueries and caches all pathauto patterns whose pattern string contains
:menu-link:parent. Used as a fast check to skip the entire menu link traversal when no pattern uses menu tokens.4. Menu link content handling (
updateEntityAlias())When a
MenuLinkContentInterfaceentity is saved, the method resolves its URL to the linked entity via the router and triggersupdateEntityAlias()on that entity, ensuring alias regeneration cascades from menu link changes.5. Cache reset
resetCaches()now also clears$patternsWithMenuTokens.Comment #25
mably commented