Problem/Motivation

I'm using a bundle class to override the toUrl() method on a particular node type. If some condition is met (the specifics are not important) I don't want the node to be visible on its own page and I don't want teasers and cards to render any links to the node

  public function toUrl($rel = 'canonical', array $options = []) {
    if ($rel === 'canonical') {
      if (//some condition) {
        return Url::fromRoute('<nolink>');
      }
    }
    return parent::toUrl($rel, $options);
  }

Let's assume I have created a node that meets the condition to have <nolink> as its canonical url. If I delete that node, every single url alias gets deleted.

That's because AliasStorageHelperInterface::deleteBySourcePrefix() gets called with / as the prefiz, which of course matches all aliases.

Proposed resolution

Add some check somewhere to prevent accidentally deleting all url aliases if a canonical url is <nolink>. Not sure if that would be best in AliasStorageHelper or pathauto.module.

One way to prevent this mass deletion from happening is to override the hasLinkTemplate() method in the bundle class such that it returns false if the node has a canonical url.

  /**
   * {@inheritdoc}
   */
  public function hasLinkTemplate($rel) {
    if ($rel === 'canonical') {
      if (//some condition) {
        return FALSE;
      }
    }
    return parent::hasLinkTemplate($rel);
  }

Remaining tasks

User interface changes

API changes

Data model changes

Issue fork pathauto-3367067

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

danflanagan8 created an issue. See original summary.

bvoynick’s picture

Thank you for the report & suggestion to override hasLinkTemplate, it's coming in handy for me today.

mably’s picture

Here is the deleteBySourcePrefix code from AliasStorageHelper:

public function deleteBySourcePrefix($source) {
  $pids = $this->loadBySourcePrefix($source);
  if ($pids) {
    $this->deleteMultiple($pids);
  }
}

May be we could simply check there that $source is not equal to /.

But the hasLinkTemplate() solution is probably the least impactful for now.

mably’s picture

Status: Active » Needs review

mably’s picture

Problem

When an entity with a <nolink> canonical URL (e.g. certain menu link content entities) was deleted, all path aliases on the site were wiped out.

In AliasStorageHelper::deleteEntityPathAll(), calling getInternalPath() on a <nolink> URL returned an empty string. This caused
deleteBySourcePrefix('/') to be called, which matched every alias in the database.

Fix

Two guard conditions were added before any deletion occurs:

  1. $url->isRouted() — skip unrouted URLs entirely
  2. $internal_path !== '' — never call deleteBySourcePrefix('/') with a bare slash

Test coverage

A kernel test was added (testDeleteEntityPathAllWithNoLinkEntity) that:

  1. Creates two nodes with aliases
  2. Mocks an entity with a <nolink> canonical URL
  3. Calls deleteEntityPathAll() on it
  4. Asserts all existing aliases are still intact

The test was verified to fail without the fix and pass with it.

berdir’s picture

Status: Needs review » Needs work

A minor suggestion, then fine to merge.

  • mably committed 67986e8d on 8.x-1.x
    fix: #3367067 All aliases are deleted if entity with <nolink> url is...
mably’s picture

Status: Needs work » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.