From 9231c9cf62c101117e17a779d37348d1f5f59bdf Mon Sep 17 00:00:00 2001
From: catch <catch@35733.no-reply.drupal.org>
Date: Mon, 12 Feb 2024 16:27:33 +0000
Subject: [PATCH] Issue #3409895 by acbramley, longwave, smustgrave:
 [regression] toUrl can incorrectly return edit-form url when another link
 template shares the canonical url

---
 core/lib/Drupal/Core/Entity/EntityBase.php    | 13 ++++----
 .../Tests/Core/Entity/EntityUrlTest.php       | 32 ++++++++++++++++---
 2 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/core/lib/Drupal/Core/Entity/EntityBase.php b/core/lib/Drupal/Core/Entity/EntityBase.php
index 79e61b3e14a4..b05468cdca87 100644
--- a/core/lib/Drupal/Core/Entity/EntityBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityBase.php
@@ -167,12 +167,13 @@ public function toUrl($rel = NULL, array $options = []) {
     // Use the canonical link template by default, or edit-form if there is not
     // a canonical one.
     if ($rel === NULL) {
-      $rel_candidates = array_intersect(
-        ['canonical', 'edit-form'],
-        array_flip($link_templates),
-      );
-      $rel = array_shift($rel_candidates);
-      if ($rel === NULL) {
+      if (isset($link_templates['canonical'])) {
+        $rel = 'canonical';
+      }
+      elseif (isset($link_templates['edit-form'])) {
+        $rel = 'edit-form';
+      }
+      else {
         throw new UndefinedLinkTemplateException("Cannot generate default URL because no link template 'canonical' or 'edit-form' was found for the '{$this->getEntityTypeId()}' entity type");
       }
     }
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php
index 2fc1f84b3f45..8e1bf3b2efaa 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php
@@ -110,27 +110,51 @@ public function testToUrlNoId() {
   }
 
   /**
-   * Tests the toUrl() method without specifying the $rel parameter..
+   * Tests the toUrl() method without specifying the $rel parameter.
+   *
+   * It should throw an exception when neither canonical and edit-form link
+   * templates exist if no parameters are passed in.
    *
    * @covers ::toUrl
    */
-  public function testToUrlDefault() {
+  public function testToUrlDefaultException(): void {
     $values = ['id' => $this->entityId];
     $entity = $this->getEntity(UrlTestEntity::class, $values);
 
     $this->expectException(UndefinedLinkTemplateException::class);
     $this->expectExceptionMessage("Cannot generate default URL because no link template 'canonical' or 'edit-form' was found for the '" . $this->entityTypeId . "' entity type");
     $entity->toUrl();
+  }
 
+  /**
+   * Tests the toUrl() method without specifying the $rel parameter.
+   *
+   * It should return the edit-form or canonical link templates by default if
+   * they are registered.
+   *
+   * @covers ::toUrl
+   */
+  public function testToUrlDefaultFallback(): void {
+    $values = ['id' => $this->entityId, 'langcode' => $this->langcode];
+    $entity = $this->getEntity(UrlTestEntity::class, $values);
     $this->registerLinkTemplate('edit-form');
     /** @var \Drupal\Core\Url $url */
     $url = $entity->toUrl();
-    $this->assertUrl('entity.test_entity.edit_form', ['test_entity' => $this->entityId], $entity, FALSE, $url);
+    $this->assertUrl('entity.test_entity.edit_form', ['test_entity' => $this->entityId], $entity, TRUE, $url);
 
     $this->registerLinkTemplate('canonical');
     /** @var \Drupal\Core\Url $url */
     $url = $entity->toUrl();
-    $this->assertUrl('entity.test_entity.canonical', ['test_entity' => $this->entityId], $entity, FALSE, $url);
+    $this->assertUrl('entity.test_entity.canonical', ['test_entity' => $this->entityId], $entity, TRUE, $url);
+
+    // Register multiple link templates with 2 that share the same path.
+    $this->entityType->getLinkTemplates()->willReturn([
+      'canonical' => "/test-entity/{test_entity}/canonical",
+      'edit-form' => "/test-entity/{test_entity}/edit-form",
+      'foobar' => "/test-entity/{test_entity}/canonical",
+    ]);
+    $url = $entity->toUrl();
+    $this->assertUrl('entity.test_entity.canonical', ['test_entity' => $this->entityId], $entity, TRUE, $url);
   }
 
   /**
-- 
GitLab