diff --git a/modules/votingapi_tokens/src/Hook/VotingapiTokensHooks.php b/modules/votingapi_tokens/src/Hook/VotingapiTokensHooks.php
new file mode 100644
index 0000000..4c7827c
--- /dev/null
+++ b/modules/votingapi_tokens/src/Hook/VotingapiTokensHooks.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\votingapi_tokens\Hook;
+
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Hook implementations for votingapi_tokens.
+ */
+class VotingapiTokensHooks {
+  use StringTranslationTrait;
+
+  /**
+   * Implements hook_help().
+   */
+  #[Hook('help')]
+  public function help($route_name, RouteMatchInterface $route_match) {
+    switch ($route_name) {
+      case 'help.page.votingapi_tokens':
+        $output = '<h3>' . $this->t('About') . '</h3>';
+        $output .= '<p>' . $this->t('This module enables tokens for all entity types that has <kbd>voting_api_field</kbd>.') . '</p>';
+        $output .= '<h3>' . $this->t('Usage') . '</h3>';
+        $output .= '<p>' . $this->t('Tokens are dynamic and you have to pass <em>vote_type</em> as the last parameter. Module defines four tokens:');
+        $output .= $this->t('<ul><li>Vote count :: <code>[votingapi_ENTITY_TYPE_token:vote_count:VOTE_TYPE]</code></li>');
+        $output .= $this->t('<li>Vote average :: <code>[votingapi_ENTITY_TYPE_token:vote_average:VOTE_TYPE]</code></li>');
+        $output .= $this->t('<li>Best vote :: <code>[votingapi_ENTITY_TYPE_token:best_vote:VOTE_TYPE]</code></li>');
+        $output .= $this->t('<li>Worst vote :: <code>[votingapi_ENTITY_TYPE_token:worst_vote:VOTE_TYPE]</code></li>');
+        $output .= '</ul></p>';
+        return $output;
+
+      default:
+    }
+  }
+
+}
diff --git a/modules/votingapi_tokens/src/Hook/VotingapiTokensTokensHooks.php b/modules/votingapi_tokens/src/Hook/VotingapiTokensTokensHooks.php
new file mode 100644
index 0000000..71f4902
--- /dev/null
+++ b/modules/votingapi_tokens/src/Hook/VotingapiTokensTokensHooks.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Drupal\votingapi_tokens\Hook;
+
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Hook implementations for votingapi_tokens.
+ */
+class VotingapiTokensTokensHooks {
+  use StringTranslationTrait;
+
+  /**
+   * Implements hook_token_info().
+   */
+  #[Hook('token_info')]
+  public function tokenInfo() {
+    $entity_types = _votingapi_tokens_get_entity_types();
+    $types = [];
+    $tokens = [];
+    foreach ($entity_types as $entity_type) {
+      $types['votingapi_' . $entity_type . '_token'] = [
+        'name' => $this->t('VotingAPI tokens'),
+        'description' => $this->t('Dynamic Tokens for VotingAPI.'),
+        'needs-data' => $entity_type,
+      ];
+      $tokens['votingapi_' . $entity_type . '_token']['vote_count'] = [
+        'name' => $this->t('Vote count'),
+        'dynamic' => TRUE,
+        'description' => $this->t('Number of votes.'),
+      ];
+      $tokens['votingapi_' . $entity_type . '_token']['vote_average'] = [
+        'name' => $this->t('Average Result'),
+        'dynamic' => TRUE,
+        'description' => $this->t('Average result of votes.'),
+      ];
+      $tokens['votingapi_' . $entity_type . '_token']['best_vote'] = [
+        'name' => $this->t('Best Vote'),
+        'dynamic' => TRUE,
+        'description' => $this->t('Best Vote cast.'),
+      ];
+      $tokens['votingapi_' . $entity_type . '_token']['worst_vote'] = [
+        'name' => $this->t('Worst Vote'),
+        'dynamic' => TRUE,
+        'description' => $this->t('Worst Vote cast.'),
+      ];
+    }
+    return [
+      'types' => $types,
+      'tokens' => $tokens,
+    ];
+  }
+
+  /**
+   * Implements hook_tokens().
+   */
+  #[Hook('tokens')]
+  public function tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
+    $replacements = [];
+    // $entity_types = array_keys(\Drupal::entityTypeManager()->getDefinitions());
+    $entity_types = _votingapi_tokens_get_entity_types();
+    foreach ($entity_types as $entity_type) {
+      if ($type == 'votingapi_' . $entity_type . '_token' && !empty($data[$entity_type])) {
+        $votes = \Drupal::service('plugin.manager.votingapi.resultfunction')->getResults($entity_type, $data[$entity_type]->id());
+        foreach ($tokens as $name => $original) {
+          [$token_name, $vote_type] = explode(':', $name);
+          switch ($token_name) {
+            case 'vote_count':
+              $replacements[$original] = $votes[$vote_type]['vote_count'];
+              break;
+
+            case 'vote_average':
+              $replacements[$original] = $votes[$vote_type]['vote_average'];
+              break;
+
+            case 'best_vote':
+              $replacements[$original] = max(_votingapi_tokens_get_votes_per_entity($entity_type, $data[$entity_type]->id(), $vote_type));
+              break;
+
+            case 'worst_vote':
+              $replacements[$original] = min(_votingapi_tokens_get_votes_per_entity($entity_type, $data[$entity_type]->id(), $vote_type));
+              break;
+          }
+        }
+      }
+    }
+    return $replacements;
+  }
+
+}
diff --git a/modules/votingapi_tokens/tests/src/Kernel/VoteTokenTest.php b/modules/votingapi_tokens/tests/src/Kernel/VoteTokenTest.php
index 507ba61..c40ac38 100644
--- a/modules/votingapi_tokens/tests/src/Kernel/VoteTokenTest.php
+++ b/modules/votingapi_tokens/tests/src/Kernel/VoteTokenTest.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\votingapi_tokens\Kernel;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\Tests\token\Functional\TokenTestTrait;
 use Drupal\node\Entity\Node;
@@ -15,6 +17,8 @@ use Drupal\votingapi\Entity\Vote;
  *
  * @group VotingAPI
  */
+#[Group('VotingAPI')]
+#[RunTestsInSeparateProcesses]
 class VoteTokenTest extends KernelTestBase {
 
   use TokenTestTrait;
diff --git a/modules/votingapi_tokens/votingapi_tokens.module b/modules/votingapi_tokens/votingapi_tokens.module
index dae75ab..67c728b 100644
--- a/modules/votingapi_tokens/votingapi_tokens.module
+++ b/modules/votingapi_tokens/votingapi_tokens.module
@@ -7,25 +7,14 @@
  * Add support for vote tokens on entities..
  */
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\votingapi_tokens\Hook\VotingapiTokensHooks;
 use Drupal\Core\Routing\RouteMatchInterface;
 
 /**
  * Implements hook_help().
  */
+#[LegacyHook]
 function votingapi_tokens_help($route_name, RouteMatchInterface $route_match) {
-  switch ($route_name) {
-    case 'help.page.votingapi_tokens':
-      $output = '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('This module enables tokens for all entity types that has <kbd>voting_api_field</kbd>.') . '</p>';
-      $output .= '<h3>' . t('Usage') . '</h3>';
-      $output .= '<p>' . t('Tokens are dynamic and you have to pass <em>vote_type</em> as the last parameter. Module defines four tokens:');
-      $output .= t('<ul><li>Vote count :: <code>[votingapi_ENTITY_TYPE_token:vote_count:VOTE_TYPE]</code></li>');
-      $output .= t('<li>Vote average :: <code>[votingapi_ENTITY_TYPE_token:vote_average:VOTE_TYPE]</code></li>');
-      $output .= t('<li>Best vote :: <code>[votingapi_ENTITY_TYPE_token:best_vote:VOTE_TYPE]</code></li>');
-      $output .= t('<li>Worst vote :: <code>[votingapi_ENTITY_TYPE_token:worst_vote:VOTE_TYPE]</code></li>');
-      $output .= '</ul></p>';
-      return $output;
-
-    default:
-  }
+  return \Drupal::service(VotingapiTokensHooks::class)->help($route_name, $route_match);
 }
diff --git a/modules/votingapi_tokens/votingapi_tokens.services.yml b/modules/votingapi_tokens/votingapi_tokens.services.yml
new file mode 100644
index 0000000..fed27dd
--- /dev/null
+++ b/modules/votingapi_tokens/votingapi_tokens.services.yml
@@ -0,0 +1,9 @@
+
+services:
+  Drupal\votingapi_tokens\Hook\VotingapiTokensHooks:
+    class: Drupal\votingapi_tokens\Hook\VotingapiTokensHooks
+    autowire: true
+
+  Drupal\votingapi_tokens\Hook\VotingapiTokensTokensHooks:
+    class: Drupal\votingapi_tokens\Hook\VotingapiTokensTokensHooks
+    autowire: true
diff --git a/modules/votingapi_tokens/votingapi_tokens.tokens.inc b/modules/votingapi_tokens/votingapi_tokens.tokens.inc
index 2a09c01..50fb784 100644
--- a/modules/votingapi_tokens/votingapi_tokens.tokens.inc
+++ b/modules/votingapi_tokens/votingapi_tokens.tokens.inc
@@ -7,86 +7,24 @@
  * Add support for vote tokens on entities.
  */
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\votingapi_tokens\Hook\VotingapiTokensTokensHooks;
 use Drupal\Core\Render\BubbleableMetadata;
 
 /**
  * Implements hook_token_info().
  */
+#[LegacyHook]
 function votingapi_tokens_token_info() {
-  $entity_types = _votingapi_tokens_get_entity_types();
-
-  $types = [];
-  $tokens = [];
-  foreach ($entity_types as $entity_type) {
-    $types['votingapi_' . $entity_type . '_token'] = [
-      'name' => t('VotingAPI tokens'),
-      'description' => t('Dynamic Tokens for VotingAPI.'),
-      'needs-data' => $entity_type,
-    ];
-
-    $tokens['votingapi_' . $entity_type . '_token']['vote_count'] = [
-      'name' => t('Vote count'),
-      'dynamic' => TRUE,
-      'description' => t('Number of votes.'),
-    ];
-    $tokens['votingapi_' . $entity_type . '_token']['vote_average'] = [
-      'name' => t('Average Result'),
-      'dynamic' => TRUE,
-      'description' => t('Average result of votes.'),
-    ];
-    $tokens['votingapi_' . $entity_type . '_token']['best_vote'] = [
-      'name' => t('Best Vote'),
-      'dynamic' => TRUE,
-      'description' => t('Best Vote cast.'),
-    ];
-    $tokens['votingapi_' . $entity_type . '_token']['worst_vote'] = [
-      'name' => t('Worst Vote'),
-      'dynamic' => TRUE,
-      'description' => t('Worst Vote cast.'),
-    ];
-  }
-
-  return [
-    'types' => $types,
-    'tokens' => $tokens,
-  ];
+  return \Drupal::service(VotingapiTokensTokensHooks::class)->tokenInfo();
 }
 
 /**
  * Implements hook_tokens().
  */
+#[LegacyHook]
 function votingapi_tokens_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
-  $replacements = [];
-  // $entity_types = array_keys(\Drupal::entityTypeManager()->getDefinitions());
-  $entity_types = _votingapi_tokens_get_entity_types();
-  foreach ($entity_types as $entity_type) {
-    if ($type == 'votingapi_' . $entity_type . '_token' && !empty($data[$entity_type])) {
-      $votes = \Drupal::service('plugin.manager.votingapi.resultfunction')
-        ->getResults($entity_type, $data[$entity_type]->id());
-      foreach ($tokens as $name => $original) {
-        [$token_name, $vote_type] = explode(':', $name);
-        switch ($token_name) {
-          case 'vote_count':
-            $replacements[$original] = $votes[$vote_type]['vote_count'];
-            break;
-
-          case 'vote_average':
-            $replacements[$original] = $votes[$vote_type]['vote_average'];
-            break;
-
-          case 'best_vote':
-            $replacements[$original] = max(_votingapi_tokens_get_votes_per_entity($entity_type, $data[$entity_type]->id(), $vote_type));
-            break;
-
-          case 'worst_vote':
-            $replacements[$original] = min(_votingapi_tokens_get_votes_per_entity($entity_type, $data[$entity_type]->id(), $vote_type));
-            break;
-        }
-      }
-    }
-  }
-
-  return $replacements;
+  return \Drupal::service(VotingapiTokensTokensHooks::class)->tokens($type, $tokens, $data, $options, $bubbleable_metadata);
 }
 
 /**
diff --git a/src/Drush/Commands/VotingApiDrushCommands.php b/src/Drush/Commands/VotingApiDrushCommands.php
index 114b7cd..7c7fc47 100644
--- a/src/Drush/Commands/VotingApiDrushCommands.php
+++ b/src/Drush/Commands/VotingApiDrushCommands.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\votingapi\Drush\Commands;
 
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\Core\Database\Statement\FetchAs;
 use Drupal\Component\Datetime\TimeInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
@@ -115,11 +117,15 @@ final class VotingApiDrushCommands extends DrushCommands {
   public function recalculate(string $entity_type = 'node', string $vote_type = 'vote', ?string $entity_id = NULL): void {
     // Prep some starter query objects.
     if (empty($entity_id)) {
-      $votes = $this->database->select('votingapi_vote', 'vv')
+      $votes = DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $this->database->select('votingapi_vote', 'vv')
         ->fields('vv', ['entity_type', 'entity_id'])
         ->condition('entity_type', $entity_type, '=')
         ->distinct(TRUE)
-        ->execute()->fetchAll(\PDO::FETCH_ASSOC);
+        ->execute()->fetchAll(FetchAs::Associative), fn() => $this->database->select('votingapi_vote', 'vv')
+        ->fields('vv', ['entity_type', 'entity_id'])
+        ->condition('entity_type', $entity_type, '=')
+        ->distinct(TRUE)
+        ->execute()->fetchAll(\PDO::FETCH_ASSOC));
       $message = dt('Rebuilt voting results for @type votes.', ['@type' => $entity_type]);
     }
     else {
@@ -213,7 +219,7 @@ final class VotingApiDrushCommands extends DrushCommands {
     if ($entity_type == 'node' && !empty($options['types'])) {
       $query->condition('e.type', $options['types'], 'IN');
     }
-    $results = $query->execute()->fetchAll(\PDO::FETCH_ASSOC);
+    $results = DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $query->execute()->fetchAll(FetchAs::Associative), fn() => $query->execute()->fetchAll(\PDO::FETCH_ASSOC));
     foreach ($results as $entity) {
       $this->castVotes($entity_type, $entity['nid'], $options['age'], $uids, $vote_type);
     }
diff --git a/src/Hook/VotingapiViewsHooks.php b/src/Hook/VotingapiViewsHooks.php
new file mode 100644
index 0000000..4c4043b
--- /dev/null
+++ b/src/Hook/VotingapiViewsHooks.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace Drupal\votingapi\Hook;
+
+use Drupal\Component\Plugin\Exception\PluginNotFoundException;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Hook implementations for votingapi.
+ */
+class VotingapiViewsHooks {
+  use StringTranslationTrait;
+
+  /**
+   * Implements hook_views_data_alter().
+   */
+  #[Hook('views_data_alter')]
+  public function viewsDataAlter(&$data) {
+    $result_function_manager = \Drupal::service('plugin.manager.votingapi.resultfunction');
+    $result_functions = $result_function_manager->getDefinitions();
+    if (empty($result_functions)) {
+      return;
+    }
+    /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_manager */
+    $entity_manager = \Drupal::service('entity_type.manager');
+    try {
+      $vote_type_storage = $entity_manager->getStorage('vote_type');
+    }
+    catch (PluginNotFoundException $e) {
+      return;
+    }
+    $vote_types = $vote_type_storage->loadMultiple();
+    if (empty($vote_types)) {
+      return;
+    }
+    // Find entity types suitable for vote results views data.
+    $views_entity_types = [];
+    $entity_types = $entity_manager->getDefinitions();
+    foreach ($entity_types as $entity_type_id => $entity_type) {
+      // Exclude votes.
+      if ($entity_type_id == 'vote_result' || $entity_type_id == 'vote' || $entity_type->getBundleOf() == 'vote') {
+        continue;
+      }
+      // Limit to content entity types.
+      if ($entity_type->getGroup() == 'content' && $entity_type->isTranslatable() && $entity_type->getKey('id')) {
+        $views_entity_types[$entity_type_id] = $entity_type;
+      }
+    }
+    // Add views data for each entity type.
+    if ($views_entity_types) {
+      foreach ($views_entity_types as $entity_type_id => $entity_type) {
+        $data_table_name = $entity_type->getDataTable() ?: $entity_type->getBaseTable();
+        if (empty($data_table_name) || !isset($data[$data_table_name])) {
+          continue;
+        }
+        $id_key = $entity_type->getKey('id');
+        $tokens = [
+          '@entity_type' => $entity_type_id,
+          '@plural_label' => $entity_type->getPluralLabel(),
+        ];
+        // Process each result function.
+        foreach ($result_functions as $result_function_id => $result_function_definition) {
+          // Extracting label from definition, unless it is for a specific field
+          // then use the function ID so that you have context.
+          $tokens['@result_function'] = isset($result_function_definition['label']) && !strpos($result_function_id, '.') ? $result_function_definition['label'] : $result_function_id;
+          // Provide one to many relationship.
+          $data[$data_table_name][$entity_type_id . '_vote_result_' . str_replace('.', '_', $result_function_id)] = [
+            'title' => $this->t('Vote Result "@result_function" for @plural_label', $tokens),
+            'help' => $this->t('This includes vote result "@result_function" for the @plural_label', $tokens),
+            'relationship' => [
+              'base' => 'votingapi_result',
+              'base field' => 'entity_id',
+              'field' => $id_key,
+              'id' => 'standard',
+              'label' => $this->t('Vote Result "@result_function" for @plural_label', $tokens),
+              'extra' => [
+                        [
+                          'field' => 'entity_type',
+                          'value' => $entity_type_id,
+                        ],
+                        [
+                          'field' => 'function',
+                          'value' => $result_function_id,
+                        ],
+              ],
+            ],
+          ];
+          // Flattened relationship for each vote type.
+          foreach ($vote_types as $vote_type_name => $vote_type) {
+            $tokens['@vote_type_label'] = $vote_type->label();
+            $data[$data_table_name][$entity_type_id . '_vote_result_' . str_replace('.', '_', $result_function_id) . '_' . $vote_type_name] = [
+              'title' => $this->t('Vote Result "@result_function" for @plural_label: @vote_type_label', $tokens),
+              'help' => $this->t('This includes vote result "@result_function" for the @plural_label voted with @vote_type', $tokens),
+              'relationship' => [
+                'base' => 'votingapi_result',
+                'base field' => 'entity_id',
+                'field' => $id_key,
+                'id' => 'standard',
+                'label' => $this->t('Vote Result "@result_function" for @plural_label: @vote_type_label', $tokens),
+                'extra' => [
+                          [
+                            'field' => 'entity_type',
+                            'value' => $entity_type_id,
+                          ],
+                          [
+                            'field' => 'function',
+                            'value' => $result_function_id,
+                          ],
+                          [
+                            'field' => 'type',
+                            'value' => $vote_type_name,
+                          ],
+                ],
+              ],
+            ];
+          }
+        }
+      }
+    }
+  }
+
+}
diff --git a/src/Plugin/migrate/D6VotingApiDeriver.php b/src/Plugin/migrate/D6VotingApiDeriver.php
index 5faa18f..78caf8a 100644
--- a/src/Plugin/migrate/D6VotingApiDeriver.php
+++ b/src/Plugin/migrate/D6VotingApiDeriver.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\votingapi\Plugin\migrate;
 
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\Core\Database\Statement\FetchAs;
 use Drupal\Component\Plugin\Derivative\DeriverBase;
 use Drupal\Component\Plugin\PluginBase;
 use Drupal\Core\Database\DatabaseExceptionWrapper;
@@ -50,8 +52,9 @@ class D6VotingApiDeriver extends DeriverBase {
             $used_node_types_query->condition('v.content_type', 'node');
             $used_node_types_query->fields('n', ['type'])
               ->groupBy('n.type');
-            $bundles = array_keys($used_node_types_query->execute()
-              ->fetchAllAssoc('type', \PDO::FETCH_ASSOC));
+            $bundles = array_keys(DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $used_node_types_query->execute()
+              ->fetchAllAssoc('type', FetchAs::Associative), fn() => $used_node_types_query->execute()
+              ->fetchAllAssoc('type', \PDO::FETCH_ASSOC)));
             break;
 
           case 'comment':
@@ -62,8 +65,9 @@ class D6VotingApiDeriver extends DeriverBase {
             $query->condition('v.content_type', 'comment');
             $query->fields('n', ['type'])
               ->groupBy('n.type');
-            $bundles = array_keys($query->execute()
-              ->fetchAllAssoc('type', \PDO::FETCH_ASSOC));
+            $bundles = array_keys(DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $query->execute()
+              ->fetchAllAssoc('type', FetchAs::Associative), fn() => $query->execute()
+              ->fetchAllAssoc('type', \PDO::FETCH_ASSOC)));
             break;
 
         }
diff --git a/src/Plugin/migrate/VotingApiDeriver.php b/src/Plugin/migrate/VotingApiDeriver.php
index 11132fe..30b373a 100644
--- a/src/Plugin/migrate/VotingApiDeriver.php
+++ b/src/Plugin/migrate/VotingApiDeriver.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\votingapi\Plugin\migrate;
 
+use Drupal\Component\Utility\DeprecationHelper;
+use Drupal\Core\Database\Statement\FetchAs;
 use Drupal\Component\Plugin\Derivative\DeriverBase;
 use Drupal\Component\Plugin\PluginBase;
 use Drupal\Core\Database\DatabaseExceptionWrapper;
@@ -50,8 +52,9 @@ class VotingApiDeriver extends DeriverBase {
             $used_node_types_query->condition('v.entity_type', 'node');
             $used_node_types_query->fields('n', ['type'])
               ->groupBy('n.type');
-            $bundles = array_keys($used_node_types_query->execute()
-              ->fetchAllAssoc('type', \PDO::FETCH_ASSOC));
+            $bundles = array_keys(DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $used_node_types_query->execute()
+              ->fetchAllAssoc('type', FetchAs::Associative), fn() => $used_node_types_query->execute()
+              ->fetchAllAssoc('type', \PDO::FETCH_ASSOC)));
             break;
 
           case 'comment':
@@ -62,8 +65,9 @@ class VotingApiDeriver extends DeriverBase {
             $query->condition('v.entity_type', 'comment');
             $query->fields('n', ['type'])
               ->groupBy('n.type');
-            $bundles = array_keys($query->execute()
-              ->fetchAllAssoc('type', \PDO::FETCH_ASSOC));
+            $bundles = array_keys(DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $query->execute()
+              ->fetchAllAssoc('type', FetchAs::Associative), fn() => $query->execute()
+              ->fetchAllAssoc('type', \PDO::FETCH_ASSOC)));
             break;
 
         }
diff --git a/tests/modules/votingapi_test/src/Hook/VotingapiTestHooks.php b/tests/modules/votingapi_test/src/Hook/VotingapiTestHooks.php
new file mode 100644
index 0000000..6a35a7b
--- /dev/null
+++ b/tests/modules/votingapi_test/src/Hook/VotingapiTestHooks.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Drupal\votingapi_test\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for votingapi_test.
+ */
+class VotingapiTestHooks {
+  /**
+   * @file
+   * Test hook implementation code for the VotingApi module.
+   */
+
+  /**
+   * Implements hook_votingapi_results_alter().
+   */
+  #[Hook('votingapi_results_alter')]
+  public function votingapiResultsAlter(&$vote_results, $entity_type, $entity_id) {
+    // Add a new function and result.
+    $vote_results[] = [
+      'entity_id' => $entity_id,
+      'entity_type' => $entity_type,
+      'type' => 'vote',
+      'function' => 'ultimate_question',
+      'value' => 42,
+      'value_type' => 'points',
+      'timestamp' => \Drupal::time()->getRequestTime(),
+    ];
+  }
+
+}
diff --git a/tests/modules/votingapi_test/votingapi_test.module b/tests/modules/votingapi_test/votingapi_test.module
index 46e3841..1a683c4 100644
--- a/tests/modules/votingapi_test/votingapi_test.module
+++ b/tests/modules/votingapi_test/votingapi_test.module
@@ -1,5 +1,12 @@
 <?php
 
+/**
+ * @file
+ */
+
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\votingapi_test\Hook\VotingapiTestHooks;
+
 /**
  * @file
  * Test hook implementation code for the VotingApi module.
@@ -8,15 +15,7 @@
 /**
  * Implements hook_votingapi_results_alter().
  */
+#[LegacyHook]
 function votingapi_test_votingapi_results_alter(&$vote_results, $entity_type, $entity_id) {
-  // Add a new function and result.
-  $vote_results[] = [
-    'entity_id' => $entity_id,
-    'entity_type' => $entity_type,
-    'type' => 'vote',
-    'function' => 'ultimate_question',
-    'value' => 42,
-    'value_type' => 'points',
-    'timestamp' => \Drupal::time()->getRequestTime(),
-  ];
+  \Drupal::service(VotingapiTestHooks::class)->votingapiResultsAlter($vote_results, $entity_type, $entity_id);
 }
diff --git a/tests/modules/votingapi_test/votingapi_test.services.yml b/tests/modules/votingapi_test/votingapi_test.services.yml
new file mode 100644
index 0000000..3518758
--- /dev/null
+++ b/tests/modules/votingapi_test/votingapi_test.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\votingapi_test\Hook\VotingapiTestHooks:
+    class: Drupal\votingapi_test\Hook\VotingapiTestHooks
+    autowire: true
diff --git a/tests/src/Functional/VoteCreationTest.php b/tests/src/Functional/VoteCreationTest.php
index 79def33..fc423ba 100644
--- a/tests/src/Functional/VoteCreationTest.php
+++ b/tests/src/Functional/VoteCreationTest.php
@@ -4,6 +4,9 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\votingapi\Functional;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
+use Drupal\Component\Utility\DeprecationHelper;
 use Drupal\Tests\BrowserTestBase;
 use Drupal\node\Entity\Node;
 use Drupal\votingapi\Entity\Vote;
@@ -13,6 +16,8 @@ use Drupal\votingapi\Entity\Vote;
  *
  * @group VotingAPI
  */
+#[Group('VotingAPI')]
+#[RunTestsInSeparateProcesses]
 class VoteCreationTest extends BrowserTestBase {
 
   /**
@@ -45,7 +50,7 @@ class VoteCreationTest extends BrowserTestBase {
         'name' => 'Basic page',
         'display_submitted' => FALSE,
       ]);
-      node_add_body_field($node_type);
+      DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $this->createBodyField('node', $node_type->id()), fn() => node_add_body_field($node_type));
     }
 
     $this->drupalLogin($this->drupalCreateUser());
diff --git a/tests/src/Functional/VoteDeletionTest.php b/tests/src/Functional/VoteDeletionTest.php
index f68d63b..1495131 100644
--- a/tests/src/Functional/VoteDeletionTest.php
+++ b/tests/src/Functional/VoteDeletionTest.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\votingapi\Functional;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Tests\BrowserTestBase;
 
 /**
@@ -11,6 +13,8 @@ use Drupal\Tests\BrowserTestBase;
  *
  * @group VotingAPI
  */
+#[Group('VotingAPI')]
+#[RunTestsInSeparateProcesses]
 class VoteDeletionTest extends BrowserTestBase {
 
   /**
diff --git a/tests/src/Functional/VoteTest.php b/tests/src/Functional/VoteTest.php
index 36a6531..117db9d 100644
--- a/tests/src/Functional/VoteTest.php
+++ b/tests/src/Functional/VoteTest.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\votingapi\Functional;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Tests\BrowserTestBase;
 
 /**
@@ -11,6 +13,8 @@ use Drupal\Tests\BrowserTestBase;
  *
  * @group VotingAPI
  */
+#[Group('VotingAPI')]
+#[RunTestsInSeparateProcesses]
 class VoteTest extends BrowserTestBase {
 
   /**
diff --git a/tests/src/Functional/VoteTypeFormTest.php b/tests/src/Functional/VoteTypeFormTest.php
index 0d791c4..4f9165e 100644
--- a/tests/src/Functional/VoteTypeFormTest.php
+++ b/tests/src/Functional/VoteTypeFormTest.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\votingapi\Functional;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Tests\BrowserTestBase;
 
 /**
@@ -11,6 +13,8 @@ use Drupal\Tests\BrowserTestBase;
  *
  * @group VotingAPI
  */
+#[Group('VotingAPI')]
+#[RunTestsInSeparateProcesses]
 class VoteTypeFormTest extends BrowserTestBase {
 
   /**
diff --git a/tests/src/Functional/VotesViewTest.php b/tests/src/Functional/VotesViewTest.php
index e48da1c..b215138 100644
--- a/tests/src/Functional/VotesViewTest.php
+++ b/tests/src/Functional/VotesViewTest.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\votingapi\Functional;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Tests\BrowserTestBase;
 
 /**
@@ -11,6 +13,8 @@ use Drupal\Tests\BrowserTestBase;
  *
  * @group VotingAPI
  */
+#[Group('VotingAPI')]
+#[RunTestsInSeparateProcesses]
 class VotesViewTest extends BrowserTestBase {
 
   /**
diff --git a/tests/src/Kernel/VoteAccessTest.php b/tests/src/Kernel/VoteAccessTest.php
index ffabd7e..e4d71ad 100644
--- a/tests/src/Kernel/VoteAccessTest.php
+++ b/tests/src/Kernel/VoteAccessTest.php
@@ -4,6 +4,9 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\votingapi\Kernel;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
+use PHPUnit\Framework\Attributes\DataProvider;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\Tests\user\Traits\UserCreationTrait;
 use Drupal\votingapi\VoteInterface;
@@ -13,6 +16,8 @@ use Drupal\votingapi\VoteInterface;
  *
  * @group VotingAPI
  */
+#[Group('VotingAPI')]
+#[RunTestsInSeparateProcesses]
 class VoteAccessTest extends KernelTestBase {
   use UserCreationTrait;
 
@@ -121,6 +126,7 @@ class VoteAccessTest extends KernelTestBase {
    *
    * @dataProvider viewVoteAccessProvider
    */
+  #[DataProvider('viewVoteAccessProvider')]
   public function testVoteViewAccess($expected_access, $vote, $user): void {
     $this->assertSame($expected_access,
       $this->accessHandler->access($this->{$vote}, 'view', $this->{$user}),
diff --git a/tests/src/Kernel/migrate/D6VoteMigrationTest.php b/tests/src/Kernel/migrate/D6VoteMigrationTest.php
index 6325429..91d96b6 100644
--- a/tests/src/Kernel/migrate/D6VoteMigrationTest.php
+++ b/tests/src/Kernel/migrate/D6VoteMigrationTest.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\votingapi\Kernel\migrate;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
 
@@ -12,6 +14,8 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
  *
  * @group VotingAPI
  */
+#[Group('VotingAPI')]
+#[RunTestsInSeparateProcesses]
 class D6VoteMigrationTest extends MigrateDrupal6TestBase {
 
   /**
diff --git a/tests/src/Kernel/migrate/VoteMigrationTest.php b/tests/src/Kernel/migrate/VoteMigrationTest.php
index 19af5e1..a380c81 100644
--- a/tests/src/Kernel/migrate/VoteMigrationTest.php
+++ b/tests/src/Kernel/migrate/VoteMigrationTest.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\votingapi\Kernel\migrate;
 
+use PHPUnit\Framework\Attributes\Group;
+use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
 
@@ -12,6 +14,8 @@ use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
  *
  * @group VotingAPI
  */
+#[Group('VotingAPI')]
+#[RunTestsInSeparateProcesses]
 class VoteMigrationTest extends MigrateDrupal7TestBase {
 
   /**
diff --git a/votingapi.services.yml b/votingapi.services.yml
index 5a4fd02..17625d9 100644
--- a/votingapi.services.yml
+++ b/votingapi.services.yml
@@ -17,3 +17,7 @@ services:
   Drupal\votingapi\Hook\VotingApiViewsHooks:
     class: \Drupal\votingapi\Hook\VotingApiViewsHooks
     autowire: true
+
+  Drupal\votingapi\Hook\VotingapiViewsHooks:
+    class: Drupal\votingapi\Hook\VotingapiViewsHooks
+    autowire: true
diff --git a/votingapi.views.inc b/votingapi.views.inc
index 0e96a67..450d6e3 100644
--- a/votingapi.views.inc
+++ b/votingapi.views.inc
@@ -5,109 +5,13 @@
  * Views data alterations for the votingapi module.
  */
 
-use Drupal\Component\Plugin\Exception\PluginNotFoundException;
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\votingapi\Hook\VotingapiViewsHooks;
 
 /**
  * Implements hook_views_data_alter().
  */
+#[LegacyHook]
 function votingapi_views_data_alter(&$data) {
-  $result_function_manager = \Drupal::service('plugin.manager.votingapi.resultfunction');
-  $result_functions = $result_function_manager->getDefinitions();
-  if (empty($result_functions)) {
-    return;
-  }
-
-  /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_manager */
-  $entity_manager = \Drupal::service('entity_type.manager');
-  try {
-    $vote_type_storage = $entity_manager->getStorage('vote_type');
-  }
-  catch (PluginNotFoundException $e) {
-    return;
-  }
-
-  $vote_types = $vote_type_storage->loadMultiple();
-  if (empty($vote_types)) {
-    return;
-  }
-
-  // Find entity types suitable for vote results views data.
-  $views_entity_types = [];
-  $entity_types = $entity_manager->getDefinitions();
-
-  foreach ($entity_types as $entity_type_id => $entity_type) {
-    // Exclude votes.
-    if ($entity_type_id == 'vote_result' || $entity_type_id == 'vote' || $entity_type->getBundleOf() == 'vote') {
-      continue;
-    }
-
-    // Limit to content entity types.
-    if ($entity_type->getGroup() == 'content' &&
-        $entity_type->isTranslatable() &&
-        $entity_type->getKey('id')) {
-      $views_entity_types[$entity_type_id] = $entity_type;
-    }
-  }
-
-  // Add views data for each entity type.
-  if ($views_entity_types) {
-    foreach ($views_entity_types as $entity_type_id => $entity_type) {
-      $data_table_name = $entity_type->getDataTable() ?: $entity_type->getBaseTable();
-      if (empty($data_table_name) || !isset($data[$data_table_name])) {
-        continue;
-      }
-
-      $id_key = $entity_type->getKey('id');
-      $tokens = [
-        '@entity_type' => $entity_type_id,
-        '@plural_label' => $entity_type->getPluralLabel(),
-      ];
-
-      // Process each result function.
-      foreach ($result_functions as $result_function_id => $result_function_definition) {
-        // Extracting label from definition, unless it is for a specific field
-        // then use the function ID so that you have context.
-        $tokens['@result_function'] = (isset($result_function_definition['label']) && !strpos($result_function_id, '.')) ? $result_function_definition['label'] : $result_function_id;
-
-        // Provide one to many relationship.
-        $data[$data_table_name][$entity_type_id . '_vote_result_' . str_replace('.', '_', $result_function_id)] = [
-          'title' => t('Vote Result "@result_function" for @plural_label', $tokens),
-          'help' => t('This includes vote result "@result_function" for the @plural_label', $tokens),
-          'relationship' => [
-            'base' => 'votingapi_result',
-            'base field' => 'entity_id',
-            'field' => $id_key,
-            'id' => 'standard',
-            'label' => t('Vote Result "@result_function" for @plural_label', $tokens),
-            'extra' => [
-              ['field' => 'entity_type', 'value' => $entity_type_id],
-              ['field' => 'function', 'value' => $result_function_id],
-            ],
-          ],
-        ];
-
-        // Flattened relationship for each vote type.
-        foreach ($vote_types as $vote_type_name => $vote_type) {
-          $tokens['@vote_type_label'] = $vote_type->label();
-
-          $data[$data_table_name][$entity_type_id . '_vote_result_' . str_replace('.', '_', $result_function_id) . '_' . $vote_type_name] = [
-            'title' => t('Vote Result "@result_function" for @plural_label: @vote_type_label', $tokens),
-            'help' => t('This includes vote result "@result_function" for the @plural_label voted with @vote_type', $tokens),
-            'relationship' => [
-              'base' => 'votingapi_result',
-              'base field' => 'entity_id',
-              'field' => $id_key,
-              'id' => 'standard',
-              'label' => t('Vote Result "@result_function" for @plural_label: @vote_type_label', $tokens),
-              'extra' => [
-                ['field' => 'entity_type', 'value' => $entity_type_id],
-                ['field' => 'function', 'value' => $result_function_id],
-                ['field' => 'type', 'value' => $vote_type_name],
-              ],
-            ],
-          ];
-        }
-      }
-    }
-  }
+  \Drupal::service(VotingapiViewsHooks::class)->viewsDataAlter($data);
 }
