Drupal 8 delete duplicate url alias

I have been working on project and one of the product owners want to modify an url alias, and she notice this:

Image
Duplicated url alias

yes i know its bad, a lot of duplicate unused url alias !

After digging into this to figure out where this came from, i found the following line inside hook_entity_presave, and it's was there for a reason we needed to insert another alias in each entity update:

\Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'insert');

By the way this line of code it’s equivalent to the following Pathauto configuration:

Image
Pathauto conf screenshot

So if you have one of those two things, after a while you will found hundreds of unused duplicated url alias:

 How to delete / Remove duplicate url alias?

To achieve this we use hook_update_N in MODULENAME.install 

/**
 * Remove duplicate alias, for articles.
 */
function MODULENAME_update_8801() {
  $query = \Drupal::entityQuery('node');
  $query->condition('type', 'article', '=');
  $nids = $query->execute();
  $path_alias_storage = \Drupal::entityTypeManager()->getStorage('path_alias');

  foreach ($nids as $nid) {
    $actual_alias = \Drupal::service('path.alias_manager')
      ->getAliasByPath('/node/' . $nid);

    // Load all path alias for this node.
    $alias_objects = $path_alias_storage->loadByProperties([
      'path' => '/node/' . $nid,
    ]);

    // Delete all other alias than the actual one.
    foreach ($alias_objects as $alias_object) {
      if ($alias_object->get('alias')->value !== $actual_alias) {
        $alias_object->delete();
      }
    }
    // Load all new path alias for this node.
    $new_alias_objects = $path_alias_storage->loadByProperties([
      'path' => '/node/' . $nid,
    ]);
    // Delete duplicate aliases.
    if (count($new_alias_objects) > 1) {
      array_shift($new_alias_objects);
      foreach ($new_alias_objects as $alias_object) {
        $alias_object->delete();
      }
    }
  }
}

Let’s break this down:

  • The first foreach 
        foreach ($alias_objects as $alias_object) {
          if ($alias_object->get('alias')->value !== $actual_alias) {
            $alias_object->delete();
          }
        }

    To remove any alias with the same path system and different alias.

  • The second foreach 
        if (count($new_alias_objects) > 1) {
          array_shift($new_alias_objects);
          foreach ($new_alias_objects as $alias_object) {
            $alias_object->delete();
          }
        }

    To let only one alias, because something you can find more than one url alias with the same alias and path system.

Now just run update database either by going to /update.php or using drush:

drush updb -y

and you are done.