Summary
This patch fixes two related issues in the Pathauto module:
- Duplicate alias creation: Prevents duplicate aliases from being created when
hook_entity_updateruns after translation creation - Missing translation aliases: Ensures aliases are generated for all entity translations (e.g., Dutch and English) using the correct language context
Problem Description
Issue 1: Duplicate Aliases
When a translatable entity (e.g., a node) is created with a Dutch translation, and an English translation is automatically added later:
hook_entity_insertruns -> creates Dutch alias- English translation is automatically created
hook_entity_updateruns -> attempts to create aliases again- Result: Duplicate Dutch aliases are created (2 Dutch aliases, 0 English aliases)
Issue 2: Missing Translation Aliases
The original code only processed the entity's current language, not all available translations. This meant:
- If a node was created in Dutch, only the Dutch alias was generated
- English translations added later would not get aliases automatically
- Each translation needs to be processed in its own language context to generate correct aliases
Solution
Changes Made to PathautoGenerator.php
1. Added TranslatableInterface Import
use Drupal\Core\Entity\TranslatableInterface;
This allows the code to check if an entity is translatable and access all its translations.
2. Enhanced Duplicate Prevention in createEntityAlias()
Change 1: Extended duplicate check to include 'insert' operation
// Before: Only checked for 'update' or 'bulkupdate'
if ($op == 'update' || $op == 'bulkupdate') {
// After: Also checks during 'insert'
if ($op == 'update' || $op == 'bulkupdate' || $op == 'insert') {
Change 2: Added additional duplicate check before alias creation
// Additional check: If an alias already exists for this source and language
// with the same alias value, skip creating a duplicate. This prevents
// duplicate aliases when hook_entity_update runs after
// hook_entity_translation_create.
if (!$existing_alias && $op == 'update') {
$check_existing = $this->aliasStorageHelper->loadBySource($source, $langcode);
if ($check_existing && isset($check_existing['alias']) && $check_existing['alias'] === $alias) {
return NULL; // Skip duplicate creation
}
}
3. Updated updateEntityAlias() to Process All Translations
The method now:
- Checks if the entity is translatable
- Loops through all translation languages
- Processes each translation in its own language context
- Generates aliases for each translation separately
// Process all translations to ensure aliases are generated for each language.
if ($entity instanceof TranslatableInterface && $entity->isTranslatable()) {
$translation_languages = $entity->getTranslationLanguages();
foreach ($translation_languages as $langcode => $language) {
$translation = $entity->getTranslation($langcode);
// Check if pathauto is enabled for this translation
if ($translation->path->pathauto == PathautoState::CREATE || !empty($options['force'])) {
$translation_options = $options;
$translation_options['language'] = $langcode;
$translation_result = $this->createEntityAlias($translation, $op);
// ...
}
}
}
else {
// For non-translatable entities, process as before
$options += ['language' => $entity->language()->getId()];
$result = $this->createEntityAlias($entity, $op);
}
How It Works
Scenario: Creating a Dutch Node with Auto-Generated English Translation
Step 1: Node Creation (hook_entity_insert)
- Dutch node is created
updateEntityAlias()is called- Code detects entity is translatable
- Loops through all translations (Dutch, English)
- For each translation:
- Sets correct language context (
$translation_options['language'] = $langcode) - Calls
createEntityAlias()with the translation - Generates alias using translation's language context
- Sets correct language context (
- Result:
- Dutch alias:
/nl/node-title(generated using Dutch language context) - English alias:
/en/node-title(generated using English language context)
- Dutch alias:
Step 2: Entity Update (hook_entity_update)
- Entity is updated (e.g., after translation modifications)
updateEntityAlias()is called again- Processes all translations again
- Duplicate check prevents creating duplicates:
- Checks if alias already exists for source + language
- If alias with same value exists, skips creation
- Result:
- No duplicates created
- Only creates aliases for translations that don't have one yet
Results
- Prevents duplicate aliases: No more duplicate Dutch aliases when
hook_entity_updateruns - Generates aliases for all translations: Both Dutch and English translations get their aliases
- Uses correct language context: Each translation is processed in its own language context, ensuring:
- Correct token replacements (e.g., language-specific titles)
- Proper language prefixes in URLs
- Language-appropriate alias patterns
- No regressions: Non-translatable entities continue to work exactly as before
Technical Details
Files Modified
docroot/modules/contrib/pathauto/src/PathautoGenerator.php
Dependencies
- Drupal Core (for
TranslatableInterface)
Testing Recommendations
- Test duplicate prevention:
- Create a translatable node in Dutch
- Verify only one Dutch alias is created
- Trigger entity update
- Verify no duplicate aliases are created
- Test translation alias generation:
- Create a translatable node in Dutch
- Verify both Dutch and English aliases are created
- Check that each alias uses the correct language context
- Test non-translatable entities:
- Create a non-translatable entity
- Verify it works as before (no regressions)
- Test with different update actions:
- Test with different Pathauto update action settings
- Verify behavior is correct for each setting
Related Issues
This fix addresses:
- Duplicate alias creation when translations are automatically added
- Missing aliases for entity translations
- Incorrect language context when generating aliases for translations
| Comment | File | Size | Author |
|---|---|---|---|
| #2 | issue-3566296-duplicate-alias.patch | 4.77 KB | ankitjhakal |
Issue fork pathauto-3566296
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
ankitjhakal commentedComment #3
ankitjhakal commentedComment #4
mably commentedPlease provide a MR with some tests.
We also need a full reproducible scenario from a fresh Drupal instance.
In the absence of new information within the coming weeks, this ticket will be closed.
And please avoid markdown in your issue summary, it's quite unreadable, use HTML.
Comment #5
mably commentedComment #6
mably commentedReplaced markdown with HTML.
Comment #8
mably commentedThis related issue will probably fix "Issue 2" described in IS.
Comment #9
mably commentedThis related issue will probably fix "Issue 1" described in IS.
I haven't been able to reproduce "Issue 1" locally.