From 7e8a995f44aa405c3f139f5509beb714cc8f81ab Mon Sep 17 00:00:00 2001
From: loze <loze@ilgstudio.com>
Date: Thu, 20 Mar 2025 16:10:34 -0700
Subject: [PATCH 1/9] Don't save aggregated vote results for fields that do not
 exist on the entity type

---
 votingapi_widgets.module | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/votingapi_widgets.module b/votingapi_widgets.module
index 83be818..65a0f6d 100644
--- a/votingapi_widgets.module
+++ b/votingapi_widgets.module
@@ -91,3 +91,18 @@ function votingapi_widgets_preprocess_votingapi_widgets_summary(array &$variable
     $variables['results']['vote_field_count'] = 0;
   }
 }
+
+/**
+ * Implements hook_votingapi_results_alter().
+ *
+ * Removes aggregated vote results for votingapi_widget fields that do not
+ * exist on the entity type, preventing unnecessary data storage.
+ */
+function votingapi_widgets_votingapi_results_alter(array &$vote_results, $entity_type, $entity_id) {
+  foreach ($vote_results as $key => $result) {
+    $function_id = explode(':', $result['function']);
+    if (count($function_id) > 1 && str_starts_with($function_id[0], 'vote_field_') && !str_starts_with($function_id[1], $entity_type . '.')) {
+      unset($vote_results[$key]);
+    }
+  }
+}
-- 
GitLab


From 1cb591b7ebd144a6f0c6192f7b0d27c42b5b6603 Mon Sep 17 00:00:00 2001
From: Tim Rohaly <tr@202830.no-reply.drupal.org>
Date: Thu, 20 Mar 2025 21:36:23 -0700
Subject: [PATCH 2/9] Move code into OO hook with BC legacy hook implementation

---
 src/Hook/VotingApiWidgetsVotingApiHooks.php | 28 +++++++++++++++++++++
 votingapi_widgets.module                    | 24 +++++++-----------
 votingapi_widgets.services.yml              |  3 +++
 3 files changed, 40 insertions(+), 15 deletions(-)
 create mode 100644 src/Hook/VotingApiWidgetsVotingApiHooks.php

diff --git a/src/Hook/VotingApiWidgetsVotingApiHooks.php b/src/Hook/VotingApiWidgetsVotingApiHooks.php
new file mode 100644
index 0000000..b18e51a
--- /dev/null
+++ b/src/Hook/VotingApiWidgetsVotingApiHooks.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\votingapi_widgets\Hook;
+
+/**
+ * Implementations of Voting API module hooks.
+ */
+final class VotingApiVotingApiHooks {
+
+  /**
+   * Implements hook_votingapi_results_alter().
+   *
+   * Removes aggregated vote results for votingapi_widget fields that do not
+   * exist on the entity type, preventing unnecessary data storage.
+   */
+  #[Hook('votingapi_results_alter')]
+  public function votingapiResultsAlter(array &$vote_results, string $entity_type, string|int $entity_id): void {
+    foreach ($vote_results as $key => $result) {
+      $function_id = explode(':', $result['function']);
+      if (count($function_id) > 1 && str_starts_with($function_id[0], 'vote_field_') && !str_starts_with($function_id[1], $entity_type . '.')) {
+        unset($vote_results[$key]);
+      }
+    }
+  }
+
+}
diff --git a/votingapi_widgets.module b/votingapi_widgets.module
index 65a0f6d..79439e3 100644
--- a/votingapi_widgets.module
+++ b/votingapi_widgets.module
@@ -13,6 +13,7 @@ use Drupal\field\Entity\FieldConfig;
 use Drupal\votingapi_widgets\Hook\VotingApiWidgetsEntityHooks;
 use Drupal\votingapi_widgets\Hook\VotingApiWidgetsFormHooks;
 use Drupal\votingapi_widgets\Hook\VotingApiWidgetsHelpHooks;
+use Drupal\votingapi_widgets\Hook\VotingApiWidgetsVotingApiHooks;
 
 /**
  * Implements hook_help().
@@ -46,6 +47,14 @@ function votingapi_widgets_form_field_config_edit_form_alter(&$form, FormStateIn
   \Drupal::service(VotingApiWidgetsFormHooks::class)->fieldConfigEditFormAlter($form, $form_state);
 }
 
+/**
+ * Implements hook_votingapi_results_alter().
+ */
+#[LegacyHook]
+function votingapi_widgets_votingapi_results_alter(array &$vote_results, string $entity_type, string|int $entity_id) {
+  \Drupal::service(VotingApiWidgetsVotingApiHooks::class)->votingApiResultsAlter($results, $entity_type, $entity_id);
+}
+
 /**
  * Implements hook_theme_suggestions_alter().
  */
@@ -91,18 +100,3 @@ function votingapi_widgets_preprocess_votingapi_widgets_summary(array &$variable
     $variables['results']['vote_field_count'] = 0;
   }
 }
-
-/**
- * Implements hook_votingapi_results_alter().
- *
- * Removes aggregated vote results for votingapi_widget fields that do not
- * exist on the entity type, preventing unnecessary data storage.
- */
-function votingapi_widgets_votingapi_results_alter(array &$vote_results, $entity_type, $entity_id) {
-  foreach ($vote_results as $key => $result) {
-    $function_id = explode(':', $result['function']);
-    if (count($function_id) > 1 && str_starts_with($function_id[0], 'vote_field_') && !str_starts_with($function_id[1], $entity_type . '.')) {
-      unset($vote_results[$key]);
-    }
-  }
-}
diff --git a/votingapi_widgets.services.yml b/votingapi_widgets.services.yml
index 4c0cc1a..ea63a36 100644
--- a/votingapi_widgets.services.yml
+++ b/votingapi_widgets.services.yml
@@ -18,3 +18,6 @@ services:
   Drupal\votingapi_widgets\Hook\VotingApiWidgetsHelpHooks:
     class: \Drupal\votingapi_widgets\Hook\VotingApiWidgetsHelpHooks
     autowire: true
+  Drupal\votingapi_widgets\Hook\VotingApiWidgetsVotingApiHooks:
+    class: \Drupal\votingapi_widgets\Hook\VotingApiWidgetsVotingApiHooks
+    autowire: true
-- 
GitLab


From a2e768d3855b12aaad5dbe497372b7f3c9f1243b Mon Sep 17 00:00:00 2001
From: Tim Rohaly <tr@202830.no-reply.drupal.org>
Date: Thu, 20 Mar 2025 21:42:22 -0700
Subject: [PATCH 3/9] Forgot a use statement

---
 src/Hook/VotingApiWidgetsVotingApiHooks.php | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/Hook/VotingApiWidgetsVotingApiHooks.php b/src/Hook/VotingApiWidgetsVotingApiHooks.php
index b18e51a..60fb749 100644
--- a/src/Hook/VotingApiWidgetsVotingApiHooks.php
+++ b/src/Hook/VotingApiWidgetsVotingApiHooks.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace Drupal\votingapi_widgets\Hook;
 
+use Drupal\Core\Hook\Attribute\Hook;
+
 /**
  * Implementations of Voting API module hooks.
  */
-- 
GitLab


From 61e8f01b1d4ffc5b479c4e86ad62c127cc34d316 Mon Sep 17 00:00:00 2001
From: Tim Rohaly <tr@202830.no-reply.drupal.org>
Date: Thu, 20 Mar 2025 21:45:57 -0700
Subject: [PATCH 4/9] Fix naming

---
 src/Hook/VotingApiWidgetsVotingApiHooks.php | 2 +-
 votingapi_widgets.module                    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Hook/VotingApiWidgetsVotingApiHooks.php b/src/Hook/VotingApiWidgetsVotingApiHooks.php
index 60fb749..5aa655c 100644
--- a/src/Hook/VotingApiWidgetsVotingApiHooks.php
+++ b/src/Hook/VotingApiWidgetsVotingApiHooks.php
@@ -9,7 +9,7 @@ use Drupal\Core\Hook\Attribute\Hook;
 /**
  * Implementations of Voting API module hooks.
  */
-final class VotingApiVotingApiHooks {
+final class VotingApiWidgetsVotingApiHooks {
 
   /**
    * Implements hook_votingapi_results_alter().
diff --git a/votingapi_widgets.module b/votingapi_widgets.module
index 79439e3..6774fe4 100644
--- a/votingapi_widgets.module
+++ b/votingapi_widgets.module
@@ -52,7 +52,7 @@ function votingapi_widgets_form_field_config_edit_form_alter(&$form, FormStateIn
  */
 #[LegacyHook]
 function votingapi_widgets_votingapi_results_alter(array &$vote_results, string $entity_type, string|int $entity_id) {
-  \Drupal::service(VotingApiWidgetsVotingApiHooks::class)->votingApiResultsAlter($results, $entity_type, $entity_id);
+  \Drupal::service(VotingApiWidgetsVotingApiHooks::class)->votingApiResultsAlter($vote_results, $entity_type, $entity_id);
 }
 
 /**
-- 
GitLab


From 7d10db74a467cd7c0294a6979ca0d076001cf40c Mon Sep 17 00:00:00 2001
From: loze <loze@ilgstudio.com>
Date: Fri, 21 Mar 2025 22:52:47 -0700
Subject: [PATCH 5/9] Checking for other scenarios where results are sometimes
 created for non-existing entity/bundle/field combos.

---
 src/Hook/VotingApiWidgetsVotingApiHooks.php | 23 +++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/src/Hook/VotingApiWidgetsVotingApiHooks.php b/src/Hook/VotingApiWidgetsVotingApiHooks.php
index 5aa655c..337cc87 100644
--- a/src/Hook/VotingApiWidgetsVotingApiHooks.php
+++ b/src/Hook/VotingApiWidgetsVotingApiHooks.php
@@ -19,10 +19,29 @@ final class VotingApiWidgetsVotingApiHooks {
    */
   #[Hook('votingapi_results_alter')]
   public function votingapiResultsAlter(array &$vote_results, string $entity_type, string|int $entity_id): void {
+    $instances = \Drupal::service('entity_field.manager')->getFieldMapByFieldType('voting_api_field');
     foreach ($vote_results as $key => $result) {
       $function_id = explode(':', $result['function']);
-      if (count($function_id) > 1 && str_starts_with($function_id[0], 'vote_field_') && !str_starts_with($function_id[1], $entity_type . '.')) {
-        unset($vote_results[$key]);
+      // A votingapi_widget field function starts with 'vote_field_'.
+      if (str_starts_with($function_id[0], 'vote_field_') && isset($function_id[1])) {
+        // The second part is the derivative id 'ENTITY_TYPE.FIELD_NAME'.
+        $derivative_id = explode('.', $function_id[1]);
+        if ($entity_type != $derivative_id[0]) {
+          // This entity_type does not match.
+          unset($vote_results[$key]);
+        }
+        elseif (empty($instances[$entity_type][$derivative_id[1]])) {
+          // This field_name does not exist for the entity.
+          unset($vote_results[$key]);
+        }
+        else {
+          $bundles = $instances[$entity_type][$derivative_id[1]]['bundles'];
+          $entity = \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_id);
+          if (!$entity || !isset($bundles[$entity->bundle()])) {
+            // The bundle does not match the entity.
+            unset($vote_results[$key]);
+          }
+        }
       }
     }
   }
-- 
GitLab


From 9a7210c4f819a5df66df5d0f21d45cae082c5998 Mon Sep 17 00:00:00 2001
From: loze <loze@ilgstudio.com>
Date: Fri, 21 Mar 2025 22:57:02 -0700
Subject: [PATCH 6/9] Added update hook to clean up the results table.

---
 votingapi_widgets.install | 48 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/votingapi_widgets.install b/votingapi_widgets.install
index af35922..aec34fe 100644
--- a/votingapi_widgets.install
+++ b/votingapi_widgets.install
@@ -25,3 +25,51 @@ function votingapi_widgets_requirements($phase) {
 
   return $requirements;
 }
+
+/**
+ * Recalculate vote results for all voted entities.
+ */
+function votingapi_widgets_update_10001(&$sandbox) {
+  $votingapiResultsManager = \Drupal::service('plugin.manager.votingapi.resultfunction');
+
+  // Query the votes table to get the entities in need of recalculating.
+  $query = \Drupal::database()->select('votingapi_vote', 'v')
+    ->fields('v', ['entity_type', 'entity_id', 'type'])
+    ->groupBy('entity_type')
+    ->groupBy('entity_id')
+    ->groupBy('type');
+
+  // Recalculate results in a batch.
+  if (!isset($sandbox['total'])) {
+    $sandbox['total'] = $query->countQuery()->execute()->fetchField();
+    $sandbox['current'] = 0;
+  }
+
+  $entities_per_batch = 20;
+
+  $rows = $query
+    ->range($sandbox['current'], $entities_per_batch)
+    ->execute()
+    ->fetchAll();
+
+  if ($rows) {
+    foreach ($rows as $row) {
+      $votingapiResultsManager->recalculateResults(
+        $row->entity_type,
+        $row->entity_id,
+        $row->type
+      );
+      $sandbox['current']++;
+    }
+    if ($sandbox['total'] == 0) {
+      $sandbox['#finished'] = 1;
+    }
+    else {
+      $sandbox['#finished'] = ($sandbox['current'] / $sandbox['total']);
+    }
+    return t(':count of :total voted entities processed.', [
+      ':count' => $sandbox['current'],
+      ':total' => $sandbox['total'],
+    ]);
+  }
+}
-- 
GitLab


From 14221a00e01abed8c796dc3a6a85a58a2f7d8f2e Mon Sep 17 00:00:00 2001
From: loze <loze@ilgstudio.com>
Date: Fri, 21 Mar 2025 23:17:17 -0700
Subject: [PATCH 7/9] Use dependency injection in hooks class

---
 src/Hook/VotingApiWidgetsVotingApiHooks.php | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/src/Hook/VotingApiWidgetsVotingApiHooks.php b/src/Hook/VotingApiWidgetsVotingApiHooks.php
index 337cc87..f4a9e27 100644
--- a/src/Hook/VotingApiWidgetsVotingApiHooks.php
+++ b/src/Hook/VotingApiWidgetsVotingApiHooks.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace Drupal\votingapi_widgets\Hook;
 
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Hook\Attribute\Hook;
 
 /**
@@ -11,6 +13,19 @@ use Drupal\Core\Hook\Attribute\Hook;
  */
 final class VotingApiWidgetsVotingApiHooks {
 
+  /**
+   * Constructs a new VotingApiWidgetsVotingApiHooks service.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
+   *   The entity_type.manager service.
+   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager
+   *   The entity_field.manager service.
+   */
+  public function __construct(
+    protected EntityTypeManagerInterface $entityTypeManager,
+    protected EntityFieldManagerInterface $entityFieldManager,
+  ) {}
+
   /**
    * Implements hook_votingapi_results_alter().
    *
@@ -19,7 +34,7 @@ final class VotingApiWidgetsVotingApiHooks {
    */
   #[Hook('votingapi_results_alter')]
   public function votingapiResultsAlter(array &$vote_results, string $entity_type, string|int $entity_id): void {
-    $instances = \Drupal::service('entity_field.manager')->getFieldMapByFieldType('voting_api_field');
+    $instances = $this->entityFieldManager->getFieldMapByFieldType('voting_api_field');
     foreach ($vote_results as $key => $result) {
       $function_id = explode(':', $result['function']);
       // A votingapi_widget field function starts with 'vote_field_'.
@@ -36,7 +51,7 @@ final class VotingApiWidgetsVotingApiHooks {
         }
         else {
           $bundles = $instances[$entity_type][$derivative_id[1]]['bundles'];
-          $entity = \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_id);
+          $entity = $this->entityTypeManager->getStorage($entity_type)->load($entity_id);
           if (!$entity || !isset($bundles[$entity->bundle()])) {
             // The bundle does not match the entity.
             unset($vote_results[$key]);
-- 
GitLab


From ac976029cd6f174e1443db8021a89351b8c69494 Mon Sep 17 00:00:00 2001
From: loze <loze@ilgstudio.com>
Date: Fri, 21 Mar 2025 23:53:30 -0700
Subject: [PATCH 8/9] Clarify comment.

---
 src/Hook/VotingApiWidgetsVotingApiHooks.php | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/Hook/VotingApiWidgetsVotingApiHooks.php b/src/Hook/VotingApiWidgetsVotingApiHooks.php
index f4a9e27..f4b036a 100644
--- a/src/Hook/VotingApiWidgetsVotingApiHooks.php
+++ b/src/Hook/VotingApiWidgetsVotingApiHooks.php
@@ -22,9 +22,10 @@ final class VotingApiWidgetsVotingApiHooks {
    *   The entity_field.manager service.
    */
   public function __construct(
-    protected EntityTypeManagerInterface $entityTypeManager,
+    protected EntityTypeManagerInterface  $entityTypeManager,
     protected EntityFieldManagerInterface $entityFieldManager,
-  ) {}
+  ) {
+  }
 
   /**
    * Implements hook_votingapi_results_alter().
@@ -53,7 +54,7 @@ final class VotingApiWidgetsVotingApiHooks {
           $bundles = $instances[$entity_type][$derivative_id[1]]['bundles'];
           $entity = $this->entityTypeManager->getStorage($entity_type)->load($entity_id);
           if (!$entity || !isset($bundles[$entity->bundle()])) {
-            // The bundle does not match the entity.
+            // The field is not present on this bundle.
             unset($vote_results[$key]);
           }
         }
-- 
GitLab


From 5fe2787499ce5ad3acc8002f9a1082b631d6a772 Mon Sep 17 00:00:00 2001
From: loze <loze@ilgstudio.com>
Date: Wed, 2 Apr 2025 20:33:01 -0700
Subject: [PATCH 9/9] Exclude results where the vote type does not match the
 vote type set for the field.

---
 src/Hook/VotingApiWidgetsVotingApiHooks.php | 10 ++++++++++
 votingapi_widgets.install                   |  2 +-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/Hook/VotingApiWidgetsVotingApiHooks.php b/src/Hook/VotingApiWidgetsVotingApiHooks.php
index f4b036a..105b9bf 100644
--- a/src/Hook/VotingApiWidgetsVotingApiHooks.php
+++ b/src/Hook/VotingApiWidgetsVotingApiHooks.php
@@ -57,6 +57,16 @@ final class VotingApiWidgetsVotingApiHooks {
             // The field is not present on this bundle.
             unset($vote_results[$key]);
           }
+          else {
+            // Check for vote types not matching the one set for this field.
+            $field_storage = $this->entityFieldManager->getFieldStorageDefinitions($entity_type);
+            if (isset($field_storage[$derivative_id[1]])) {
+              $field_settings = $field_storage[$derivative_id[1]]->getSettings();
+              if ($result['type'] != $field_settings['vote_type']) {
+                unset($vote_results[$key]);
+              }
+            }
+          }
         }
       }
     }
diff --git a/votingapi_widgets.install b/votingapi_widgets.install
index aec34fe..07fe34f 100644
--- a/votingapi_widgets.install
+++ b/votingapi_widgets.install
@@ -45,7 +45,7 @@ function votingapi_widgets_update_10001(&$sandbox) {
     $sandbox['current'] = 0;
   }
 
-  $entities_per_batch = 20;
+  $entities_per_batch = 50;
 
   $rows = $query
     ->range($sandbox['current'], $entities_per_batch)
-- 
GitLab