From 6e798368453528963fc0420d9c3f57d87387e8fe Mon Sep 17 00:00:00 2001
From: Chris Green <chrisgreen@arizona.edu>
Date: Mon, 26 May 2025 14:10:36 -0700
Subject: [PATCH 1/5] Issue #3526657 Extract switcher link logic from
 ToolbarHandler into new SwitcherManager service

---
 environment_indicator.module       |  26 +------
 environment_indicator.services.yml |   6 ++
 src/Service/SwitcherManager.php    | 109 +++++++++++++++++++++++++++++
 src/ToolbarHandler.php             |  67 ++++--------------
 4 files changed, 130 insertions(+), 78 deletions(-)
 create mode 100644 src/Service/SwitcherManager.php

diff --git a/environment_indicator.module b/environment_indicator.module
index fffb6058..1e60da92 100644
--- a/environment_indicator.module
+++ b/environment_indicator.module
@@ -126,7 +126,7 @@ function environment_indicator_page_top(array &$page_top) {
   }
   // Only add the environment indicator switcher if there are environments to
   // switch to.
-  if ($items = _environment_indicator_switcher_links()) {
+  if ($items = \Drupal::service('environment_indicator.switcher_manager')->getLinks()) {
     uasort($items, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
     $page_top['indicator']['switcher'] = [
       '#theme' => 'links',
@@ -143,7 +143,7 @@ function environment_indicator_page_top(array &$page_top) {
     ];
     $page_top['indicator'] += [
       '#cache' => [
-        'tags' => Cache::mergeTags(['config:environment_indicator.settings'], _environment_indicator_switcher_cache_tags()),
+        'tags' => Cache::mergeTags(['config:environment_indicator.settings'], \Drupal::service('environment_indicator.switcher_manager')->getCacheTags()),
       ],
     ];
   }
@@ -225,17 +225,6 @@ function environment_indicator_toolbar() {
     ->toolbar();
 }
 
-/**
- * Helper function that generates the environment switcher links.
- *
- * @return array
- *   A renderable array with the links.
- */
-function _environment_indicator_switcher_links(): array {
-  return \Drupal::service('environment_indicator.toolbar_handler')
-    ->getLinks();
-}
-
 /**
  * Helper function that checks if there is external integration.
  *
@@ -250,17 +239,6 @@ function _environment_indicator_external_integration_is_enabled(string $integrat
     ->externalIntegration($integration);
 }
 
-/**
- * Get the cache tags for the environment indicator switcher.
- *
- * @return string[]
- *   The cache tags.
- */
-function _environment_indicator_switcher_cache_tags(): array {
-  return \Drupal::service('environment_indicator.toolbar_handler')
-    ->getCacheTags();
-}
-
 /**
  * Loads an environment indicator in a procedural way.
  *
diff --git a/environment_indicator.services.yml b/environment_indicator.services.yml
index 6115b450..7344a74c 100644
--- a/environment_indicator.services.yml
+++ b/environment_indicator.services.yml
@@ -8,3 +8,9 @@ services:
       - '@state'
       - '@settings'
       - '@entity_type.manager'
+      - '@environment_indicator.switcher_manager'
+  environment_indicator.switcher_manager:
+    class: Drupal\environment_indicator\Service\SwitcherManager
+    arguments:
+      - '@entity_type.manager'
+      - '@current_route_match'
diff --git a/src/Service/SwitcherManager.php b/src/Service/SwitcherManager.php
new file mode 100644
index 00000000..119199fc
--- /dev/null
+++ b/src/Service/SwitcherManager.php
@@ -0,0 +1,109 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\environment_indicator\Service;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Routing\CurrentRouteMatch;
+use Drupal\Core\Url;
+
+/**
+ * Manages active environment switcher entities and builds UI-ready link data.
+ *
+ * This service centralizes logic related to the environment switcher list,
+ * including filtering active switchers and formatting their metadata as
+ * render-ready link arrays. It also provides cache tags to support proper
+ * cache invalidation when switcher configuration changes.
+ *
+ * Responsibilities:
+ * - Load and filter switchers based on status (and eventually permissions).
+ * - Build renderable links for UI components (e.g., page top, toolbar).
+ * - Provide cache tags related to switcher listings.
+ *
+ * Future enhancements may include:
+ * - Per-switcher access checks (once fixed in the module).
+ * - Domain/path/language-aware filtering.
+ * - Grouping, prioritization, or transformations.
+ *
+ * This service intentionally keeps responsibilities simple and centralized.
+ * If needed, future refactors may extract access filtering, link rendering,
+ * or entity loading into dedicated services or helpers.
+ */
+class SwitcherManager {
+
+  /**
+   * The entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected EntityTypeManagerInterface $entityTypeManager;
+
+  /**
+   * The current route match service.
+   *
+   * @var \Drupal\Core\Routing\CurrentRouteMatch
+   */
+  protected CurrentRouteMatch $routeMatch;
+
+  /**
+   * Constructs a new SwitcherManager instance.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
+   *   The entity type manager service.
+   * @param \Drupal\Core\Routing\CurrentRouteMatch $routeMatch
+   *   The current route match service.
+   */
+  public function __construct(EntityTypeManagerInterface $entityTypeManager, CurrentRouteMatch $routeMatch) {
+    $this->entityTypeManager = $entityTypeManager;
+    $this->routeMatch = $routeMatch;
+  }
+
+  /**
+   * Builds an array of environment switcher links.
+   *
+   * @return array[]
+   *   A render array of link definitions for each active environment.
+   */
+  public function getLinks(): array {
+    /** @var \Drupal\environment_indicator\Entity\EnvironmentIndicator[] $entities */
+    $entities = $this->entityTypeManager->getStorage('environment_indicator')->loadMultiple();
+
+    $current_path = Url::fromRoute('<current>')->toString();
+
+    $links = [];
+    foreach ($entities as $entity) {
+      if (!$entity->status() || empty($entity->getUrl())) {
+        continue;
+      }
+
+      $links[] = [
+        'attributes' => [
+          'style' => sprintf('color: %s; background-color: %s;', $entity->getFgColor(), $entity->getBgColor()),
+          'title' => t('Opens the current page in the selected environment.'),
+        ],
+        'title' => t('Open on @label', ['@label' => $entity->label()]),
+        'url' => Url::fromUri($entity->getUrl() . $current_path),
+        'type' => 'link',
+        'weight' => $entity->getWeight(),
+      ];
+    }
+
+    uasort($links, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
+
+    return $links;
+  }
+
+  /**
+   * Returns cache tags related to the switcher list.
+   *
+   * @return string[]
+   *   An array of cache tags.
+   */
+  public function getCacheTags(): array {
+    return $this->entityTypeManager
+      ->getDefinition('environment_indicator')
+      ->getListCacheTags();
+  }
+
+}
diff --git a/src/ToolbarHandler.php b/src/ToolbarHandler.php
index 10b1b8ce..29bb9dbf 100644
--- a/src/ToolbarHandler.php
+++ b/src/ToolbarHandler.php
@@ -12,7 +12,7 @@ use Drupal\Core\Site\Settings;
 use Drupal\Core\State\StateInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\Url;
-use Drupal\environment_indicator\Entity\EnvironmentIndicator;
+use Drupal\environment_indicator\Service\SwitcherManager;
 
 /**
  * Toolbar integration handler.
@@ -70,6 +70,13 @@ class ToolbarHandler {
    */
   protected EntityTypeManagerInterface $entityTypeManager;
 
+  /**
+   * The SwitcherManager service.
+   *
+   * @var \Drupal\environment_indicator\Service\SwitcherManager
+   */
+  protected SwitcherManager $switcherManager;
+
   /**
    * ToolbarHandler constructor.
    *
@@ -85,6 +92,8 @@ class ToolbarHandler {
    *   The settings.
    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
    *   The entity type manager.
+   * @param \Drupal\environment_indicator\Service\Service\SwitcherManager $switcher_manager
+   *   The switcher manager service.
    */
   public function __construct(
     ModuleHandlerInterface $module_handler,
@@ -93,6 +102,7 @@ class ToolbarHandler {
     StateInterface $state,
     Settings $settings,
     EntityTypeManagerInterface $entity_type_manager,
+    SwitcherManager $switcher_manager,
   ) {
     $this->moduleHandler = $module_handler;
     $this->config = $config_factory->get('environment_indicator.settings');
@@ -101,6 +111,7 @@ class ToolbarHandler {
     $this->state = $state;
     $this->settings = $settings;
     $this->entityTypeManager = $entity_type_manager;
+    $this->switcherManager = $switcher_manager;
   }
 
   /**
@@ -206,7 +217,7 @@ class ToolbarHandler {
         ];
       }
 
-      if ($links = $this->getLinks()) {
+      if ($links = $this->switcherManager->getLinks()) {
         $items['environment_indicator']['tray']['environment_links'] = [
           '#theme' => 'links__toolbar_shortcuts',
           '#links' => $links,
@@ -316,56 +327,4 @@ class ToolbarHandler {
     return $this->entityTypeManager->getDefinition('environment_indicator')->getListCacheTags();
   }
 
-  /**
-   * Get all the links for the switcher.
-   *
-   * @return array
-   *   Returns all the links.
-   */
-  public function getLinks(): array {
-    if (!$environment_entities = EnvironmentIndicator::loadMultiple()) {
-      return [];
-    }
-
-    $current = Url::fromRoute('<current>');
-    $current_path = $current->toString();
-    $url = parse_url($current_path);
-    $path = $url['path'];
-    if (isset($url['query'])) {
-      $path .= '?' . $url['query'];
-    }
-    $environment_entities = array_filter(
-      $environment_entities,
-      function (EnvironmentIndicator $entity) {
-        return $entity->status()
-          && !empty($entity->getUrl())
-          && $this->hasAccessEnvironment($entity->id());
-      }
-    );
-
-    $links = array_map(
-      function (EnvironmentIndicator $entity) use ($path) {
-        return [
-          'attributes' => [
-            'style' => 'color: ' . $entity->getFgColor() . '; background-color: ' . $entity->getBgColor() . ';',
-            'title' => $this->t('Opens the current page in the selected environment.'),
-          ],
-          'title' => $this->t('Open on @label', ['@label' => $entity->label()]),
-          'url' => Url::fromUri($entity->getUrl() . $path),
-          'type' => 'link',
-          'weight' => $entity->getWeight(),
-        ];
-      },
-      $environment_entities
-    );
-
-    if (!$links) {
-      return [];
-    }
-
-    uasort($links, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
-
-    return $links;
-  }
-
 }
-- 
GitLab


From d9d98cc4bbf1e8d59e75dc4ba47cc01361650735 Mon Sep 17 00:00:00 2001
From: Arun Sahijpal <69732-arunsahijpal@users.noreply.drupalcode.org>
Date: Wed, 7 May 2025 22:33:41 +0000
Subject: [PATCH 2/5] Updates

---
 environment_indicator.module | 1 -
 1 file changed, 1 deletion(-)

diff --git a/environment_indicator.module b/environment_indicator.module
index 1e60da92..f1f8bca2 100644
--- a/environment_indicator.module
+++ b/environment_indicator.module
@@ -127,7 +127,6 @@ function environment_indicator_page_top(array &$page_top) {
   // Only add the environment indicator switcher if there are environments to
   // switch to.
   if ($items = \Drupal::service('environment_indicator.switcher_manager')->getLinks()) {
-    uasort($items, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
     $page_top['indicator']['switcher'] = [
       '#theme' => 'links',
       '#links' => $items,
-- 
GitLab


From 6bfa7790f5a487ba6442353a8d662174095896b1 Mon Sep 17 00:00:00 2001
From: Chris Green <chrisgreen@arizona.edu>
Date: Tue, 27 May 2025 17:17:59 -0700
Subject: [PATCH 3/5] Add back getLinks

---
 src/ToolbarHandler.php | 52 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/src/ToolbarHandler.php b/src/ToolbarHandler.php
index 29bb9dbf..286f16d2 100644
--- a/src/ToolbarHandler.php
+++ b/src/ToolbarHandler.php
@@ -317,6 +317,58 @@ class ToolbarHandler {
     return FALSE;
   }
 
+  /**
+   * Get all the links for the switcher.
+   *
+   * @return array
+   *   Returns all the links.
+   */
+  public function getLinks(): array {
+    if (!$environment_entities = EnvironmentIndicator::loadMultiple()) {
+      return [];
+    }
+
+    $current = Url::fromRoute('<current>');
+    $current_path = $current->toString();
+    $url = parse_url($current_path);
+    $path = $url['path'];
+    if (isset($url['query'])) {
+      $path .= '?' . $url['query'];
+    }
+    $environment_entities = array_filter(
+      $environment_entities,
+      function (EnvironmentIndicator $entity) {
+        return $entity->status()
+          && !empty($entity->getUrl())
+          && $this->hasAccessEnvironment($entity->id());
+      }
+    );
+
+    $links = array_map(
+      function (EnvironmentIndicator $entity) use ($path) {
+        return [
+          'attributes' => [
+            'style' => 'color: ' . $entity->getFgColor() . '; background-color: ' . $entity->getBgColor() . ';',
+            'title' => $this->t('Opens the current page in the selected environment.'),
+          ],
+          'title' => $this->t('Open on @label', ['@label' => $entity->label()]),
+          'url' => Url::fromUri($entity->getUrl() . $path),
+          'type' => 'link',
+          'weight' => $entity->getWeight(),
+        ];
+      },
+      $environment_entities
+    );
+
+    if (!$links) {
+      return [];
+    }
+
+    uasort($links, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
+
+    return $links;
+  }
+
   /**
    * Get the cache tags for the environment indicator switcher.
    *
-- 
GitLab


From a466986565162bf90d233465761a105827251c59 Mon Sep 17 00:00:00 2001
From: Chris Green <chrisgreen@arizona.edu>
Date: Tue, 27 May 2025 17:34:06 -0700
Subject: [PATCH 4/5] Updates

---
 environment_indicator.module | 34 ++++++++++++++++++++++++++++++++++
 src/ToolbarHandler.php       | 32 ++++++++++++++++++++++----------
 2 files changed, 56 insertions(+), 10 deletions(-)

diff --git a/environment_indicator.module b/environment_indicator.module
index f1f8bca2..b42eb38e 100644
--- a/environment_indicator.module
+++ b/environment_indicator.module
@@ -224,6 +224,40 @@ function environment_indicator_toolbar() {
     ->toolbar();
 }
 
+/**
+ * Helper function that generates the environment switcher links.
+ *
+ * @return array
+ *   A renderable array with the links.
+ *
+ * @deprecated in environment_indicator:4.0.22 and is removed from environment_indicator:5.0.0.
+ *   Use
+ *   \Drupal::service('environment_indicator.switcher_manager')->getLinks()
+ *   instead.
+ * @see https://www.drupal.org/node/3526893
+ */
+function _environment_indicator_switcher_links(): array {
+  return \Drupal::service('environment_indicator.toolbar_handler')
+    ->getLinks();
+}
+
+/**
+ * Get the cache tags for the environment indicator switcher.
+ *
+ * @return string[]
+ *   The cache tags.
+ *
+ * @deprecated in environment_indicator:4.0.22 and is removed from environment_indicator:5.0.0.
+ *   Use
+ *   \Drupal::service('environment_indicator.switcher_manager')->getLinks()
+ *   instead.
+ * @see https://www.drupal.org/node/3526893
+ */
+function _environment_indicator_switcher_cache_tags(): array {
+  return \Drupal::service('environment_indicator.toolbar_handler')
+    ->getCacheTags();
+}
+
 /**
  * Helper function that checks if there is external integration.
  *
diff --git a/src/ToolbarHandler.php b/src/ToolbarHandler.php
index 286f16d2..b430e6ef 100644
--- a/src/ToolbarHandler.php
+++ b/src/ToolbarHandler.php
@@ -317,11 +317,33 @@ class ToolbarHandler {
     return FALSE;
   }
 
+  /**
+   * Get the cache tags for the environment indicator switcher.
+   *
+   * @return array
+   *   The cache tags.
+   *
+   * @deprecated in environment_indicator:4.0.22 and is removed from environment_indicator:5.0.0.
+   *   Use
+   *   \Drupal::service('environment_indicator.switcher_manager')->getCacheTags()
+   *   instead.
+   * @see https://www.drupal.org/node/3526893
+   */
+  public function getCacheTags(): array {
+    return $this->entityTypeManager->getDefinition('environment_indicator')->getListCacheTags();
+  }
+
   /**
    * Get all the links for the switcher.
    *
    * @return array
    *   Returns all the links.
+   *
+   * @deprecated in environment_indicator:4.0.22 and is removed from environment_indicator:5.0.0.
+   *   Use
+   *   \Drupal::service('environment_indicator.switcher_manager')->getLinks()
+   *   instead.
+   * @see https://www.drupal.org/node/3526893
    */
   public function getLinks(): array {
     if (!$environment_entities = EnvironmentIndicator::loadMultiple()) {
@@ -369,14 +391,4 @@ class ToolbarHandler {
     return $links;
   }
 
-  /**
-   * Get the cache tags for the environment indicator switcher.
-   *
-   * @return array
-   *   The cache tags.
-   */
-  public function getCacheTags(): array {
-    return $this->entityTypeManager->getDefinition('environment_indicator')->getListCacheTags();
-  }
-
 }
-- 
GitLab


From a0a00f4c8c27a53b3bc16b6c4faf8659d91a533a Mon Sep 17 00:00:00 2001
From: Chris Green <chrisgreen@arizona.edu>
Date: Tue, 27 May 2025 17:35:59 -0700
Subject: [PATCH 5/5] Updates

---
 environment_indicator.module | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/environment_indicator.module b/environment_indicator.module
index b42eb38e..2c33584f 100644
--- a/environment_indicator.module
+++ b/environment_indicator.module
@@ -241,6 +241,20 @@ function _environment_indicator_switcher_links(): array {
     ->getLinks();
 }
 
+/**
+ * Helper function that checks if there is external integration.
+ *
+ * @param string $integration
+ *   Name of the integration: toolbar, admin_menu, ...
+ *
+ * @return bool
+ *   TRUE if integration is enabled. FALSE otherwise.
+ */
+function _environment_indicator_external_integration_is_enabled(string $integration): bool {
+  return \Drupal::service('environment_indicator.toolbar_handler')
+    ->externalIntegration($integration);
+}
+
 /**
  * Get the cache tags for the environment indicator switcher.
  *
@@ -258,20 +272,6 @@ function _environment_indicator_switcher_cache_tags(): array {
     ->getCacheTags();
 }
 
-/**
- * Helper function that checks if there is external integration.
- *
- * @param string $integration
- *   Name of the integration: toolbar, admin_menu, ...
- *
- * @return bool
- *   TRUE if integration is enabled. FALSE otherwise.
- */
-function _environment_indicator_external_integration_is_enabled(string $integration): bool {
-  return \Drupal::service('environment_indicator.toolbar_handler')
-    ->externalIntegration($integration);
-}
-
 /**
  * Loads an environment indicator in a procedural way.
  *
-- 
GitLab