From 69986883e69585b89d11510fa1db2aa7fc6f457f Mon Sep 17 00:00:00 2001
From: Andrei Mateescu <2117-amateescu@users.noreply.drupalcode.org>
Date: Mon, 9 Dec 2024 22:15:43 +0000
Subject: [PATCH] Issue #3301512 by amateescu, itamair, kiseleva.t, s_leu,
 alecsmrekar, plach: Extend the ability to skip geocoding when processing a
 large number of entity updates, like migrations or workspace publishing

---
 modules/geocoder_field/geocoder_field.module  |  9 ++-
 .../geocoder_field.services.yml               |  6 ++
 .../WorkspacePublishingSubscriber.php         | 62 +++++++++++++++++++
 3 files changed, 75 insertions(+), 2 deletions(-)
 create mode 100644 modules/geocoder_field/src/EventSubscriber/WorkspacePublishingSubscriber.php

diff --git a/modules/geocoder_field/geocoder_field.module b/modules/geocoder_field/geocoder_field.module
index 0231fcd..f144e26 100644
--- a/modules/geocoder_field/geocoder_field.module
+++ b/modules/geocoder_field/geocoder_field.module
@@ -163,9 +163,14 @@ function geocoder_field_entity_presave(EntityInterface $entity) {
     return;
   }
 
+  // Skip any action if requested.
   $geocoder_config = \Drupal::configFactory()->get('geocoder.settings');
-  // Check geocoder_presave_disabled setting and do nothing if enabled.
-  if ($geocoder_config->get('geocoder_presave_disabled')) {
+  $config_disabled = $geocoder_config->get('geocoder_presave_disabled');
+  // Check if geocoder presave is disabled at runtime by a request attribute.
+  // i.e. via WorkspacePublishingSubscriber
+  // (@see https://www.drupal.org/i/3301512)
+  $runtime_disabled = \Drupal::request()->attributes->get('geocoder_presave_disabled', FALSE);
+  if ($config_disabled || $runtime_disabled) {
     return;
   }
 
diff --git a/modules/geocoder_field/geocoder_field.services.yml b/modules/geocoder_field/geocoder_field.services.yml
index 1bcd1c0..9876780 100644
--- a/modules/geocoder_field/geocoder_field.services.yml
+++ b/modules/geocoder_field/geocoder_field.services.yml
@@ -9,3 +9,9 @@ services:
     class: Drupal\geocoder_field\PreprocessorPluginManager
     parent: default_plugin_manager
     arguments: ["@country_manager"]
+
+  geocoder_field.workspace_publishing_subscriber:
+    class: Drupal\geocoder_field\EventSubscriber\WorkspacePublishingSubscriber
+    arguments: ['@request_stack']
+    tags:
+      - { name: event_subscriber }
diff --git a/modules/geocoder_field/src/EventSubscriber/WorkspacePublishingSubscriber.php b/modules/geocoder_field/src/EventSubscriber/WorkspacePublishingSubscriber.php
new file mode 100644
index 0000000..460ff3f
--- /dev/null
+++ b/modules/geocoder_field/src/EventSubscriber/WorkspacePublishingSubscriber.php
@@ -0,0 +1,62 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\geocoder_field\EventSubscriber;
+
+use Drupal\workspaces\Event\WorkspacePostPublishEvent;
+use Drupal\workspaces\Event\WorkspacePrePublishEvent;
+use Drupal\workspaces\Event\WorkspacePublishEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+/**
+ * Event subscriber to respond to workspace publishing events.
+ *
+ * The geocoding operations from geocoder_field_entity_presave() can be very
+ * expensive when updating many entities at once. Workspace publishing doesn't
+ * change any field data, it only re-saves the latest workspace-specific
+ * revision and sets it as the default one, so there is no need to update
+ * geocoding data.
+ *
+ * @see geocoder_field_entity_presave()
+ */
+class WorkspacePublishingSubscriber implements EventSubscriberInterface {
+
+  public function __construct(
+    protected RequestStack $requestStack,
+  ) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents(): array {
+    if (!class_exists(WorkspacePublishEvent::class)) {
+      return [];
+    }
+
+    return [
+      WorkspacePrePublishEvent::class => ['onPrePublish'],
+      WorkspacePostPublishEvent::class => ['onPostPublish'],
+    ];
+  }
+
+  /**
+   * Adds a custom request attribute to prevent geocoding updates.
+   */
+  public function onPrePublish(): void {
+    if ($request = $this->requestStack->getCurrentRequest()) {
+      $request->attributes->set('geocoder_presave_disabled', TRUE);
+    }
+  }
+
+  /**
+   * Removes the custom request attribute.
+   */
+  public function onPostPublish(): void {
+    if ($request = $this->requestStack->getCurrentRequest()) {
+      $request->attributes->remove('geocoder_presave_disabled');
+    }
+  }
+
+}
-- 
GitLab