diff --git a/src/Hook/VotingApiWidgetsVotingApiHooks.php b/src/Hook/VotingApiWidgetsVotingApiHooks.php new file mode 100644 index 0000000000000000000000000000000000000000..105b9bfb60e6d446da469edd35e5ff9002c7b2d6 --- /dev/null +++ b/src/Hook/VotingApiWidgetsVotingApiHooks.php @@ -0,0 +1,75 @@ +<?php + +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; + +/** + * Implementations of Voting API module hooks. + */ +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(). + * + * 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 { + $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_'. + 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 = $this->entityTypeManager->getStorage($entity_type)->load($entity_id); + if (!$entity || !isset($bundles[$entity->bundle()])) { + // 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 af3592256354502f7e1a8f3d07fbfb352f6f490d..07fe34fc37c929cac42287717c0a61f3d5d419e7 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 = 50; + + $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'], + ]); + } +} diff --git a/votingapi_widgets.module b/votingapi_widgets.module index 83be81834aa0fe43fe68c8122f7201e83da645fe..6774fe49207c4537d5a7b5339ae11c70f210746c 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($vote_results, $entity_type, $entity_id); +} + /** * Implements hook_theme_suggestions_alter(). */ diff --git a/votingapi_widgets.services.yml b/votingapi_widgets.services.yml index 4c0cc1ad1a52f1020f745d459f0308b426be9585..ea63a363b3a585d888bb9623bd903d79cb90def9 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