Problem/Motivation

I noticed that during updating a node, pathauto checks for usable patterns before the node is updated in the database. That means if it ever compares entity field values, it doesn't use the updated ones.

Steps to reproduce

How I discovered that:
PathautoWidget calls $pattern = \Drupal::service('pathauto.generator')->getPatternByEntity($entity); and the usable patterns are saved to $patterns property in PathautoGenerator. But the $entity parameter here doesn't contain the new field values, yet.

Later, in pathauto_entity_update hook, the \Drupal::service('pathauto.generator') is called again and there is no check for possible patterns, the "cached" $patterns property value is used. Shouldn't the module check for patterns only now that the new field values are available?

There's one case I encountered where this causes problems: with manually programmed custom conditions added to a pattern. Conditions like checking if a field has a specific value.
For example:
- Pattern1 is used for article node & when field_category = Books
- Pattern2 is used for article node & when field_category = E-Books

What happens in the example is:
- when updating article node1 I change the field_category to E-Books from Books
- after I click save, the alias is not updated, it still uses Pattern1 (because when the getPatternByEntity function checked patterns, it didn't use the newly saved node, and field_category was still Books)
- if I open node1 and click save again without changing anything, the alias is updated using Pattern2 (because when the getPatternByEntity function checked patterns, field_category was E-Books before and after saving the node)

Proposed resolution

Reset the cached pattern for the entity before updating its alias.

Remaining tasks

Cover this case with tests?

User interface changes

No.

API changes

No.

Data model changes

No.

Issue fork pathauto-3187430

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

kaszarobert created an issue. See original summary.

Dmitriy.trt made their first commit to this issue’s fork.

dmitriy.trt’s picture

Issue summary: View changes
Status: Active » Needs review

Created MR with a fix.

berdir’s picture

Makes sense, but the static cache is pretty pointless then at that point, no?

bkosborne’s picture

Makes sense, but the static cache is pretty pointless then at that point, no?

Not entirely.->updateEntityAlias calls ->createEntityAlias() which checks the static cache again, which is now re-hydrated. Also ->getPatternByEntity() is public and used in a few areas that benefit from this cache. It's worth keeping the static cache around, it just needs to be cleared here because the form widget hydrates the cache initially before the entities data is fully available.

This seems straightforward to me and would help resolve issues surfaced in #3224916: Provide UI for pattern conditions. That issue makes it much easier to add additional conditions to a pattern, which opens the door for making conditions based on entity data.

bkosborne’s picture

Status: Needs review » Reviewed & tested by the community

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

mably’s picture

Assigned: Unassigned » berdir

Summary

Added a resetEntityPatternCache() method to PathautoGenerator and a call to it at the top of updateEntityAlias(), so that the per-entity cached pattern is cleared before getPatternByEntity() re-evaluates which pattern applies. This prevents stale cached patterns from being reused when an entity's values have changed in a way that would match a different pattern.

Changes in src/PathautoGenerator.php:

  • New resetEntityPatternCache(EntityInterface $entity) protected method — unsets $this->patterns[$entityTypeId][$entityId][$langcode].
  • updateEntityAlias() now calls resetEntityPatternCache() before getPatternByEntity(), ensuring conditions are re-evaluated against current entity values.

Test coverage in tests/src/Kernel/PathautoKernelTest.php:

  • New testEntityPatternCacheResetOnUpdate() kernel test.
  • Creates a bundle-specific pattern /page/[node:title] (weight -1) alongside the generic /content/[node:title] (weight 10).
  • Creates a page node -> alias is /page/test-node, page pattern cached.
  • Injects a stale cache entry via reflection (replaces the cached page pattern with the generic one).
  • Calls updateEntityAlias() and asserts the alias remains /page/test-node — proving the stale cache was cleared and the correct bundle-specific pattern was re-evaluated.
  • Verified the test fails without the fix (alias becomes /content/test-node).