Summary

This patch fixes two related issues in the Pathauto module:

  1. Duplicate alias creation: Prevents duplicate aliases from being created when hook_entity_update runs after translation creation
  2. 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:

  1. hook_entity_insert runs -> creates Dutch alias
  2. English translation is automatically created
  3. hook_entity_update runs -> attempts to create aliases again
  4. 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)

  1. Dutch node is created
  2. updateEntityAlias() is called
  3. Code detects entity is translatable
  4. Loops through all translations (Dutch, English)
  5. For each translation:
    • Sets correct language context ($translation_options['language'] = $langcode)
    • Calls createEntityAlias() with the translation
    • Generates alias using translation's language context
  6. Result:
    • Dutch alias: /nl/node-title (generated using Dutch language context)
    • English alias: /en/node-title (generated using English language context)

Step 2: Entity Update (hook_entity_update)

  1. Entity is updated (e.g., after translation modifications)
  2. updateEntityAlias() is called again
  3. Processes all translations again
  4. Duplicate check prevents creating duplicates:
    • Checks if alias already exists for source + language
    • If alias with same value exists, skips creation
  5. 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_update runs
  • 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

  1. 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
  2. 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
  3. Test non-translatable entities:
    • Create a non-translatable entity
    • Verify it works as before (no regressions)
  4. 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
CommentFileSizeAuthor
#2 issue-3566296-duplicate-alias.patch4.77 KBankitjhakal

Issue fork pathauto-3566296

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

ankitjhakal created an issue. See original summary.

ankitjhakal’s picture

StatusFileSize
new4.77 KB
ankitjhakal’s picture

Assigned: ankitjhakal » Unassigned
Status: Active » Needs review
mably’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests, +Needs steps to reproduce

Please 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.

mably’s picture

Status: Needs work » Postponed (maintainer needs more info)
mably’s picture

Issue summary: View changes

Replaced markdown with HTML.

mably’s picture

This related issue will probably fix "Issue 2" described in IS.

mably’s picture

This related issue will probably fix "Issue 1" described in IS.

I haven't been able to reproduce "Issue 1" locally.