From bf9b204344d9abcfa8d74a7c5d3950b8cec3373f Mon Sep 17 00:00:00 2001
From: Alex Pott <1732-alexpott@users.noreply.drupalcode.org>
Date: Sat, 15 Feb 2025 19:31:16 +0000
Subject: [PATCH] Issue #3505827: Use the new Drupal\entity_usage\UrlToEntity
 service instead of \Drupal\entity_usage_updater\Utility\UrlHelper

---
 composer.json                               |  2 +-
 phpstan-baseline.neon                       | 10 -----
 src/Controller/LocalTaskUsageController.php |  2 +-
 src/Form/LinkRemoverForm.php                | 16 +++----
 src/LinkRemoverTrait.php                    | 17 +++++---
 src/Plugin/EntityUsageUpdater/HtmlLink.php  | 21 ++++-----
 src/Plugin/EntityUsageUpdater/LinkIt.php    | 12 +++---
 src/Utility/UrlHelper.php                   | 47 ---------------------
 src/Utility/UrlHelperInner.php              | 43 -------------------
 9 files changed, 39 insertions(+), 131 deletions(-)
 delete mode 100644 src/Utility/UrlHelper.php
 delete mode 100644 src/Utility/UrlHelperInner.php

diff --git a/composer.json b/composer.json
index a66b78d..9da1c71 100644
--- a/composer.json
+++ b/composer.json
@@ -5,7 +5,7 @@
     "require": {
         "php": ">= 7.4",
         "ext-dom": "*",
-        "drupal/entity_usage": "^2"
+        "drupal/entity_usage": "^2.0-beta18"
     },
     "require-dev": {
         "drupal/paragraphs": ">=1.0"
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index a4b0751..418fa2d 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -50,16 +50,6 @@ parameters:
 			count: 2
 			path: src/Plugin/EntityUsageUpdater/LinkIt.php
 
-		-
-			message: "#^PHPDoc tag @var for property Drupal\\\\entity_usage_updater\\\\Utility\\\\UrlHelper\\:\\:\\$inner with type Drupal\\\\entity_usage\\\\EntityUsageTrackBase is not subtype of native type Drupal\\\\entity_usage_updater\\\\Utility\\\\UrlHelperInner\\.$#"
-			count: 1
-			path: src/Utility/UrlHelper.php
-
-		-
-			message: "#^\\\\Drupal calls should be avoided in classes, use dependency injection instead$#"
-			count: 1
-			path: src/Utility/UrlHelper.php
-
 		-
 			message: "#^Call to an undefined method Drupal\\\\Core\\\\Entity\\\\EntityInterface\\:\\:set\\(\\)\\.$#"
 			count: 1
diff --git a/src/Controller/LocalTaskUsageController.php b/src/Controller/LocalTaskUsageController.php
index 6e5ffbd..3f2991c 100644
--- a/src/Controller/LocalTaskUsageController.php
+++ b/src/Controller/LocalTaskUsageController.php
@@ -35,7 +35,7 @@ class LocalTaskUsageController extends BaseLocalTaskUsageController {
   /**
    * {@inheritdoc}
    */
-  public function listUsageLocalTask(RouteMatchInterface $route_match) {
+  public function listUsageLocalTask(RouteMatchInterface $route_match): array {
     $entity = $this->getEntityFromRouteMatch($route_match);
     return $this->listUsagePage($entity->getEntityTypeId(), $entity->id());
   }
diff --git a/src/Form/LinkRemoverForm.php b/src/Form/LinkRemoverForm.php
index 32416c6..7029419 100644
--- a/src/Form/LinkRemoverForm.php
+++ b/src/Form/LinkRemoverForm.php
@@ -15,9 +15,9 @@ use Drupal\Core\Entity\EntityPublishedInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\entity_usage\UrlToEntityInterface;
 use Drupal\entity_usage_updater\EntityUsageUpdater;
 use Drupal\entity_usage_updater\EntityUsageUpdaterInterface;
-use Drupal\entity_usage_updater\Utility\UrlHelper;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -35,9 +35,9 @@ class LinkRemoverForm extends FormBase {
   /**
    * The URL helper.
    *
-   * @var \Drupal\entity_usage_updater\Utility\UrlHelper
+   * @var \Drupal\entity_usage\UrlToEntityInterface
    */
-  protected UrlHelper $urlHelper;
+  protected UrlToEntityInterface $urlToEntity;
 
   /**
    * The entity type manager.
@@ -64,7 +64,7 @@ class LinkRemoverForm extends FormBase {
   public static function create(ContainerInterface $container): static {
     $form = parent::create($container);
     $form->entityUsageUpdater = $container->get('entity_usage_updater.updater');
-    $form->urlHelper = $container->get('class_resolver')->getInstanceFromDefinition(UrlHelper::class);
+    $form->urlToEntity = $container->get(UrlToEntityInterface::class);
     $form->entityTypeManager = $container->get('entity_type.manager');
     if ($container->has('content_moderation.state_transition_validation')) {
       $form->contentModerationValidation = $container->get('content_moderation.state_transition_validation');
@@ -134,15 +134,15 @@ class LinkRemoverForm extends FormBase {
       if (empty($url)) {
         continue;
       }
-      $entity = $this->urlHelper->findEntityByUrlString($url);
+      $entity = $this->urlToEntity->findEntityIdByUrl($url);
       if (!$entity) {
         $urls_not_found[] = $url;
         continue;
       }
-      if (!isset($argument[$entity->getEntityTypeId()])) {
-        $argument[$entity->getEntityTypeId()] = [];
+      if (!isset($argument[$entity['type']])) {
+        $argument[$entity['type']] = [];
       }
-      $argument[$entity->getEntityTypeId()][] = $entity->id();
+      $argument[$entity['type']][] = $entity['id'];
     }
 
     if (!empty($urls_not_found)) {
diff --git a/src/LinkRemoverTrait.php b/src/LinkRemoverTrait.php
index e56d541..153617a 100644
--- a/src/LinkRemoverTrait.php
+++ b/src/LinkRemoverTrait.php
@@ -4,8 +4,9 @@ namespace Drupal\entity_usage_updater;
 
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\TypedData\Type\StringInterface;
-use Drupal\entity_usage_updater\Utility\UrlHelper;
+use Drupal\entity_usage\UrlToEntityInterface;
 
 trait LinkRemoverTrait {
 
@@ -29,10 +30,12 @@ trait LinkRemoverTrait {
    *   The target entity to remove references to.
    * @param \Drupal\Core\TypedData\Type\StringInterface $text
    *   The string item to search within.
-   * @param \Drupal\entity_usage_updater\Utility\UrlHelper $url_helper
-   *   A UrlHelper service object.
+   * @param \Drupal\entity_usage\UrlToEntityInterface $url_to_entity
+   *   A UrlToEntity service object.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
    */
-  protected function removeLinks(EntityInterface $entity, StringInterface $text, UrlHelper $url_helper): void {
+  protected function removeLinks(EntityInterface $entity, StringInterface $text, UrlToEntityInterface $url_to_entity, EntityTypeManagerInterface $entity_type_manager): void {
     if (!trim($text->getValue())) {
       return;
     }
@@ -45,7 +48,11 @@ trait LinkRemoverTrait {
       try {
         // Get the href value of the <a> element.
         $href = $element->getAttribute('href');
-        $url_entity = $url_helper->findEntityByUrlString($href);
+        $url_entity = $url_to_entity->findEntityIdByUrl($href);
+        if ($url_entity === NULL) {
+          continue;
+        }
+        $url_entity = $entity_type_manager->getStorage($url_entity['type'])->load($url_entity['id']);
 
         // If using LinkIt and the uuids match remove the link. Sometimes
         // users hack the url causing the data-entity-uuid not match. If
diff --git a/src/Plugin/EntityUsageUpdater/HtmlLink.php b/src/Plugin/EntityUsageUpdater/HtmlLink.php
index 8523f3c..06425e4 100644
--- a/src/Plugin/EntityUsageUpdater/HtmlLink.php
+++ b/src/Plugin/EntityUsageUpdater/HtmlLink.php
@@ -5,7 +5,7 @@ declare(strict_types=1);
 namespace Drupal\entity_usage_updater\Plugin\EntityUsageUpdater;
 
 use Drupal\Component\Utility\Html;
-use Drupal\Component\Utility\UrlHelper as CoreUrlHelper;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Field\FieldItemInterface;
@@ -17,10 +17,10 @@ use Drupal\Core\Plugin\PluginFormInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\TypedData\Type\StringInterface;
 use Drupal\Core\Url;
+use Drupal\entity_usage\UrlToEntityInterface;
 use Drupal\entity_usage_updater\EntityUsageUpdaterException;
 use Drupal\entity_usage_updater\EntityUsageUpdaterPluginBase;
 use Drupal\entity_usage_updater\LinkRemoverTrait;
-use Drupal\entity_usage_updater\Utility\UrlHelper;
 use Psr\Log\LoggerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -71,9 +71,9 @@ class HtmlLink extends EntityUsageUpdaterPluginBase implements ContainerFactoryP
   /**
    * The URL helper object.
    *
-   * @var \Drupal\entity_usage_updater\Utility\UrlHelper
+   * @var \Drupal\entity_usage\UrlToEntityInterface
    */
-  protected UrlHelper $urlHelper;
+  protected UrlToEntityInterface $urlToEntity;
 
   /**
    * {@inheritdoc}
@@ -84,7 +84,7 @@ class HtmlLink extends EntityUsageUpdaterPluginBase implements ContainerFactoryP
     $plugin->languageManager = $container->get('language_manager', ContainerInterface::NULL_ON_INVALID_REFERENCE);
     $plugin->languageNegotiator = $container->get('language_negotiator', ContainerInterface::NULL_ON_INVALID_REFERENCE);
     $plugin->logger = $container->get('logger.channel.entity_usage_updater');
-    $plugin->urlHelper = $container->get('class_resolver')->getInstanceFromDefinition(UrlHelper::class);
+    $plugin->urlToEntity = $container->get(UrlToEntityInterface::class);
     $plugin->setStringTranslation($container->get('string_translation'));
     return $plugin;
   }
@@ -144,11 +144,11 @@ class HtmlLink extends EntityUsageUpdaterPluginBase implements ContainerFactoryP
   public function remove(EntityInterface $old_target, FieldItemInterface $item): void {
     $value = $item->get('value');
     assert($value instanceof StringInterface);
-    $this->removeLinks($old_target, $value, $this->urlHelper);
+    $this->removeLinks($old_target, $value, $this->urlToEntity, $this->entityTypeManager);
     if (isset($item->summary)) {
       $summary = $item->get('summary');
       assert($summary instanceof StringInterface);
-      $this->removeLinks($old_target, $summary, $this->urlHelper);
+      $this->removeLinks($old_target, $summary, $this->urlToEntity, $this->entityTypeManager);
     }
   }
 
@@ -189,11 +189,11 @@ class HtmlLink extends EntityUsageUpdaterPluginBase implements ContainerFactoryP
     }
     foreach ($hrefs as $language => $language_hrefs) {
       foreach ($language_hrefs as $href) {
-        $parts = CoreUrlHelper::parse($href);
+        $parts = UrlHelper::parse($href);
         $options = [
           'fragment' => $parts['fragment'],
           'query' => $parts['query'],
-          'absolute' => CoreUrlHelper::isExternal($href),
+          'absolute' => UrlHelper::isExternal($href),
         ];
 
         if ($this->configuration['convert_to_linkit'] ?? FALSE) {
@@ -237,8 +237,9 @@ class HtmlLink extends EntityUsageUpdaterPluginBase implements ContainerFactoryP
       try {
         // Get the href value of the <a> element.
         $href = $element->getAttribute('href');
-        $entity = $this->urlHelper->findEntityByUrlString($href);
+        $entity = $this->urlToEntity->findEntityIdByUrl($href);
         if ($entity) {
+          $entity = $this->entityTypeManager->getStorage($entity['type'])->load($entity['id']);
           if ($element->hasAttribute('data-entity-uuid')) {
             // Normally the Linkit plugin handles when a element has this
             // attribute, but sometimes users may change the HREF manually and
diff --git a/src/Plugin/EntityUsageUpdater/LinkIt.php b/src/Plugin/EntityUsageUpdater/LinkIt.php
index 500e9ca..7a4e949 100644
--- a/src/Plugin/EntityUsageUpdater/LinkIt.php
+++ b/src/Plugin/EntityUsageUpdater/LinkIt.php
@@ -9,9 +9,9 @@ use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\TypedData\Type\StringInterface;
+use Drupal\entity_usage\UrlToEntityInterface;
 use Drupal\entity_usage_updater\EntityUsageUpdaterPluginBase;
 use Drupal\entity_usage_updater\LinkRemoverTrait;
-use Drupal\entity_usage_updater\Utility\UrlHelper;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -36,9 +36,9 @@ class LinkIt extends EntityUsageUpdaterPluginBase implements ContainerFactoryPlu
   /**
    * The URL helper object.
    *
-   * @var \Drupal\entity_usage_updater\Utility\UrlHelper
+   * @var \Drupal\entity_usage\UrlToEntityInterface
    */
-  protected UrlHelper $urlHelper;
+  protected UrlToEntityInterface $urlToEntity;
 
   /**
    * {@inheritdoc}
@@ -46,7 +46,7 @@ class LinkIt extends EntityUsageUpdaterPluginBase implements ContainerFactoryPlu
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
     $plugin = new static($configuration, $plugin_id, $plugin_definition);
     $plugin->entityTypeManager = $container->get('entity_type.manager');
-    $plugin->urlHelper = $container->get('class_resolver')->getInstanceFromDefinition(UrlHelper::class);
+    $plugin->urlToEntity = $container->get(UrlToEntityInterface::class);
     return $plugin;
   }
 
@@ -66,11 +66,11 @@ class LinkIt extends EntityUsageUpdaterPluginBase implements ContainerFactoryPlu
   public function remove(EntityInterface $old_target, FieldItemInterface $item): void {
     $value = $item->get('value');
     assert($value instanceof StringInterface);
-    $this->removeLinks($old_target, $value, $this->urlHelper);
+    $this->removeLinks($old_target, $value, $this->urlToEntity, $this->entityTypeManager);
     if (isset($item->summary)) {
       $summary = $item->get('summary');
       assert($summary instanceof StringInterface);
-      $this->removeLinks($old_target, $summary, $this->urlHelper);
+      $this->removeLinks($old_target, $summary, $this->urlToEntity, $this->entityTypeManager);
     }
   }
 
diff --git a/src/Utility/UrlHelper.php b/src/Utility/UrlHelper.php
deleted file mode 100644
index ea94e53..0000000
--- a/src/Utility/UrlHelper.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Drupal\entity_usage_updater\Utility;
-
-use Drupal\Core\Entity\EntityInterface;
-
-/**
- * Provides a utility class for analyzing URLs.
- */
-final class UrlHelper {
-
-  /**
-   * The inner helper object.
-   *
-   * @var UrlHelperInner|\Drupal\entity_usage\EntityUsageTrackBase
-   */
-  protected UrlHelperInner $inner;
-
-  /**
-   * Creates the helper object.
-   */
-  public function __construct() {
-    $this->inner = UrlHelperInner::create(
-      \Drupal::getContainer(),
-      [],
-      'url_helper (not real plugin)',
-      []
-    );
-  }
-
-  /**
-   * Try to retrieve an entity from an URL string.
-   *
-   * @param string $url
-   *   A relative or absolute URL string.
-   *
-   * @return \Drupal\Core\Entity\EntityInterface|null
-   *   The entity object that corresponds to the received URL, or NULL if no
-   *   entity could be retrieved.
-   */
-  public function findEntityByUrlString(string $url): ?EntityInterface {
-    return $this->inner->findEntityByUrlString($url);
-  }
-
-}
diff --git a/src/Utility/UrlHelperInner.php b/src/Utility/UrlHelperInner.php
deleted file mode 100644
index da6c3eb..0000000
--- a/src/Utility/UrlHelperInner.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Drupal\entity_usage_updater\Utility;
-
-use Drupal\Core\Field\FieldItemInterface;
-use Drupal\entity_usage\EntityUsageTrackBase;
-
-/**
- * Exposes Entity Usage logic for extracting entity details from a URL string.
- *
- * This is an ugly approach in that it extends EntityUsageTrackBase while it
- * isn't an entity usage tracker. It should only be usd via UrlHelper which
- * hides irrelevant public methods. Ideally this functionality could be
- * factored out from the base class within Entity Usage itself.
- *
- * @internal
- */
-final class UrlHelperInner extends EntityUsageTrackBase {
-
-  /**
-   * This shouldn't be used.
-   */
-  public function getTargetEntities(FieldItemInterface $item) {
-    throw new \LogicException("This isn't a real tracker.");
-  }
-
-  /**
-   * Try to retrieve an entity from an URL string.
-   *
-   * @param string $url
-   *   A relative or absolute URL string.
-   *
-   * @return \Drupal\Core\Entity\EntityInterface|null
-   *   The entity object that corresponds to the received URL, or NULL if no
-   *   entity could be retrieved.
-   */
-  public function findEntityByUrlString($url) {
-    return parent::findEntityByUrlString($url);
-  }
-
-}
-- 
GitLab