diff --git a/entityqueue.install b/entityqueue.install
index 83a65b7..e59ebbb 100644
--- a/entityqueue.install
+++ b/entityqueue.install
@@ -5,6 +5,7 @@
  * Install, update and uninstall functions for the Entityqueue module.
  */
 
+use Drupal\Component\Utility\DeprecationHelper;
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 
@@ -35,7 +36,7 @@ function entityqueue_update_8001() {
     // Remove outdated properties.
     $config->clear('min_size')->clear('max_size')->clear('act_as_queue')->clear('target_type');
 
-    $config->save(TRUE);
+    DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => $config->save(), fn() => $config->save(TRUE));
   }
 }
 
diff --git a/entityqueue.module b/entityqueue.module
index fc6c8d2..5cd8147 100644
--- a/entityqueue.module
+++ b/entityqueue.module
@@ -5,29 +5,21 @@
  * Allows users to collect entities in arbitrarily ordered lists.
  */
 
-use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\entityqueue\Hook\EntityqueueHooks;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\views\ViewExecutable;
-use Drupal\entityqueue\Entity\EntityQueue;
-use Drupal\entityqueue\Entity\EntitySubqueue;
 use Drupal\views\Plugin\views\query\QueryPluginBase;
 
 /**
  * Implements hook_entity_field_access().
  */
+#[LegacyHook]
 function entityqueue_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, ?FieldItemListInterface $items = NULL) {
-  // Only allow edit access on a subqueue title field if the queue doesn't have
-  // automated subqueues.
-  if ($operation == 'edit' && $field_definition->getName() == 'title' && $items && $items->getEntity()->getEntityTypeId() === 'entity_subqueue') {
-    /** @var \Drupal\entityqueue\EntityQueueInterface $queue */
-    $queue = $items->getEntity()->getQueue();
-    return AccessResult::forbiddenIf($queue->getHandlerPlugin()->hasAutomatedSubqueues());
-  }
-
-  return AccessResult::neutral();
+  return \Drupal::service(EntityqueueHooks::class)->entityFieldAccess($operation, $field_definition, $account, $items);
 }
 
 /**
@@ -35,56 +27,9 @@ function entityqueue_entity_field_access($operation, FieldDefinitionInterface $f
  *
  * Add contextual links to views before rendering.
  */
+#[LegacyHook]
 function entityqueue_views_pre_render(ViewExecutable $view) {
-  // Do not add contextual link on view preview.
-  if (\Drupal::moduleHandler()->moduleExists('views_ui') && views_ui_contextual_links_suppress()) {
-    return;
-  }
-
-  // Allow to disable the contextual links.
-  if (!$view->display_handler->getOption('show_admin_links')) {
-    return;
-  }
-
-  // Get view display relationships.
-  $relationships = $view->relationship;
-  foreach ($relationships as $relationship) {
-    if ($relationship->field == 'entityqueue_relationship') {
-      $referenced_subqueues = (array) $relationship->options['limit_queue'];
-
-      // Contextual links can handle only one set of links coming from a module,
-      // so we'll have to settle for the first referenced queue.
-      if (!empty($referenced_subqueues) && ($subqueue = EntitySubqueue::load(reset($referenced_subqueues)))) {
-        $route_parameters = [
-          'entity_queue' => $subqueue->getQueue()->id(),
-          'entity_subqueue' => $subqueue->id(),
-        ];
-        $view->element['#contextual_links']['entityqueue'] = [
-          'route_parameters' => $route_parameters,
-        ];
-      }
-    }
-  }
-
-  // Add contextual link when view row plugin is entity_subqueue,
-  // and entity queue filter are provided.
-  if ($view->rowPlugin && $view->rowPlugin->getPluginId() == 'entity:entity_subqueue' && $view->rowPlugin->getEntityTypeId() == 'entity_subqueue') {
-    $view_filters = $view->filter;
-    foreach ($view_filters as $filter) {
-      if ($filter->field == 'queue' && !empty($filter->value)) {
-        $subqueue = EntitySubqueue::load(reset($filter->value));
-        if ($subqueue) {
-          $route_parameters = [
-            'entity_queue' => $subqueue->getQueue()->id(),
-            'entity_subqueue' => $subqueue->id(),
-          ];
-          $view->element['#contextual_links']['entityqueue'] = [
-            'route_parameters' => $route_parameters,
-          ];
-        }
-      }
-    }
-  }
+  \Drupal::service(EntityqueueHooks::class)->viewsPreRender($view);
 }
 
 /**
@@ -92,15 +37,9 @@ function entityqueue_views_pre_render(ViewExecutable $view) {
  *
  * Change Entityqueue on views into off-canvas links if available.
  */
+#[LegacyHook]
 function entityqueue_contextual_links_view_alter(&$element, $items) {
-  if (\Drupal::moduleHandler()->moduleExists('settings_tray') && isset($element['#links']['entityentity-subqueueedit-form'])) {
-    $element['#links']['entityentity-subqueueedit-form']['attributes'] = [
-      'class' => ['use-ajax'],
-      'data-dialog-type' => 'dialog',
-      'data-dialog-renderer' => 'off_canvas',
-      'data-settings-tray-edit' => TRUE,
-    ];
-  }
+  \Drupal::service(EntityqueueHooks::class)->contextualLinksViewAlter($element, $items);
 }
 
 /**
@@ -108,59 +47,15 @@ function entityqueue_contextual_links_view_alter(&$element, $items) {
  *
  * @todo Remove this when https://www.drupal.org/node/2723323 is fixed.
  */
+#[LegacyHook]
 function entityqueue_entity_delete(EntityInterface $entity) {
-  // Get all the entity queues referencing the targets entity type.
-  $queues = EntityQueue::loadMultipleByTargetType($entity->getEntityTypeId());
-  foreach ($queues as $queue) {
-    // Get all the subqueues which are referencing the deleted entity.
-    $query = \Drupal::entityQuery('entity_subqueue')
-      ->accessCheck(FALSE)
-      ->condition('queue', $queue->id())
-      ->condition('items', $entity->id());
-    $result = $query->execute();
-    $subqueues = EntitySubqueue::loadMultiple($result);
-
-    // Check if the entity is referenced in a subqueue.
-    foreach ($subqueues as $subqueue) {
-      if ($subqueue->hasItem($entity)) {
-        $subqueue->removeItem($entity)->save();
-      }
-    }
-  }
+  \Drupal::service(EntityqueueHooks::class)->entityDelete($entity);
 }
 
 /**
  * Implements hook_views_query_alter().
  */
+#[LegacyHook]
 function entityqueue_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
-  if (!\Drupal::moduleHandler()->moduleExists('workspaces')) {
-    return;
-  }
-
-  $joins = $view->getHandlers('relationship');
-  $join_ids = \array_keys($joins);
-  if (\in_array('entityqueue_relationship', $join_ids, TRUE)) {
-    $entity_type_id = 'entity_subqueue';
-    $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
-    $keys = $entity_type->getKeys();
-
-    $revision_table = 'entity_subqueue_revision';
-    /** @var \Drupal\Core\Database\Query\SelectInterface $subquery */
-    $subquery = $query->getConnection()->select($revision_table);
-    $subquery->addExpression("MAX($revision_table.{$keys['revision']})", $keys['revision']);
-    if (\Drupal::service('workspaces.manager')->hasActiveWorkspace()) {
-      $subquery->condition('workspace', \Drupal::service('workspaces.manager')->getActiveWorkspace()->id());
-    }
-    else {
-      $subquery->condition('revision_default', 1);
-    }
-
-    // The table joined in the relationship handler changes based on whether
-    // a workspaces is active or not, so use the corresponding table here too.
-    $query_table_aliases = \array_keys($query->getTableQueue());
-    $query_base_table = $query_table_aliases[1];
-    $group_id = $query->setWhereGroup('OR');
-    $query->addWhere($group_id, "$query_base_table.{$keys['revision']}", $subquery, 'IN');
-    $query->addWhere($group_id, "$query_base_table.{$keys['revision']}", NULL, "IS NULL");
-  }
+  \Drupal::service(EntityqueueHooks::class)->viewsQueryAlter($view, $query);
 }
diff --git a/entityqueue.post_update.php b/entityqueue.post_update.php
index 45cbb6d..e190450 100644
--- a/entityqueue.post_update.php
+++ b/entityqueue.post_update.php
@@ -5,6 +5,7 @@
  * Post update functions for Entityqueue.
  */
 
+use Drupal\Component\Utility\DeprecationHelper;
 use Drupal\Core\Config\Entity\ConfigEntityUpdater;
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
@@ -111,7 +112,7 @@ function entityqueue_post_update_remove_reverse_in_admin_setting() {
     $queue_settings['reverse'] = FALSE;
     $queue_config->set('queue_settings', $queue_settings);
 
-    $queue_config->save(TRUE);
+    DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => $queue_config->save(), fn() => $queue_config->save(TRUE));
   }
 }
 
diff --git a/entityqueue.services.yml b/entityqueue.services.yml
index a538c29..a78ef30 100644
--- a/entityqueue.services.yml
+++ b/entityqueue.services.yml
@@ -10,3 +10,11 @@ services:
   entityqueue.repository:
     class: Drupal\entityqueue\EntityQueueRepository
     arguments: ['@entity_type.manager']
+
+  Drupal\entityqueue\Hook\EntityqueueHooks:
+    class: Drupal\entityqueue\Hook\EntityqueueHooks
+    autowire: true
+
+  Drupal\entityqueue\Hook\EntityqueueViewsHooks:
+    class: Drupal\entityqueue\Hook\EntityqueueViewsHooks
+    autowire: true
diff --git a/entityqueue.views.inc b/entityqueue.views.inc
index 2d7bae5..2b481d8 100644
--- a/entityqueue.views.inc
+++ b/entityqueue.views.inc
@@ -5,123 +5,13 @@
  * Provide views data for the Entityqueue module.
  */
 
-use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
-use Drupal\entityqueue\Entity\EntityQueue;
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\entityqueue\Hook\EntityqueueViewsHooks;
 
 /**
  * Implements hook_views_data_alter().
  */
+#[LegacyHook]
 function entityqueue_views_data_alter(array &$data) {
-  $entity_type_manager = \Drupal::entityTypeManager();
-  $entity_subqueue = $entity_type_manager->getDefinition('entity_subqueue');
-
-  // Find all entity types that need an 'entityqueue' relationship.
-  $target_entity_type_ids = [];
-  $queues = EntityQueue::loadMultiple();
-  foreach ($queues as $queue) {
-    $target_entity_type_ids[$queue->getTargetEntityTypeId()] = TRUE;
-  }
-
-  // Filter entity types to those that have a 'views_data' handler and use a SQL
-  // storage.
-  /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
-  $entity_types = [];
-  foreach (array_keys($target_entity_type_ids) as $entity_type_id) {
-    $entity_type = $entity_type_manager->getDefinition($entity_type_id);
-    if ($entity_type->hasHandlerClass('views_data') && $entity_type_manager->getStorage($entity_type->id()) instanceof SqlEntityStorageInterface) {
-      $entity_types[$entity_type_id] = $entity_type;
-    }
-  }
-
-  foreach ($entity_types as $entity_type) {
-    $field_name = 'items';
-    $field_storage = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions('entity_subqueue')[$field_name];
-    $target_base_table = $entity_type->getDataTable() ?: $entity_type->getBaseTable();
-
-    /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
-    $table_mapping = $entity_type_manager->getStorage('entity_subqueue')->getTableMapping();
-    $columns = $table_mapping->getColumnNames($field_name);
-    $subqueue_items_table_name = $table_mapping->getDedicatedDataTableName($field_storage);
-
-    $data[$target_base_table]['entityqueue_relationship']['relationship'] = [
-      'id' => 'entity_queue',
-      'title' => t('@target_label queue', ['@target_label' => $entity_type->getLabel()]),
-      'label' => t('@target_label queue', ['@target_label' => $entity_type->getLabel()]),
-      'group' => t('Entityqueue'),
-      'help' => t('Create a relationship from @target_label to an entityqueue.', ['@target_label' => $entity_type->getLabel()]),
-      'base' => $entity_subqueue->getDataTable() ?: $entity_subqueue->getBaseTable(),
-      'entity_type' => 'entity_subqueue',
-      'base field' => $entity_subqueue->getKey('id'),
-      'field_name' => $field_storage->getName(),
-      'field table' => $subqueue_items_table_name,
-      'field field' => $columns['target_id'],
-    ];
-
-    $data[$target_base_table]['entityqueue_relationship_in_queue']['sort'] = [
-      'id' => 'entity_queue_in_queue',
-      'group' => t('Entityqueue'),
-      'title' => t('In @target_label queue', [
-        '@target_label' => $entity_type->getLabel(),
-      ]),
-      'label' => t('In @target_label queue', [
-        '@target_label' => $entity_type->getLabel(),
-      ]),
-      'help' => t('Filter to ensure a(n) @target_label IS or IS NOT in the related queue', [
-        '@target_label' => $entity_type->getLabel(),
-      ]),
-      'field' => 'delta',
-      'field table' => $subqueue_items_table_name,
-      'field_name' => $field_name,
-    ];
-
-    $data[$target_base_table]['entityqueue_relationship']['sort'] = [
-      'id' => 'entity_queue_position',
-      'group' => t('Entityqueue'),
-      'title' => t('@target_label Queue Position', [
-        '@target_label' => $entity_type->getLabel(),
-      ]),
-      'label' => t('@target_label Queue Position', [
-        '@target_label' => $entity_type->getLabel(),
-      ]),
-      'help' => t('Position of item in the @target_label queue.', [
-        '@target_label' => $entity_type->getLabel(),
-      ]),
-      'field' => 'delta',
-      'field table' => $subqueue_items_table_name,
-      'field_name' => $field_name,
-    ];
-
-    $data[$target_base_table]['entityqueue_relationship']['filter'] = [
-      'id' => 'entity_queue_in_queue',
-      'type' => 'yes-no',
-      'group' => t('Entityqueue'),
-      'title' => t('@target_label In Queue', [
-        '@target_label' => $entity_type->getLabel(),
-      ]),
-      'label' => t('@target_label In Queue', [
-        '@target_label' => $entity_type->getLabel(),
-      ]),
-      'help' => t('Filter for entities that are available or not in the @target_label entity queue.', [
-        '@target_label' => $entity_type->getLabel(),
-      ]),
-      'field table' => $subqueue_items_table_name,
-      'field field' => $columns['target_id'],
-    ];
-
-    $data[$target_base_table]['entityqueue_relationship_position']['field'] = [
-      'id' => 'entity_queue_position',
-      'group' => t('Entityqueue'),
-      'title' => t('@target_label Position In Queue', [
-        '@target_label' => $entity_type->getLabel(),
-      ]),
-      'label' => t('@target_label Position In Queue', [
-        '@target_label' => $entity_type->getLabel(),
-      ]),
-      'help' => t('Position of item in the @target_label queue.', [
-        '@target_label' => $entity_type->getLabel(),
-      ]),
-      'field' => 'delta',
-      'field table' => $subqueue_items_table_name,
-    ];
-  }
+  \Drupal::service(EntityqueueViewsHooks::class)->viewsDataAlter($data);
 }
diff --git a/modules/entityqueue_smartqueue/entityqueue_smartqueue.module b/modules/entityqueue_smartqueue/entityqueue_smartqueue.module
index f47b5d1..31b642d 100644
--- a/modules/entityqueue_smartqueue/entityqueue_smartqueue.module
+++ b/modules/entityqueue_smartqueue/entityqueue_smartqueue.module
@@ -5,120 +5,58 @@
  * Provides automated subqueues for each entity of a given entity type.
  */
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\entityqueue_smartqueue\Hook\EntityqueueSmartqueueHooks;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
-use Drupal\Core\Field\BaseFieldDefinition;
-use Drupal\Core\Field\FieldDefinition;
 use Drupal\Core\Routing\RouteMatchInterface;
-use Drupal\entityqueue\Entity\EntityQueue;
-use Drupal\entityqueue\Entity\EntitySubqueue;
 
 /**
  * Implements hook_help().
  */
+#[LegacyHook]
 function entityqueue_smartqueue_help($route_name, RouteMatchInterface $route_match) {
-  switch ($route_name) {
-    // Main module help for the entityqueue_smartqueue module.
-    case 'help.page.entityqueue_smartqueue':
-      $output = '';
-      $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('Entityqueue Smartqueue - Automated queues for each entity of a given entity type.') . '</p>';
-      return $output;
-  }
+  return \Drupal::service(EntityqueueSmartqueueHooks::class)->help($route_name, $route_match);
 }
 
 /**
  * Implements hook_entity_field_storage_info().
  */
+#[LegacyHook]
 function entityqueue_smartqueue_entity_field_storage_info(EntityTypeInterface $entity_type) {
-  if ($entity_type->id() === 'entity_subqueue') {
-    $field_storage_definitions['attached_entity'] = BaseFieldDefinition::create('entity_reference')
-      ->setName('attached_entity')
-      ->setTargetEntityTypeId('entity_subqueue')
-      ->setTargetBundle(NULL)
-      ->setLabel(t('Attached entity'))
-      // This setting is overridden per bundle (queue) in
-      // entityqueue_smartqueue_entity_bundle_field_info(), but we need to
-      // default to a target entity type that uses strings IDs, in order to
-      // allow both integers and strings to be stored by the default entity
-      // reference field storage.
-      ->setSetting('target_type', 'entity_subqueue');
-
-    return $field_storage_definitions;
-  }
+  return \Drupal::service(EntityqueueSmartqueueHooks::class)->entityFieldStorageInfo($entity_type);
 }
 
 /**
  * Implements hook_entity_bundle_field_info().
  */
+#[LegacyHook]
 function entityqueue_smartqueue_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
-  /** @var \Drupal\entityqueue\EntityQueueInterface $queue */
-  if ($entity_type->id() === 'entity_subqueue' && ($queue = EntityQueue::load($bundle)) && $queue->getHandler() === 'smartqueue') {
-    $field_storage_definitions = \Drupal::service('entity_field.manager')->getActiveFieldStorageDefinitions('entity_subqueue');
-
-    $field_definitions['attached_entity'] = FieldDefinition::createFromFieldStorageDefinition($field_storage_definitions['attached_entity']);
-    $field_definitions['attached_entity']->setTargetBundle($bundle);
-    $field_definitions['attached_entity']->setSetting('target_type', $queue->getHandlerConfiguration()['entity_type']);
-
-    return $field_definitions;
-  }
+  return \Drupal::service(EntityqueueSmartqueueHooks::class)->entityBundleFieldInfo($entity_type, $bundle, $base_field_definitions);
 }
 
 /**
  * Implements hook_entity_insert().
  */
+#[LegacyHook]
 function entityqueue_smartqueue_entity_insert(EntityInterface $entity) {
-  $queues = _entityqueue_smartqueue_get_queues($entity->getEntityTypeId());
-  foreach ($queues as $queue) {
-    // Check if the entity that got inserted is of the relevant bundle.
-    if (!in_array($entity->bundle(), $queue->getHandlerConfiguration()['bundles'], TRUE)) {
-      continue;
-    }
-
-    $subqueue = EntitySubqueue::create([
-      'queue' => $queue->id(),
-      'name' => $queue->id() . '__' . $entity->id(),
-      'title' => $entity->label(),
-      'langcode' => $queue->language()->getId(),
-      'attached_entity' => $entity,
-    ]);
-    $subqueue->save();
-  }
+  \Drupal::service(EntityqueueSmartqueueHooks::class)->entityInsert($entity);
 }
 
 /**
  * Implements hook_entity_update().
  */
+#[LegacyHook]
 function entityqueue_smartqueue_entity_update(EntityInterface $entity) {
-  $queues = _entityqueue_smartqueue_get_queues($entity->getEntityTypeId());
-  foreach ($queues as $queue) {
-    // Check if the entity that got updated is of the relevant bundle.
-    if (!in_array($entity->bundle(), $queue->getHandlerConfiguration()['bundles'], TRUE)) {
-      continue;
-    }
-
-    if ($subqueue = EntitySubqueue::load($queue->id() . '__' . $entity->id())) {
-      $subqueue->set('title', $entity->label());
-      $subqueue->save();
-    }
-  }
+  \Drupal::service(EntityqueueSmartqueueHooks::class)->entityUpdate($entity);
 }
 
 /**
  * Implements hook_entity_delete().
  */
+#[LegacyHook]
 function entityqueue_smartqueue_entity_delete(EntityInterface $entity) {
-  $queues = _entityqueue_smartqueue_get_queues($entity->getEntityTypeId());
-  foreach ($queues as $queue) {
-    // Check if the entity that got deleted is of the relevant bundle.
-    if (!in_array($entity->bundle(), $queue->getHandlerConfiguration()['bundles'], TRUE)) {
-      continue;
-    }
-
-    if ($subqueue = EntitySubqueue::load($queue->id() . '__' . $entity->id())) {
-      $subqueue->delete();
-    }
-  }
+  \Drupal::service(EntityqueueSmartqueueHooks::class)->entityDelete($entity);
 }
 
 /**
@@ -144,7 +82,7 @@ function _entityqueue_smartqueue_get_queues($entity_type_id) {
 /**
  * Implements hook_views_data_alter().
  */
+#[LegacyHook]
 function entityqueue_smartqueue_views_data_alter(&$data) {
-  $data['entity_subqueue']['name']['argument']['title'] = t('Entityqueue smartqueue name');
-  $data['entity_subqueue']['name']['argument']['id'] = 'entityqueue_smartqueue_name';
+  \Drupal::service(EntityqueueSmartqueueHooks::class)->viewsDataAlter($data);
 }
diff --git a/modules/entityqueue_smartqueue/entityqueue_smartqueue.services.yml b/modules/entityqueue_smartqueue/entityqueue_smartqueue.services.yml
new file mode 100644
index 0000000..0202561
--- /dev/null
+++ b/modules/entityqueue_smartqueue/entityqueue_smartqueue.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\entityqueue_smartqueue\Hook\EntityqueueSmartqueueHooks:
+    class: Drupal\entityqueue_smartqueue\Hook\EntityqueueSmartqueueHooks
+    autowire: true
diff --git a/modules/entityqueue_smartqueue/src/Hook/EntityqueueSmartqueueHooks.php b/modules/entityqueue_smartqueue/src/Hook/EntityqueueSmartqueueHooks.php
new file mode 100644
index 0000000..2becf44
--- /dev/null
+++ b/modules/entityqueue_smartqueue/src/Hook/EntityqueueSmartqueueHooks.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace Drupal\entityqueue_smartqueue\Hook;
+
+use Drupal\entityqueue\Entity\EntitySubqueue;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Field\FieldDefinition;
+use Drupal\entityqueue\Entity\EntityQueue;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Hook implementations for entityqueue_smartqueue.
+ */
+class EntityqueueSmartqueueHooks {
+  use StringTranslationTrait;
+
+  /**
+   * Implements hook_help().
+   */
+  #[Hook('help')]
+  public function help($route_name, RouteMatchInterface $route_match) {
+    switch ($route_name) {
+      // Main module help for the entityqueue_smartqueue module.
+      case 'help.page.entityqueue_smartqueue':
+        $output = '';
+        $output .= '<h3>' . $this->t('About') . '</h3>';
+        $output .= '<p>' . $this->t('Entityqueue Smartqueue - Automated queues for each entity of a given entity type.') . '</p>';
+        return $output;
+    }
+  }
+
+  /**
+   * Implements hook_entity_field_storage_info().
+   */
+  #[Hook('entity_field_storage_info')]
+  public function entityFieldStorageInfo(EntityTypeInterface $entity_type) {
+    if ($entity_type->id() === 'entity_subqueue') {
+      $field_storage_definitions['attached_entity'] = BaseFieldDefinition::create('entity_reference')->setName('attached_entity')->setTargetEntityTypeId('entity_subqueue')->setTargetBundle(NULL)->setLabel($this->t('Attached entity'))->setSetting('target_type', 'entity_subqueue');
+      return $field_storage_definitions;
+    }
+  }
+
+  /**
+   * Implements hook_entity_bundle_field_info().
+   */
+  #[Hook('entity_bundle_field_info')]
+  public static function entityBundleFieldInfo(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
+    /** @var \Drupal\entityqueue\EntityQueueInterface $queue */
+    if ($entity_type->id() === 'entity_subqueue' && ($queue = EntityQueue::load($bundle)) && $queue->getHandler() === 'smartqueue') {
+      $field_storage_definitions = \Drupal::service('entity_field.manager')->getActiveFieldStorageDefinitions('entity_subqueue');
+      $field_definitions['attached_entity'] = FieldDefinition::createFromFieldStorageDefinition($field_storage_definitions['attached_entity']);
+      $field_definitions['attached_entity']->setTargetBundle($bundle);
+      $field_definitions['attached_entity']->setSetting('target_type', $queue->getHandlerConfiguration()['entity_type']);
+      return $field_definitions;
+    }
+  }
+
+  /**
+   * Implements hook_entity_insert().
+   */
+  #[Hook('entity_insert')]
+  public static function entityInsert(EntityInterface $entity) {
+    $queues = _entityqueue_smartqueue_get_queues($entity->getEntityTypeId());
+    foreach ($queues as $queue) {
+      // Check if the entity that got inserted is of the relevant bundle.
+      if (!in_array($entity->bundle(), $queue->getHandlerConfiguration()['bundles'], TRUE)) {
+        continue;
+      }
+      $subqueue = EntitySubqueue::create([
+        'queue' => $queue->id(),
+        'name' => $queue->id() . '__' . $entity->id(),
+        'title' => $entity->label(),
+        'langcode' => $queue->language()->getId(),
+        'attached_entity' => $entity,
+      ]);
+      $subqueue->save();
+    }
+  }
+
+  /**
+   * Implements hook_entity_update().
+   */
+  #[Hook('entity_update')]
+  public static function entityUpdate(EntityInterface $entity) {
+    $queues = _entityqueue_smartqueue_get_queues($entity->getEntityTypeId());
+    foreach ($queues as $queue) {
+      // Check if the entity that got updated is of the relevant bundle.
+      if (!in_array($entity->bundle(), $queue->getHandlerConfiguration()['bundles'], TRUE)) {
+        continue;
+      }
+      if ($subqueue = EntitySubqueue::load($queue->id() . '__' . $entity->id())) {
+        $subqueue->set('title', $entity->label());
+        $subqueue->save();
+      }
+    }
+  }
+
+  /**
+   * Implements hook_entity_delete().
+   */
+  #[Hook('entity_delete')]
+  public static function entityDelete(EntityInterface $entity) {
+    $queues = _entityqueue_smartqueue_get_queues($entity->getEntityTypeId());
+    foreach ($queues as $queue) {
+      // Check if the entity that got deleted is of the relevant bundle.
+      if (!in_array($entity->bundle(), $queue->getHandlerConfiguration()['bundles'], TRUE)) {
+        continue;
+      }
+      if ($subqueue = EntitySubqueue::load($queue->id() . '__' . $entity->id())) {
+        $subqueue->delete();
+      }
+    }
+  }
+
+  /**
+   * Implements hook_views_data_alter().
+   */
+  #[Hook('views_data_alter')]
+  public function viewsDataAlter(&$data) {
+    $data['entity_subqueue']['name']['argument']['title'] = $this->t('Entityqueue smartqueue name');
+    $data['entity_subqueue']['name']['argument']['id'] = 'entityqueue_smartqueue_name';
+  }
+
+}
diff --git a/src/Hook/EntityqueueHooks.php b/src/Hook/EntityqueueHooks.php
new file mode 100644
index 0000000..433c317
--- /dev/null
+++ b/src/Hook/EntityqueueHooks.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace Drupal\entityqueue\Hook;
+
+use Drupal\views\Plugin\views\query\QueryPluginBase;
+use Drupal\entityqueue\Entity\EntityQueue;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\entityqueue\Entity\EntitySubqueue;
+use Drupal\views\ViewExecutable;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for entityqueue.
+ */
+class EntityqueueHooks {
+
+  /**
+   * Implements hook_entity_field_access().
+   */
+  #[Hook('entity_field_access')]
+  public static function entityFieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, ?FieldItemListInterface $items = NULL) {
+    // Only allow edit access on a subqueue title field if the queue doesn't have
+    // automated subqueues.
+    if ($operation == 'edit' && $field_definition->getName() == 'title' && $items && $items->getEntity()->getEntityTypeId() === 'entity_subqueue') {
+      /** @var \Drupal\entityqueue\EntityQueueInterface $queue */
+      $queue = $items->getEntity()->getQueue();
+      return AccessResult::forbiddenIf($queue->getHandlerPlugin()->hasAutomatedSubqueues());
+    }
+    return AccessResult::neutral();
+  }
+
+  /**
+   * Implements hook_views_pre_render().
+   *
+   * Add contextual links to views before rendering.
+   */
+  #[Hook('views_pre_render')]
+  public static function viewsPreRender(ViewExecutable $view) {
+    // Do not add contextual link on view preview.
+    if (\Drupal::moduleHandler()->moduleExists('views_ui') && views_ui_contextual_links_suppress()) {
+      return;
+    }
+    // Allow to disable the contextual links.
+    if (!$view->display_handler->getOption('show_admin_links')) {
+      return;
+    }
+    // Get view display relationships.
+    $relationships = $view->relationship;
+    foreach ($relationships as $relationship) {
+      if ($relationship->field == 'entityqueue_relationship') {
+        $referenced_subqueues = (array) $relationship->options['limit_queue'];
+        // Contextual links can handle only one set of links coming from a module,
+        // so we'll have to settle for the first referenced queue.
+        if (!empty($referenced_subqueues) && $subqueue = EntitySubqueue::load(reset($referenced_subqueues))) {
+          $route_parameters = [
+            'entity_queue' => $subqueue->getQueue()->id(),
+            'entity_subqueue' => $subqueue->id(),
+          ];
+          $view->element['#contextual_links']['entityqueue'] = [
+            'route_parameters' => $route_parameters,
+          ];
+        }
+      }
+    }
+    // Add contextual link when view row plugin is entity_subqueue,
+    // and entity queue filter are provided.
+    if ($view->rowPlugin && $view->rowPlugin->getPluginId() == 'entity:entity_subqueue' && $view->rowPlugin->getEntityTypeId() == 'entity_subqueue') {
+      $view_filters = $view->filter;
+      foreach ($view_filters as $filter) {
+        if ($filter->field == 'queue' && !empty($filter->value)) {
+          $subqueue = EntitySubqueue::load(reset($filter->value));
+          if ($subqueue) {
+            $route_parameters = [
+              'entity_queue' => $subqueue->getQueue()->id(),
+              'entity_subqueue' => $subqueue->id(),
+            ];
+            $view->element['#contextual_links']['entityqueue'] = [
+              'route_parameters' => $route_parameters,
+            ];
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Implements hook_contextual_links_view_alter().
+   *
+   * Change Entityqueue on views into off-canvas links if available.
+   */
+  #[Hook('contextual_links_view_alter')]
+  public static function contextualLinksViewAlter(&$element, $items) {
+    if (\Drupal::moduleHandler()->moduleExists('settings_tray') && isset($element['#links']['entityentity-subqueueedit-form'])) {
+      $element['#links']['entityentity-subqueueedit-form']['attributes'] = [
+        'class' => [
+          'use-ajax',
+        ],
+        'data-dialog-type' => 'dialog',
+        'data-dialog-renderer' => 'off_canvas',
+        'data-settings-tray-edit' => TRUE,
+      ];
+    }
+  }
+
+  /**
+   * Implements hook_entity_delete().
+   *
+   * @todo Remove this when https://www.drupal.org/node/2723323 is fixed.
+   */
+  #[Hook('entity_delete')]
+  public static function entityDelete(EntityInterface $entity) {
+    // Get all the entity queues referencing the targets entity type.
+    $queues = EntityQueue::loadMultipleByTargetType($entity->getEntityTypeId());
+    foreach ($queues as $queue) {
+      // Get all the subqueues which are referencing the deleted entity.
+      $query = \Drupal::entityQuery('entity_subqueue')->accessCheck(FALSE)->condition('queue', $queue->id())->condition('items', $entity->id());
+      $result = $query->execute();
+      $subqueues = EntitySubqueue::loadMultiple($result);
+      // Check if the entity is referenced in a subqueue.
+      foreach ($subqueues as $subqueue) {
+        if ($subqueue->hasItem($entity)) {
+          $subqueue->removeItem($entity)->save();
+        }
+      }
+    }
+  }
+
+  /**
+   * Implements hook_views_query_alter().
+   */
+  #[Hook('views_query_alter')]
+  public static function viewsQueryAlter(ViewExecutable $view, QueryPluginBase $query) {
+    if (!\Drupal::moduleHandler()->moduleExists('workspaces')) {
+      return;
+    }
+    $joins = $view->getHandlers('relationship');
+    $join_ids = array_keys($joins);
+    if (in_array('entityqueue_relationship', $join_ids, TRUE)) {
+      $entity_type_id = 'entity_subqueue';
+      $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
+      $keys = $entity_type->getKeys();
+      $revision_table = 'entity_subqueue_revision';
+      /** @var \Drupal\Core\Database\Query\SelectInterface $subquery */
+      $subquery = $query->getConnection()->select($revision_table);
+      $subquery->addExpression("MAX({$revision_table}.{$keys['revision']})", $keys['revision']);
+      if (\Drupal::service('workspaces.manager')->hasActiveWorkspace()) {
+        $subquery->condition('workspace', \Drupal::service('workspaces.manager')->getActiveWorkspace()->id());
+      }
+      else {
+        $subquery->condition('revision_default', 1);
+      }
+      // The table joined in the relationship handler changes based on whether
+      // a workspaces is active or not, so use the corresponding table here too.
+      $query_table_aliases = array_keys($query->getTableQueue());
+      $query_base_table = $query_table_aliases[1];
+      $group_id = $query->setWhereGroup('OR');
+      $query->addWhere($group_id, "{$query_base_table}.{$keys['revision']}", $subquery, 'IN');
+      $query->addWhere($group_id, "{$query_base_table}.{$keys['revision']}", NULL, "IS NULL");
+    }
+  }
+
+}
diff --git a/src/Hook/EntityqueueViewsHooks.php b/src/Hook/EntityqueueViewsHooks.php
new file mode 100644
index 0000000..d7167a6
--- /dev/null
+++ b/src/Hook/EntityqueueViewsHooks.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace Drupal\entityqueue\Hook;
+
+use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
+use Drupal\entityqueue\Entity\EntityQueue;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Hook implementations for entityqueue.
+ */
+class EntityqueueViewsHooks {
+  use StringTranslationTrait;
+
+  /**
+   * Implements hook_views_data_alter().
+   */
+  #[Hook('views_data_alter')]
+  public function viewsDataAlter(array &$data) {
+    $entity_type_manager = \Drupal::entityTypeManager();
+    $entity_subqueue = $entity_type_manager->getDefinition('entity_subqueue');
+    // Find all entity types that need an 'entityqueue' relationship.
+    $target_entity_type_ids = [];
+    $queues = EntityQueue::loadMultiple();
+    foreach ($queues as $queue) {
+      $target_entity_type_ids[$queue->getTargetEntityTypeId()] = TRUE;
+    }
+    // Filter entity types to those that have a 'views_data' handler and use a SQL
+    // storage.
+    /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
+    $entity_types = [];
+    foreach (array_keys($target_entity_type_ids) as $entity_type_id) {
+      $entity_type = $entity_type_manager->getDefinition($entity_type_id);
+      if ($entity_type->hasHandlerClass('views_data') && $entity_type_manager->getStorage($entity_type->id()) instanceof SqlEntityStorageInterface) {
+        $entity_types[$entity_type_id] = $entity_type;
+      }
+    }
+    foreach ($entity_types as $entity_type) {
+      $field_name = 'items';
+      $field_storage = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions('entity_subqueue')[$field_name];
+      $target_base_table = $entity_type->getDataTable() ?: $entity_type->getBaseTable();
+      /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
+      $table_mapping = $entity_type_manager->getStorage('entity_subqueue')->getTableMapping();
+      $columns = $table_mapping->getColumnNames($field_name);
+      $subqueue_items_table_name = $table_mapping->getDedicatedDataTableName($field_storage);
+      $data[$target_base_table]['entityqueue_relationship']['relationship'] = [
+        'id' => 'entity_queue',
+        'title' => $this->t('@target_label queue', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'label' => $this->t('@target_label queue', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'group' => $this->t('Entityqueue'),
+        'help' => $this->t('Create a relationship from @target_label to an entityqueue.', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'base' => $entity_subqueue->getDataTable() ?: $entity_subqueue->getBaseTable(),
+        'entity_type' => 'entity_subqueue',
+        'base field' => $entity_subqueue->getKey('id'),
+        'field_name' => $field_storage->getName(),
+        'field table' => $subqueue_items_table_name,
+        'field field' => $columns['target_id'],
+      ];
+      $data[$target_base_table]['entityqueue_relationship_in_queue']['sort'] = [
+        'id' => 'entity_queue_in_queue',
+        'group' => $this->t('Entityqueue'),
+        'title' => $this->t('In @target_label queue', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'label' => $this->t('In @target_label queue', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'help' => $this->t('Filter to ensure a(n) @target_label IS or IS NOT in the related queue', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'field' => 'delta',
+        'field table' => $subqueue_items_table_name,
+        'field_name' => $field_name,
+      ];
+      $data[$target_base_table]['entityqueue_relationship']['sort'] = [
+        'id' => 'entity_queue_position',
+        'group' => $this->t('Entityqueue'),
+        'title' => $this->t('@target_label Queue Position', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'label' => $this->t('@target_label Queue Position', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'help' => $this->t('Position of item in the @target_label queue.', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'field' => 'delta',
+        'field table' => $subqueue_items_table_name,
+        'field_name' => $field_name,
+      ];
+      $data[$target_base_table]['entityqueue_relationship']['filter'] = [
+        'id' => 'entity_queue_in_queue',
+        'type' => 'yes-no',
+        'group' => $this->t('Entityqueue'),
+        'title' => $this->t('@target_label In Queue', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'label' => $this->t('@target_label In Queue', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'help' => $this->t('Filter for entities that are available or not in the @target_label entity queue.', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'field table' => $subqueue_items_table_name,
+        'field field' => $columns['target_id'],
+      ];
+      $data[$target_base_table]['entityqueue_relationship_position']['field'] = [
+        'id' => 'entity_queue_position',
+        'group' => $this->t('Entityqueue'),
+        'title' => $this->t('@target_label Position In Queue', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'label' => $this->t('@target_label Position In Queue', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'help' => $this->t('Position of item in the @target_label queue.', [
+          '@target_label' => $entity_type->getLabel(),
+        ]),
+        'field' => 'delta',
+        'field table' => $subqueue_items_table_name,
+      ];
+    }
+  }
+
+}
diff --git a/src/Plugin/EntityQueueHandler/Simple.php b/src/Plugin/EntityQueueHandler/Simple.php
index ae5e8ca..a14b09d 100644
--- a/src/Plugin/EntityQueueHandler/Simple.php
+++ b/src/Plugin/EntityQueueHandler/Simple.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\entityqueue\Plugin\EntityQueueHandler;
 
+use Drupal\Component\Utility\DeprecationHelper;
 use Drupal\Core\Entity\EntityRepositoryInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
@@ -117,7 +118,7 @@ class Simple extends EntityQueueHandlerBase implements ContainerFactoryPluginInt
     ];
 
     // Add a 'Translate' operation if translation is enabled for this queue.
-    if ($this->moduleHandler->moduleExists('content_translation') && content_translation_translate_access($subqueue)->isAllowed()) {
+    if ($this->moduleHandler->moduleExists('content_translation') && DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('content_translation.manager')->access($subqueue), fn() => content_translation_translate_access($subqueue))->isAllowed()) {
       $operations['translate_subqueue'] = [
         'title' => $this->t('Translate subqueue'),
         'url' => $subqueue->toUrl('drupal:content-translation-overview'),
