From 492f6ff29285d26c6731de5815f98b631bfab88e Mon Sep 17 00:00:00 2001
From: catch <6915-catch@users.noreply.drupalcode.org>
Date: Tue, 31 Dec 2024 20:55:39 +0000
Subject: [PATCH] Issue #3488093 by amateescu, nicxvan, longwave: Clean up hook
 implementations in the Workspaces module

---
 core/.phpstan-baseline.php                    | 186 ----------------
 core/modules/workspaces/src/EntityAccess.php  | 152 -------------
 .../workspaces/src/Hook/EntityAccess.php      |  87 ++++++++
 .../src/{ => Hook}/EntityOperations.php       | 198 +++++++----------
 .../src/{ => Hook}/EntityTypeInfo.php         |  77 +++----
 .../src/{ => Hook}/FormOperations.php         |  56 +----
 .../ViewsOperations.php}                      | 139 +++---------
 .../workspaces/src/Hook/WorkspacesHooks.php   | 208 +++---------------
 .../WorkspaceContentTranslationTest.php       |   2 +-
 .../Kernel/WorkspaceViewsIntegrationTest.php  |   4 +-
 core/modules/workspaces/workspaces.module     |   4 +-
 11 files changed, 271 insertions(+), 842 deletions(-)
 delete mode 100644 core/modules/workspaces/src/EntityAccess.php
 create mode 100644 core/modules/workspaces/src/Hook/EntityAccess.php
 rename core/modules/workspaces/src/{ => Hook}/EntityOperations.php (70%)
 rename core/modules/workspaces/src/{ => Hook}/EntityTypeInfo.php (65%)
 rename core/modules/workspaces/src/{ => Hook}/FormOperations.php (60%)
 rename core/modules/workspaces/src/{ViewsQueryAlter.php => Hook/ViewsOperations.php} (78%)

diff --git a/core/.phpstan-baseline.php b/core/.phpstan-baseline.php
index 1b3583fcddff..aa2be3f4acca 100644
--- a/core/.phpstan-baseline.php
+++ b/core/.phpstan-baseline.php
@@ -53337,60 +53337,6 @@
 	'count' => 1,
 	'path' => __DIR__ . '/modules/workspaces/src/Entity/Workspace.php',
 ];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityAccess\\:\\:create\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityAccess.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:create\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityFormAlter\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityFormEntityBuild\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityInsert\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityPredelete\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityPreload\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityPresave\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityOperations\\:\\:entityUpdate\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityOperations.php',
-];
 $ignoreErrors[] = [
 	'message' => '#^Method Drupal\\\\workspaces\\\\EntityQuery\\\\Query\\:\\:traitPrepare\\(\\) has no return type specified\\.$#',
 	'identifier' => 'missingType.return',
@@ -53403,36 +53349,6 @@
 	'count' => 1,
 	'path' => __DIR__ . '/modules/workspaces/src/EntityQuery/QueryAggregate.php',
 ];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityTypeInfo\\:\\:create\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityTypeInfo.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityTypeInfo\\:\\:entityBaseFieldInfo\\(\\) should return array\\<Drupal\\\\Core\\\\Field\\\\FieldDefinitionInterface\\> but return statement is missing\\.$#',
-	'identifier' => 'return.missing',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityTypeInfo.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityTypeInfo\\:\\:entityTypeAlter\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityTypeInfo.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityTypeInfo\\:\\:entityTypeBuild\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityTypeInfo.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\EntityTypeInfo\\:\\:fieldInfoAlter\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/EntityTypeInfo.php',
-];
 $ignoreErrors[] = [
 	'message' => '#^Method Drupal\\\\workspaces\\\\EventSubscriber\\\\EntitySchemaSubscriber\\:\\:addRevisionMetadataField\\(\\) has no return type specified\\.$#',
 	'identifier' => 'missingType.return',
@@ -53577,84 +53493,6 @@
 	'count' => 1,
 	'path' => __DIR__ . '/modules/workspaces/src/Form/WorkspaceSwitcherForm.php',
 ];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\FormOperations\\:\\:addWorkspaceValidation\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/FormOperations.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\FormOperations\\:\\:create\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/FormOperations.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\FormOperations\\:\\:formAlter\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/FormOperations.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\FormOperations\\:\\:validateDefaultWorkspace\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/FormOperations.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityBaseFieldInfo\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityDelete\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityInsert\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityPredelete\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityPresave\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityRevisionDelete\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:entityUpdate\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:help\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\Hook\\\\WorkspacesHooks\\:\\:menuLinkContentUpdate\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/Hook/WorkspacesHooks.php',
-];
 $ignoreErrors[] = [
 	'message' => '#^Method Drupal\\\\workspaces\\\\Negotiator\\\\SessionWorkspaceNegotiator\\:\\:getActiveWorkspace\\(\\) has no return type specified\\.$#',
 	'identifier' => 'missingType.return',
@@ -53715,30 +53553,6 @@
 	'count' => 1,
 	'path' => __DIR__ . '/modules/workspaces/src/Plugin/Validation/Constraint/EntityWorkspaceConflictConstraintValidator.php',
 ];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\ViewsQueryAlter\\:\\:alterQuery\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/ViewsQueryAlter.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\ViewsQueryAlter\\:\\:alterQueryForEntityType\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/ViewsQueryAlter.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\ViewsQueryAlter\\:\\:create\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/ViewsQueryAlter.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\workspaces\\\\ViewsQueryAlter\\:\\:moveEntityTable\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/modules/workspaces/src/ViewsQueryAlter.php',
-];
 $ignoreErrors[] = [
 	'message' => '#^Method Drupal\\\\workspaces\\\\WorkspaceAssociation\\:\\:deleteAssociations\\(\\) has no return type specified\\.$#',
 	'identifier' => 'missingType.return',
diff --git a/core/modules/workspaces/src/EntityAccess.php b/core/modules/workspaces/src/EntityAccess.php
deleted file mode 100644
index baef5bcc4dd4..000000000000
--- a/core/modules/workspaces/src/EntityAccess.php
+++ /dev/null
@@ -1,152 +0,0 @@
-<?php
-
-namespace Drupal\workspaces;
-
-use Drupal\Core\Access\AccessResult;
-use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Service wrapper for hooks relating to entity access control.
- *
- * @internal
- */
-class EntityAccess implements ContainerInjectionInterface {
-
-  use StringTranslationTrait;
-
-  /**
-   * The entity type manager service.
-   *
-   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
-   */
-  protected $entityTypeManager;
-
-  /**
-   * The workspace manager service.
-   *
-   * @var \Drupal\workspaces\WorkspaceManagerInterface
-   */
-  protected $workspaceManager;
-
-  /**
-   * The workspace information service.
-   *
-   * @var \Drupal\workspaces\WorkspaceInformationInterface
-   */
-  protected $workspaceInfo;
-
-  /**
-   * Constructs a new EntityAccess instance.
-   *
-   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
-   *   The entity type manager service.
-   * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager
-   *   The workspace manager service.
-   * @param \Drupal\workspaces\WorkspaceInformationInterface $workspace_information
-   *   The workspace information service.
-   */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager, WorkspaceInformationInterface $workspace_information) {
-    $this->entityTypeManager = $entity_type_manager;
-    $this->workspaceManager = $workspace_manager;
-    $this->workspaceInfo = $workspace_information;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity_type.manager'),
-      $container->get('workspaces.manager'),
-      $container->get('workspaces.information')
-    );
-  }
-
-  /**
-   * Implements a hook bridge for hook_entity_access().
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity to check access for.
-   * @param string $operation
-   *   The operation being performed.
-   * @param \Drupal\Core\Session\AccountInterface $account
-   *   The user account making the to check access for.
-   *
-   * @return \Drupal\Core\Access\AccessResult
-   *   The result of the access check.
-   *
-   * @see hook_entity_access()
-   */
-  public function entityOperationAccess(EntityInterface $entity, $operation, AccountInterface $account) {
-    // Workspaces themselves are handled by their own access handler and we
-    // should not try to do any access checks for entity types that can not
-    // belong to a workspace.
-    if (!$this->workspaceInfo->isEntitySupported($entity) || !$this->workspaceManager->hasActiveWorkspace()) {
-      return AccessResult::neutral();
-    }
-
-    // Prevent the deletion of entities with a published default revision.
-    if ($operation === 'delete') {
-      $active_workspace = $this->workspaceManager->getActiveWorkspace();
-      $is_deletable = $this->workspaceInfo->isEntityDeletable($entity, $active_workspace);
-
-      return AccessResult::forbiddenIf(!$is_deletable)
-        ->addCacheableDependency($entity)
-        ->addCacheableDependency($active_workspace);
-    }
-
-    return $this->bypassAccessResult($account);
-  }
-
-  /**
-   * Implements a hook bridge for hook_entity_create_access().
-   *
-   * @param \Drupal\Core\Session\AccountInterface $account
-   *   The user account making the to check access for.
-   * @param array $context
-   *   The context of the access check.
-   * @param string $entity_bundle
-   *   The bundle of the entity.
-   *
-   * @return \Drupal\Core\Access\AccessResult
-   *   The result of the access check.
-   *
-   * @see hook_entity_create_access()
-   */
-  public function entityCreateAccess(AccountInterface $account, array $context, $entity_bundle) {
-    // Workspaces themselves are handled by their own access handler and we
-    // should not try to do any access checks for entity types that can not
-    // belong to a workspace.
-    $entity_type = $this->entityTypeManager->getDefinition($context['entity_type_id']);
-    if (!$this->workspaceInfo->isEntityTypeSupported($entity_type) || !$this->workspaceManager->hasActiveWorkspace()) {
-      return AccessResult::neutral();
-    }
-
-    return $this->bypassAccessResult($account);
-  }
-
-  /**
-   * Checks the 'bypass' permissions.
-   *
-   * @param \Drupal\Core\Session\AccountInterface $account
-   *   The user account making the to check access for.
-   *
-   * @return \Drupal\Core\Access\AccessResult
-   *   The result of the access check.
-   */
-  protected function bypassAccessResult(AccountInterface $account) {
-    // This approach assumes that the current "global" active workspace is
-    // correct, i.e. if you're "in" a given workspace then you get ALL THE PERMS
-    // to ALL THE THINGS! That's why this is a dangerous permission.
-    $active_workspace = $this->workspaceManager->getActiveWorkspace();
-
-    return AccessResult::allowedIf($active_workspace->getOwnerId() == $account->id())->cachePerUser()->addCacheableDependency($active_workspace)
-      ->andIf(AccessResult::allowedIfHasPermission($account, 'bypass entity access own workspace'));
-  }
-
-}
diff --git a/core/modules/workspaces/src/Hook/EntityAccess.php b/core/modules/workspaces/src/Hook/EntityAccess.php
new file mode 100644
index 000000000000..f62839adc26b
--- /dev/null
+++ b/core/modules/workspaces/src/Hook/EntityAccess.php
@@ -0,0 +1,87 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\workspaces\Hook;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Access\AccessResultInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\workspaces\WorkspaceInformationInterface;
+use Drupal\workspaces\WorkspaceManagerInterface;
+
+/**
+ * Defines a class for reacting to entity access control hooks.
+ */
+class EntityAccess {
+
+  public function __construct(
+    protected EntityTypeManagerInterface $entityTypeManager,
+    protected WorkspaceManagerInterface $workspaceManager,
+    protected WorkspaceInformationInterface $workspaceInfo,
+  ) {}
+
+  /**
+   * Implements hook_entity_access().
+   */
+  #[Hook('entity_access')]
+  public function entityAccess(EntityInterface $entity, $operation, AccountInterface $account): AccessResultInterface {
+    // Workspaces themselves are handled by their own access handler and we
+    // should not try to do any access checks for entity types that can not
+    // belong to a workspace.
+    if (!$this->workspaceInfo->isEntitySupported($entity) || !$this->workspaceManager->hasActiveWorkspace()) {
+      return AccessResult::neutral();
+    }
+
+    // Prevent the deletion of entities with a published default revision.
+    if ($operation === 'delete') {
+      $active_workspace = $this->workspaceManager->getActiveWorkspace();
+      $is_deletable = $this->workspaceInfo->isEntityDeletable($entity, $active_workspace);
+
+      return AccessResult::forbiddenIf(!$is_deletable)
+        ->addCacheableDependency($entity)
+        ->addCacheableDependency($active_workspace);
+    }
+
+    return $this->bypassAccessResult($account);
+  }
+
+  /**
+   * Implements hook_entity_create_access().
+   */
+  #[Hook('entity_create_access')]
+  public function entityCreateAccess(AccountInterface $account, array $context, $entity_bundle): AccessResultInterface {
+    // Workspaces themselves are handled by their own access handler and we
+    // should not try to do any access checks for entity types that can not
+    // belong to a workspace.
+    $entity_type = $this->entityTypeManager->getDefinition($context['entity_type_id']);
+    if (!$this->workspaceInfo->isEntityTypeSupported($entity_type) || !$this->workspaceManager->hasActiveWorkspace()) {
+      return AccessResult::neutral();
+    }
+
+    return $this->bypassAccessResult($account);
+  }
+
+  /**
+   * Checks the 'bypass' permissions.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The user account making the to check access for.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   *   The result of the access check.
+   */
+  protected function bypassAccessResult(AccountInterface $account): AccessResultInterface {
+    // This approach assumes that the current "global" active workspace is
+    // correct, i.e. if you're "in" a given workspace then you get ALL THE PERMS
+    // to ALL THE THINGS! That's why this is a dangerous permission.
+    $active_workspace = $this->workspaceManager->getActiveWorkspace();
+
+    return AccessResult::allowedIf($active_workspace->getOwnerId() == $account->id())->cachePerUser()->addCacheableDependency($active_workspace)
+      ->andIf(AccessResult::allowedIfHasPermission($account, 'bypass entity access own workspace'));
+  }
+
+}
diff --git a/core/modules/workspaces/src/EntityOperations.php b/core/modules/workspaces/src/Hook/EntityOperations.php
similarity index 70%
rename from core/modules/workspaces/src/EntityOperations.php
rename to core/modules/workspaces/src/Hook/EntityOperations.php
index 588520bc833a..9c690d6ff9e2 100644
--- a/core/modules/workspaces/src/EntityOperations.php
+++ b/core/modules/workspaces/src/Hook/EntityOperations.php
@@ -1,90 +1,39 @@
 <?php
 
-namespace Drupal\workspaces;
+declare(strict_types=1);
 
-use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+namespace Drupal\workspaces\Hook;
+
+use Drupal\Core\Entity\EntityFormInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityPublishedInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Entity\RevisionableInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\workspaces\WorkspaceAssociationInterface;
+use Drupal\workspaces\WorkspaceInformationInterface;
+use Drupal\workspaces\WorkspaceManagerInterface;
+use Drupal\workspaces\WorkspaceRepositoryInterface;
 
 /**
- * Defines a class for reacting to entity events.
- *
- * @internal
+ * Defines a class for reacting to entity runtime hooks.
  */
-class EntityOperations implements ContainerInjectionInterface {
-
-  use StringTranslationTrait;
-
-  /**
-   * The entity type manager service.
-   *
-   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
-   */
-  protected $entityTypeManager;
-
-  /**
-   * The workspace manager service.
-   *
-   * @var \Drupal\workspaces\WorkspaceManagerInterface
-   */
-  protected $workspaceManager;
-
-  /**
-   * The workspace association service.
-   *
-   * @var \Drupal\workspaces\WorkspaceAssociationInterface
-   */
-  protected $workspaceAssociation;
-
-  /**
-   * The workspace information service.
-   *
-   * @var \Drupal\workspaces\WorkspaceInformationInterface
-   */
-  protected $workspaceInfo;
-
-  /**
-   * Constructs a new EntityOperations instance.
-   *
-   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
-   *   The entity type manager service.
-   * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager
-   *   The workspace manager service.
-   * @param \Drupal\workspaces\WorkspaceAssociationInterface $workspace_association
-   *   The workspace association service.
-   * @param \Drupal\workspaces\WorkspaceInformationInterface $workspace_information
-   *   The workspace information service.
-   */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager, WorkspaceAssociationInterface $workspace_association, WorkspaceInformationInterface $workspace_information) {
-    $this->entityTypeManager = $entity_type_manager;
-    $this->workspaceManager = $workspace_manager;
-    $this->workspaceAssociation = $workspace_association;
-    $this->workspaceInfo = $workspace_information;
-  }
+class EntityOperations {
 
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity_type.manager'),
-      $container->get('workspaces.manager'),
-      $container->get('workspaces.association'),
-      $container->get('workspaces.information')
-    );
-  }
+  public function __construct(
+    protected EntityTypeManagerInterface $entityTypeManager,
+    protected WorkspaceManagerInterface $workspaceManager,
+    protected WorkspaceAssociationInterface $workspaceAssociation,
+    protected WorkspaceInformationInterface $workspaceInfo,
+    protected WorkspaceRepositoryInterface $workspaceRepository,
+  ) {}
 
   /**
-   * Acts on entity IDs before they are loaded.
-   *
-   * @see hook_entity_preload()
+   * Implements hook_entity_preload().
    */
-  public function entityPreload(array $ids, $entity_type_id) {
+  #[Hook('entity_preload')]
+  public function entityPreload(array $ids, string $entity_type_id): array {
     $entities = [];
 
     $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
@@ -115,14 +64,10 @@ public function entityPreload(array $ids, $entity_type_id) {
   }
 
   /**
-   * Acts on an entity before it is created or updated.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity being saved.
-   *
-   * @see hook_entity_presave()
+   * Implements hook_entity_presave().
    */
-  public function entityPresave(EntityInterface $entity) {
+  #[Hook('entity_presave')]
+  public function entityPresave(EntityInterface $entity): void {
     if ($this->shouldSkipOperations($entity)) {
       return;
     }
@@ -178,14 +123,15 @@ public function entityPresave(EntityInterface $entity) {
   }
 
   /**
-   * Responds to the creation of a new entity.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity that was just saved.
-   *
-   * @see hook_entity_insert()
+   * Implements hook_entity_insert().
    */
-  public function entityInsert(EntityInterface $entity) {
+  #[Hook('entity_insert')]
+  public function entityInsert(EntityInterface $entity): void {
+    if ($entity->getEntityTypeId() === 'workspace') {
+      $this->workspaceAssociation->workspaceInsert($entity);
+      $this->workspaceRepository->resetCache();
+    }
+
     if ($this->shouldSkipOperations($entity) || !$this->workspaceInfo->isEntitySupported($entity)) {
       return;
     }
@@ -215,14 +161,14 @@ public function entityInsert(EntityInterface $entity) {
   }
 
   /**
-   * Responds to updates to an entity.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity that was just saved.
-   *
-   * @see hook_entity_update()
+   * Implements hook_entity_update().
    */
-  public function entityUpdate(EntityInterface $entity) {
+  #[Hook('entity_update')]
+  public function entityUpdate(EntityInterface $entity): void {
+    if ($entity->getEntityTypeId() === 'workspace') {
+      $this->workspaceRepository->resetCache();
+    }
+
     if ($this->shouldSkipOperations($entity) || !$this->workspaceInfo->isEntitySupported($entity)) {
       return;
     }
@@ -235,13 +181,9 @@ public function entityUpdate(EntityInterface $entity) {
   }
 
   /**
-   * Acts after an entity translation has been added.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $translation
-   *   The translation that was added.
-   *
-   * @see hook_entity_translation_insert()
+   * Implements hook_entity_translation_insert().
    */
+  #[Hook('entity_translation_insert')]
   public function entityTranslationInsert(EntityInterface $translation): void {
     if ($this->shouldSkipOperations($translation)
       || !$this->workspaceInfo->isEntitySupported($translation)
@@ -269,14 +211,14 @@ public function entityTranslationInsert(EntityInterface $translation): void {
   }
 
   /**
-   * Acts on an entity before it is deleted.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity being deleted.
-   *
-   * @see hook_entity_predelete()
+   * Implements hook_entity_predelete().
    */
-  public function entityPredelete(EntityInterface $entity) {
+  #[Hook('entity_predelete')]
+  public function entityPredelete(EntityInterface $entity): void {
+    if ($entity->getEntityTypeId() === 'workspace') {
+      $this->workspaceRepository->resetCache();
+    }
+
     if ($this->shouldSkipOperations($entity)) {
       return;
     }
@@ -291,18 +233,36 @@ public function entityPredelete(EntityInterface $entity) {
   }
 
   /**
-   * Alters entity forms to disallow concurrent editing in multiple workspaces.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   * @param string $form_id
-   *   The form ID.
+   * Implements hook_entity_delete().
+   */
+  #[Hook('entity_delete')]
+  public function entityDelete(EntityInterface $entity): void {
+    if ($this->workspaceInfo->isEntityTypeSupported($entity->getEntityType())) {
+      $this->workspaceAssociation->deleteAssociations(NULL, $entity->getEntityTypeId(), [$entity->id()]);
+    }
+  }
+
+  /**
+   * Implements hook_entity_revision_delete().
+   */
+  #[Hook('entity_revision_delete')]
+  public function entityRevisionDelete(EntityInterface $entity): void {
+    if ($this->workspaceInfo->isEntityTypeSupported($entity->getEntityType())) {
+      $this->workspaceAssociation->deleteAssociations(NULL, $entity->getEntityTypeId(), [$entity->id()], [$entity->getRevisionId()]);
+    }
+  }
+
+  /**
+   * Implements hook_form_alter().
    *
-   * @see hook_form_alter()
+   * Alters entity forms to disallow concurrent editing in multiple workspaces.
    */
-  public function entityFormAlter(array &$form, FormStateInterface $form_state, $form_id) {
+  #[Hook('form_alter')]
+  public function entityFormAlter(array &$form, FormStateInterface $form_state, string $form_id): void {
+    if (!$form_state->getFormObject() instanceof EntityFormInterface) {
+      return;
+    }
+
     $entity = $form_state->getFormObject()->getEntity();
     if (!$this->workspaceInfo->isEntitySupported($entity) && !$this->workspaceInfo->isEntityIgnored($entity)) {
       return;
@@ -310,7 +270,7 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, $f
 
     // For supported and ignored entity types, signal the fact that this form is
     // safe to use in a workspace.
-    // @see \Drupal\workspaces\FormOperations::validateForm()
+    // @see \Drupal\workspaces\Hook\FormOperations::formAlter()
     $form_state->set('workspace_safe', TRUE);
 
     // There is nothing more to do for ignored entity types.
@@ -331,7 +291,7 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, $f
   /**
    * Entity builder that marks all supported entities as pending revisions.
    */
-  public static function entityFormEntityBuild($entity_type_id, RevisionableInterface $entity, &$form, FormStateInterface &$form_state) {
+  public static function entityFormEntityBuild(string $entity_type_id, RevisionableInterface $entity, array &$form, FormStateInterface &$form_state): void {
     // Ensure that all entity forms are signaling that a new revision will be
     // created.
     $entity->setNewRevision(TRUE);
@@ -350,7 +310,7 @@ public static function entityFormEntityBuild($entity_type_id, RevisionableInterf
    * @return bool
    *   Returns TRUE if entity operations should not be altered, FALSE otherwise.
    */
-  protected function shouldSkipOperations(EntityInterface $entity) {
+  protected function shouldSkipOperations(EntityInterface $entity): bool {
     // We should not react on entity operations when the entity is ignored or
     // when we're not in a workspace context.
     return $this->workspaceInfo->isEntityIgnored($entity) || !$this->workspaceManager->hasActiveWorkspace();
diff --git a/core/modules/workspaces/src/EntityTypeInfo.php b/core/modules/workspaces/src/Hook/EntityTypeInfo.php
similarity index 65%
rename from core/modules/workspaces/src/EntityTypeInfo.php
rename to core/modules/workspaces/src/Hook/EntityTypeInfo.php
index 4bd7e0c426e9..6b5ad43ddbb1 100644
--- a/core/modules/workspaces/src/EntityTypeInfo.php
+++ b/core/modules/workspaces/src/Hook/EntityTypeInfo.php
@@ -1,51 +1,38 @@
 <?php
 
-namespace Drupal\workspaces;
+declare(strict_types=1);
+
+namespace Drupal\workspaces\Hook;
 
-use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Entity\EntityPublishedInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Hook\Attribute\Hook;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\workspaces\Entity\Handler\BlockContentWorkspaceHandler;
 use Drupal\workspaces\Entity\Handler\DefaultWorkspaceHandler;
 use Drupal\workspaces\Entity\Handler\IgnoredWorkspaceHandler;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\workspaces\WorkspaceInformationInterface;
 
 /**
- * Manipulates entity type information.
- *
- * This class contains primarily bridged hooks for compile-time or
- * cache-clear-time hooks. Runtime hooks should be placed in EntityOperations.
+ * Defines a class for reacting to entity type information hooks.
  *
- * @internal
+ * This class contains primarily compile-time or cache-clear-time hooks. Runtime
+ * hooks should be placed in EntityOperations.
  */
-class EntityTypeInfo implements ContainerInjectionInterface {
+class EntityTypeInfo {
 
   public function __construct(
-    protected readonly WorkspaceInformationInterface $workspaceInfo,
-  ) {
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('workspaces.information')
-    );
-  }
+    protected WorkspaceInformationInterface $workspaceInfo,
+  ) {}
 
   /**
-   * Adds workspace support info to eligible entity types.
+   * Implements hook_entity_type_build().
    *
-   * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
-   *   An associative array of all entity type definitions, keyed by the entity
-   *   type name. Passed by reference.
-   *
-   * @see hook_entity_type_build()
+   * Adds workspace support info to eligible entity types.
    */
-  public function entityTypeBuild(array &$entity_types) {
+  #[Hook('entity_type_build')]
+  public function entityTypeBuild(array &$entity_types): void {
     foreach ($entity_types as $entity_type) {
       if ($entity_type->hasHandlerClass('workspace')) {
         continue;
@@ -77,14 +64,12 @@ public function entityTypeBuild(array &$entity_types) {
   }
 
   /**
-   * Adds Workspace configuration to appropriate entity types.
+   * Implements hook_entity_type_alter().
    *
-   * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
-   *   An array of entity types.
-   *
-   * @see hook_entity_type_alter()
+   * Adds workspace configuration to appropriate entity types.
    */
-  public function entityTypeAlter(array &$entity_types) {
+  #[Hook('entity_type_alter')]
+  public function entityTypeAlter(array &$entity_types): void {
     foreach ($entity_types as $entity_type) {
       if (!$this->workspaceInfo->isEntityTypeSupported($entity_type)) {
         continue;
@@ -105,14 +90,10 @@ public function entityTypeAlter(array &$entity_types) {
   }
 
   /**
-   * Alters field plugin definitions.
-   *
-   * @param array[] $definitions
-   *   An array of field plugin definitions.
-   *
-   * @see hook_field_info_alter()
+   * Implements hook_field_info_alter().
    */
-  public function fieldInfoAlter(&$definitions) {
+  #[Hook('field_info_alter')]
+  public function fieldInfoAlter(array &$definitions): void {
     if (isset($definitions['entity_reference'])) {
       $definitions['entity_reference']['constraints']['EntityReferenceSupportedNewEntities'] = [];
     }
@@ -124,17 +105,10 @@ public function fieldInfoAlter(&$definitions) {
   }
 
   /**
-   * Provides custom base field definitions for a content entity type.
-   *
-   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
-   *   The entity type definition.
-   *
-   * @return \Drupal\Core\Field\FieldDefinitionInterface[]
-   *   An array of field definitions, keyed by field name.
-   *
-   * @see hook_entity_base_field_info()
+   * Implements hook_entity_base_field_info().
    */
-  public function entityBaseFieldInfo(EntityTypeInterface $entity_type) {
+  #[Hook('entity_base_field_info')]
+  public function entityBaseFieldInfo(EntityTypeInterface $entity_type): array {
     if ($this->workspaceInfo->isEntityTypeSupported($entity_type)) {
       $field_name = $entity_type->getRevisionMetadataKey('workspace');
       $fields[$field_name] = BaseFieldDefinition::create('entity_reference')
@@ -147,6 +121,7 @@ public function entityBaseFieldInfo(EntityTypeInterface $entity_type) {
 
       return $fields;
     }
+    return [];
   }
 
 }
diff --git a/core/modules/workspaces/src/FormOperations.php b/core/modules/workspaces/src/Hook/FormOperations.php
similarity index 60%
rename from core/modules/workspaces/src/FormOperations.php
rename to core/modules/workspaces/src/Hook/FormOperations.php
index 6d06ec6aa058..85f374582398 100644
--- a/core/modules/workspaces/src/FormOperations.php
+++ b/core/modules/workspaces/src/Hook/FormOperations.php
@@ -1,61 +1,29 @@
 <?php
 
-namespace Drupal\workspaces;
+namespace Drupal\workspaces\Hook;
 
-use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Form\WorkspaceDynamicSafeFormInterface;
 use Drupal\Core\Form\WorkspaceSafeFormInterface;
+use Drupal\Core\Hook\Attribute\Hook;
 use Drupal\Core\Render\Element;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\workspaces\WorkspaceManagerInterface;
 
 /**
  * Defines a class for reacting to form operations.
- *
- * @internal
  */
-class FormOperations implements ContainerInjectionInterface {
+class FormOperations {
 
-  /**
-   * The workspace manager service.
-   *
-   * @var \Drupal\workspaces\WorkspaceManagerInterface
-   */
-  protected $workspaceManager;
-
-  /**
-   * Constructs a new FormOperations instance.
-   *
-   * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager
-   *   The workspace manager service.
-   */
-  public function __construct(WorkspaceManagerInterface $workspace_manager) {
-    $this->workspaceManager = $workspace_manager;
-  }
+  public function __construct(
+    protected WorkspaceManagerInterface $workspaceManager,
+  ) {}
 
   /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('workspaces.manager')
-    );
-  }
-
-  /**
-   * Alters forms to disallow editing in non-default workspaces.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   * @param string $form_id
-   *   The form ID.
-   *
-   * @see hook_form_alter()
+   * Implements hook_form_alter().
    */
-  public function formAlter(array &$form, FormStateInterface $form_state, $form_id) {
+  #[Hook('form_alter')]
+  public function formAlter(array &$form, FormStateInterface $form_state, $form_id): void {
     // No alterations are needed if we're not in a workspace context.
     if (!$this->workspaceManager->hasActiveWorkspace()) {
       return;
@@ -83,7 +51,7 @@ public function formAlter(array &$form, FormStateInterface $form_state, $form_id
    * @param array &$element
    *   An associative array containing the structure of the form.
    */
-  protected function addWorkspaceValidation(array &$element) {
+  protected function addWorkspaceValidation(array &$element): void {
     // Recurse through all children and add our validation handler if needed.
     foreach (Element::children($element) as $key) {
       if (isset($element[$key]) && $element[$key]) {
@@ -99,7 +67,7 @@ protected function addWorkspaceValidation(array &$element) {
   /**
    * Validation handler which sets a validation error for all unsupported forms.
    */
-  public static function validateDefaultWorkspace(array &$form, FormStateInterface $form_state) {
+  public static function validateDefaultWorkspace(array &$form, FormStateInterface $form_state): void {
     if ($form_state->get('workspace_safe') !== TRUE) {
       $form_state->setError($form, new TranslatableMarkup('This form can only be submitted in the default workspace.'));
     }
diff --git a/core/modules/workspaces/src/ViewsQueryAlter.php b/core/modules/workspaces/src/Hook/ViewsOperations.php
similarity index 78%
rename from core/modules/workspaces/src/ViewsQueryAlter.php
rename to core/modules/workspaces/src/Hook/ViewsOperations.php
index f409a20b0d9c..dcf86d7bf603 100644
--- a/core/modules/workspaces/src/ViewsQueryAlter.php
+++ b/core/modules/workspaces/src/Hook/ViewsOperations.php
@@ -1,132 +1,55 @@
 <?php
 
-namespace Drupal\workspaces;
+declare(strict_types=1);
+
+namespace Drupal\workspaces\Hook;
 
-use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Hook\Attribute\Hook;
 use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\views\Plugin\ViewsHandlerManager;
+use Drupal\views\Plugin\views\join\JoinPluginInterface;
 use Drupal\views\Plugin\views\query\QueryPluginBase;
 use Drupal\views\Plugin\views\query\Sql;
-use Drupal\views\Plugin\ViewsHandlerManager;
 use Drupal\views\ViewExecutable;
 use Drupal\views\ViewsData;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\workspaces\WorkspaceAssociation;
+use Drupal\workspaces\WorkspaceInformationInterface;
+use Drupal\workspaces\WorkspaceManagerInterface;
+use Symfony\Component\DependencyInjection\Attribute\Autowire;
 
 /**
  * Defines a class for altering views queries.
- *
- * @internal
  */
-class ViewsQueryAlter implements ContainerInjectionInterface {
-
-  /**
-   * The entity type manager service.
-   *
-   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
-   */
-  protected $entityTypeManager;
-
-  /**
-   * The entity field manager.
-   *
-   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
-   */
-  protected $entityFieldManager;
-
-  /**
-   * The workspace manager service.
-   *
-   * @var \Drupal\workspaces\WorkspaceManagerInterface
-   */
-  protected $workspaceManager;
-
-  /**
-   * The views data.
-   *
-   * @var \Drupal\views\ViewsData
-   */
-  protected $viewsData;
-
-  /**
-   * A plugin manager which handles instances of views join plugins.
-   *
-   * @var \Drupal\views\Plugin\ViewsHandlerManager
-   */
-  protected $viewsJoinPluginManager;
-
-  /**
-   * The language manager.
-   *
-   * @var \Drupal\Core\Language\LanguageManagerInterface
-   */
-  protected $languageManager;
-
-  /**
-   * The workspace information service.
-   *
-   * @var \Drupal\workspaces\WorkspaceInformationInterface
-   */
-  protected WorkspaceInformationInterface $workspaceInfo;
+class ViewsOperations {
 
   /**
    * An array of tables adjusted for workspace_association join.
    *
    * @var \WeakMap
    */
-  protected \WeakMap $adjustedTables;
-
-  /**
-   * Constructs a new ViewsQueryAlter instance.
-   *
-   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
-   *   The entity type manager service.
-   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
-   *   The entity field manager.
-   * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager
-   *   The workspace manager service.
-   * @param \Drupal\views\ViewsData $views_data
-   *   The views data.
-   * @param \Drupal\views\Plugin\ViewsHandlerManager $views_join_plugin_manager
-   *   The views join plugin manager.
-   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
-   *   The language manager.
-   * @param \Drupal\workspaces\WorkspaceInformationInterface $workspace_information
-   *   The workspace information service.
-   */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, WorkspaceManagerInterface $workspace_manager, ViewsData $views_data, ViewsHandlerManager $views_join_plugin_manager, LanguageManagerInterface $language_manager, WorkspaceInformationInterface $workspace_information) {
-    $this->entityTypeManager = $entity_type_manager;
-    $this->entityFieldManager = $entity_field_manager;
-    $this->workspaceManager = $workspace_manager;
-    $this->viewsData = $views_data;
-    $this->viewsJoinPluginManager = $views_join_plugin_manager;
-    $this->languageManager = $language_manager;
-    $this->workspaceInfo = $workspace_information;
+  private \WeakMap $adjustedTables;
+
+  public function __construct(
+    protected EntityTypeManagerInterface $entityTypeManager,
+    protected EntityFieldManagerInterface $entityFieldManager,
+    protected WorkspaceManagerInterface $workspaceManager,
+    protected WorkspaceInformationInterface $workspaceInfo,
+    protected LanguageManagerInterface $languageManager,
+    protected ?ViewsData $viewsData = NULL,
+    #[Autowire(service: 'plugin.manager.views.join')]
+    protected ?ViewsHandlerManager $viewsJoinPluginManager = NULL,
+  ) {
     $this->adjustedTables = new \WeakMap();
   }
 
   /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity_type.manager'),
-      $container->get('entity_field.manager'),
-      $container->get('workspaces.manager'),
-      $container->get('views.views_data'),
-      $container->get('plugin.manager.views.join'),
-      $container->get('language_manager'),
-      $container->get('workspaces.information')
-    );
-  }
-
-  /**
-   * Implements a hook bridge for hook_views_query_alter().
-   *
-   * @see hook_views_query_alter()
+   * Implements hook_views_query_alter().
    */
-  public function alterQuery(ViewExecutable $view, QueryPluginBase $query) {
+  #[Hook('views_query_alter')]
+  public function viewsQueryAlter(ViewExecutable $view, QueryPluginBase $query): void {
     // Don't alter any views queries if we're not in a workspace context.
     if (!$this->workspaceManager->hasActiveWorkspace()) {
       return;
@@ -168,7 +91,7 @@ public function alterQuery(ViewExecutable $view, QueryPluginBase $query) {
    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
    *   The entity type definition.
    */
-  protected function alterQueryForEntityType(Sql $query, EntityTypeInterface $entity_type) {
+  protected function alterQueryForEntityType(Sql $query, EntityTypeInterface $entity_type): void {
     /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
     $table_mapping = $this->entityTypeManager->getStorage($entity_type->id())->getTableMapping();
     $field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type->id());
@@ -301,7 +224,7 @@ protected function alterQueryForEntityType(Sql $query, EntityTypeInterface $enti
    * @return string
    *   The alias of the 'workspace_association' table.
    */
-  protected function ensureWorkspaceAssociationTable($entity_type_id, Sql $query, $relationship) {
+  protected function ensureWorkspaceAssociationTable(string $entity_type_id, Sql $query, string $relationship): string {
     if (isset($query->tables[$relationship]['workspace_association'])) {
       return $query->tables[$relationship]['workspace_association']['alias'];
     }
@@ -346,7 +269,7 @@ protected function ensureWorkspaceAssociationTable($entity_type_id, Sql $query,
    * @return string
    *   The alias of the relationship.
    */
-  protected function ensureRevisionTable(EntityTypeInterface $entity_type, Sql $query, $relationship) {
+  protected function ensureRevisionTable(EntityTypeInterface $entity_type, Sql $query, string $relationship): string {
     // Get the alias for the 'workspace_association' table we chain off of in
     // the COALESCE.
     $workspace_association_table = $this->ensureWorkspaceAssociationTable($entity_type->id(), $query, $relationship);
@@ -400,7 +323,7 @@ protected function ensureRevisionTable(EntityTypeInterface $entity_type, Sql $qu
    *
    * @throws \Drupal\Component\Plugin\Exception\PluginException
    */
-  protected function getRevisionTableJoin($relationship, $table, $field, $workspace_association_table, EntityTypeInterface $entity_type) {
+  protected function getRevisionTableJoin(string $relationship, string $table, string $field, string $workspace_association_table, EntityTypeInterface $entity_type): JoinPluginInterface {
     $definition = [
       'table' => $table,
       'field' => $field,
@@ -438,7 +361,7 @@ protected function getRevisionTableJoin($relationship, $table, $field, $workspac
    * @param string $alias
    *   The alias of the table it needs to appear before.
    */
-  protected function moveEntityTable(Sql $query, $workspace_association_table, $alias) {
+  protected function moveEntityTable(Sql $query, string $workspace_association_table, string $alias): void {
     $table_queue =& $query->getTableQueue();
     $keys = array_keys($table_queue);
     $current_index = array_search($workspace_association_table, $keys);
diff --git a/core/modules/workspaces/src/Hook/WorkspacesHooks.php b/core/modules/workspaces/src/Hook/WorkspacesHooks.php
index 2a382eb0b317..b945a24163fd 100644
--- a/core/modules/workspaces/src/Hook/WorkspacesHooks.php
+++ b/core/modules/workspaces/src/Hook/WorkspacesHooks.php
@@ -1,210 +1,72 @@
 <?php
 
+declare(strict_types=1);
+
 namespace Drupal\workspaces\Hook;
 
-use Drupal\Core\Access\AccessResultInterface;
-use Drupal\workspaces\ViewsQueryAlter;
-use Drupal\views\Plugin\views\query\QueryPluginBase;
-use Drupal\views\ViewExecutable;
 use Drupal\Core\Cache\Cache;
-use Drupal\workspaces\EntityAccess;
-use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
+use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityTypeInterface;
-use Drupal\workspaces\FormOperations;
-use Drupal\workspaces\EntityOperations;
-use Drupal\Core\Entity\EntityFormInterface;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\workspaces\EntityTypeInfo;
-use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\workspaces\WorkspaceInformationInterface;
+use Drupal\workspaces\WorkspaceManagerInterface;
 
 /**
  * Hook implementations for workspaces.
  */
 class WorkspacesHooks {
 
+  use StringTranslationTrait;
+
+  public function __construct(
+    protected WorkspaceManagerInterface $workspaceManager,
+    protected WorkspaceInformationInterface $workspaceInfo,
+    protected EntityDefinitionUpdateManagerInterface $entityDefinitionUpdateManager,
+    protected CacheTagsInvalidatorInterface $cacheTagsInvalidator,
+  ) {}
+
   /**
    * Implements hook_help().
    */
   #[Hook('help')]
-  public function help($route_name, RouteMatchInterface $route_match) {
+  public function help(string $route_name, RouteMatchInterface $route_match): string {
+    $output = '';
     switch ($route_name) {
       // Main module help for the Workspaces module.
       case 'help.page.workspaces':
         $output = '';
-        $output .= '<h2>' . t('About') . '</h2>';
-        $output .= '<p>' . t('The Workspaces module allows workspaces to be defined and switched between. Content is then assigned to the active workspace when created. For more information, see the <a href=":workspaces">online documentation for the Workspaces module</a>.', [':workspaces' => 'https://www.drupal.org/docs/8/core/modules/workspace/overview']) . '</p>';
-        return $output;
+        $output .= '<h2>' . $this->t('About') . '</h2>';
+        $output .= '<p>' . $this->t('The Workspaces module allows workspaces to be defined and switched between. Content is then assigned to the active workspace when created. For more information, see the <a href=":workspaces">online documentation for the Workspaces module</a>.', [':workspaces' => 'https://www.drupal.org/docs/8/core/modules/workspace/overview']) . '</p>';
+        break;
     }
+    return $output;
   }
 
   /**
    * Implements hook_module_preinstall().
    */
   #[Hook('module_preinstall')]
-  public function modulePreinstall($module): void {
+  public function modulePreinstall(string $module): void {
     if ($module !== 'workspaces') {
       return;
     }
 
-    /** @var \Drupal\workspaces\WorkspaceInformationInterface $workspace_info */
-    $workspace_info = \Drupal::service('workspaces.information');
-    $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
-    foreach ($entity_definition_update_manager->getEntityTypes() as $entity_type) {
-      if ($workspace_info->isEntityTypeSupported($entity_type)) {
+    foreach ($this->entityDefinitionUpdateManager->getEntityTypes() as $entity_type) {
+      if ($this->workspaceInfo->isEntityTypeSupported($entity_type)) {
         $entity_type->setRevisionMetadataKey('workspace', 'workspace');
-        $entity_definition_update_manager->updateEntityType($entity_type);
+        $this->entityDefinitionUpdateManager->updateEntityType($entity_type);
       }
     }
   }
 
   /**
-   * Implements hook_entity_type_build().
-   */
-  #[Hook('entity_type_build')]
-  public function entityTypeBuild(array &$entity_types): void {
-    \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityTypeInfo::class)->entityTypeBuild($entity_types);
-  }
-
-  /**
-   * Implements hook_entity_type_alter().
-   */
-  #[Hook('entity_type_alter')]
-  public function entityTypeAlter(array &$entity_types) : void {
-    \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityTypeInfo::class)->entityTypeAlter($entity_types);
-  }
-
-  /**
-   * Implements hook_form_alter().
-   */
-  #[Hook('form_alter')]
-  public function formAlter(&$form, FormStateInterface $form_state, $form_id) : void {
-    if ($form_state->getFormObject() instanceof EntityFormInterface) {
-      \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityFormAlter($form, $form_state, $form_id);
-    }
-    \Drupal::service('class_resolver')->getInstanceFromDefinition(FormOperations::class)->formAlter($form, $form_state, $form_id);
-  }
-
-  /**
-   * Implements hook_field_info_alter().
-   */
-  #[Hook('field_info_alter')]
-  public function fieldInfoAlter(&$definitions): void {
-    \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityTypeInfo::class)->fieldInfoAlter($definitions);
-  }
-
-  /**
-   * Implements hook_entity_base_field_info().
-   */
-  #[Hook('entity_base_field_info')]
-  public function entityBaseFieldInfo(EntityTypeInterface $entity_type) {
-    return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityTypeInfo::class)->entityBaseFieldInfo($entity_type);
-  }
-
-  /**
-   * Implements hook_entity_preload().
-   */
-  #[Hook('entity_preload')]
-  public function entityPreload(array $ids, $entity_type_id): array {
-    return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityPreload($ids, $entity_type_id);
-  }
-
-  /**
-   * Implements hook_entity_presave().
-   */
-  #[Hook('entity_presave')]
-  public function entityPresave(EntityInterface $entity) {
-    return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityPresave($entity);
-  }
-
-  /**
-   * Implements hook_entity_insert().
-   */
-  #[Hook('entity_insert')]
-  public function entityInsert(EntityInterface $entity) {
-    if ($entity->getEntityTypeId() === 'workspace') {
-      \Drupal::service('workspaces.association')->workspaceInsert($entity);
-      \Drupal::service('workspaces.repository')->resetCache();
-    }
-    return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityInsert($entity);
-  }
-
-  /**
-   * Implements hook_entity_update().
-   */
-  #[Hook('entity_update')]
-  public function entityUpdate(EntityInterface $entity) {
-    if ($entity->getEntityTypeId() === 'workspace') {
-      \Drupal::service('workspaces.repository')->resetCache();
-    }
-    return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityUpdate($entity);
-  }
-
-  /**
-   * Implements hook_entity_translation_insert().
-   */
-  #[Hook('entity_translation_insert')]
-  public function entityTranslationInsert(EntityInterface $translation) : void {
-    \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityTranslationInsert($translation);
-  }
-
-  /**
-   * Implements hook_entity_predelete().
-   */
-  #[Hook('entity_predelete')]
-  public function entityPredelete(EntityInterface $entity) {
-    if ($entity->getEntityTypeId() === 'workspace') {
-      \Drupal::service('workspaces.repository')->resetCache();
-    }
-    return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityOperations::class)->entityPredelete($entity);
-  }
-
-  /**
-   * Implements hook_entity_delete().
-   */
-  #[Hook('entity_delete')]
-  public function entityDelete(EntityInterface $entity) {
-    if (\Drupal::service('workspaces.information')->isEntityTypeSupported($entity->getEntityType())) {
-      \Drupal::service('workspaces.association')->deleteAssociations(NULL, $entity->getEntityTypeId(), [$entity->id()]);
-    }
-  }
-
-  /**
-   * Implements hook_entity_revision_delete().
-   */
-  #[Hook('entity_revision_delete')]
-  public function entityRevisionDelete(EntityInterface $entity) {
-    if (\Drupal::service('workspaces.information')->isEntityTypeSupported($entity->getEntityType())) {
-      \Drupal::service('workspaces.association')->deleteAssociations(NULL, $entity->getEntityTypeId(), [$entity->id()], [$entity->getRevisionId()]);
-    }
-  }
-
-  /**
-   * Implements hook_entity_access().
-   *
-   * @see \Drupal\workspaces\EntityAccess
-   */
-  #[Hook('entity_access')]
-  public function entityAccess(EntityInterface $entity, $operation, AccountInterface $account): AccessResultInterface {
-    return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityAccess::class)->entityOperationAccess($entity, $operation, $account);
-  }
-
-  /**
-   * Implements hook_entity_create_access().
-   *
-   * @see \Drupal\workspaces\EntityAccess
-   */
-  #[Hook('entity_create_access')]
-  public function entityCreateAccess(AccountInterface $account, array $context, $entity_bundle): AccessResultInterface {
-    return \Drupal::service('class_resolver')->getInstanceFromDefinition(EntityAccess::class)->entityCreateAccess($account, $context, $entity_bundle);
-  }
-
-  /**
-   * Implements hook_ENTITY_TYPE_update() for the 'menu_link_content' entity type.
+   * Implements hook_ENTITY_TYPE_update() for 'menu_link_content' entities.
    */
   #[Hook('menu_link_content_update')]
-  public function menuLinkContentUpdate(EntityInterface $entity) {
+  public function menuLinkContentUpdate(EntityInterface $entity): void {
     /** @var \Drupal\menu_link_content\MenuLinkContentInterface $entity */
     if ($entity->getLoadedRevisionId() != $entity->getRevisionId()) {
       // We are not updating the menu tree definitions when a custom menu link
@@ -213,24 +75,16 @@ public function menuLinkContentUpdate(EntityInterface $entity) {
       // inserting or deleting a custom menu link updates the menu tree
       // definitions, so we don't have to do anything in those cases.
       $cache_tags = Cache::buildTags('config:system.menu', [$entity->getMenuName()], '.');
-      \Drupal::service('cache_tags.invalidator')->invalidateTags($cache_tags);
+      $this->cacheTagsInvalidator->invalidateTags($cache_tags);
     }
   }
 
-  /**
-   * Implements hook_views_query_alter().
-   */
-  #[Hook('views_query_alter')]
-  public function viewsQueryAlter(ViewExecutable $view, QueryPluginBase $query): void {
-    \Drupal::service('class_resolver')->getInstanceFromDefinition(ViewsQueryAlter::class)->alterQuery($view, $query);
-  }
-
   /**
    * Implements hook_cron().
    */
   #[Hook('cron')]
   public function cron(): void {
-    \Drupal::service('workspaces.manager')->purgeDeletedWorkspacesBatch();
+    $this->workspaceManager->purgeDeletedWorkspacesBatch();
   }
 
 }
diff --git a/core/modules/workspaces/tests/src/Kernel/WorkspaceContentTranslationTest.php b/core/modules/workspaces/tests/src/Kernel/WorkspaceContentTranslationTest.php
index eabe058b9160..3aa8b9a5bab7 100644
--- a/core/modules/workspaces/tests/src/Kernel/WorkspaceContentTranslationTest.php
+++ b/core/modules/workspaces/tests/src/Kernel/WorkspaceContentTranslationTest.php
@@ -65,7 +65,7 @@ protected function setUp(): void {
   /**
    * Tests translations created in a workspace.
    *
-   * @covers \Drupal\workspaces\EntityOperations::entityTranslationInsert
+   * @covers \Drupal\workspaces\Hook\EntityOperations::entityTranslationInsert
    */
   public function testTranslations(): void {
     $storage = $this->entityTypeManager->getStorage('entity_test_mulrevpub');
diff --git a/core/modules/workspaces/tests/src/Kernel/WorkspaceViewsIntegrationTest.php b/core/modules/workspaces/tests/src/Kernel/WorkspaceViewsIntegrationTest.php
index 7ecc77559891..b8c0ca4b3d03 100644
--- a/core/modules/workspaces/tests/src/Kernel/WorkspaceViewsIntegrationTest.php
+++ b/core/modules/workspaces/tests/src/Kernel/WorkspaceViewsIntegrationTest.php
@@ -100,8 +100,8 @@ protected function setUp($import_test_views = TRUE): void {
   /**
    * Tests workspace query alter for views.
    *
-   * @covers \Drupal\workspaces\ViewsQueryAlter::alterQueryForEntityType
-   * @covers \Drupal\workspaces\ViewsQueryAlter::getRevisionTableJoin
+   * @covers \Drupal\workspaces\Hook\ViewsOperations::alterQueryForEntityType
+   * @covers \Drupal\workspaces\Hook\ViewsOperations::getRevisionTableJoin
    */
   public function testViewsQueryAlter(): void {
     // Create a test entity and two nodes.
diff --git a/core/modules/workspaces/workspaces.module b/core/modules/workspaces/workspaces.module
index 2b6d101ca394..a053105f20c3 100644
--- a/core/modules/workspaces/workspaces.module
+++ b/core/modules/workspaces/workspaces.module
@@ -10,7 +10,7 @@
 function workspaces_module_implements_alter(&$implementations, $hook): void {
   // Move our 'hook_entity_presave' implementation at the beginning to ensure
   // that other presave implementations are aware of the changes done in
-  // \Drupal\workspaces\EntityOperations::entityPresave().
+  // \Drupal\workspaces\Hook\EntityOperations::entityPresave().
   if ($hook === 'entity_presave') {
     $implementation = $implementations['workspaces'];
     $implementations = ['workspaces' => $implementation] + $implementations;
@@ -26,7 +26,7 @@ function workspaces_module_implements_alter(&$implementations, $hook): void {
   // Move our 'hook_entity_insert' implementation at the end to ensure that
   // the second (pending) revision created for published entities is not used
   // by other 'hook_entity_insert' implementations.
-  // @see \Drupal\workspaces\EntityOperations::entityInsert()
+  // @see \Drupal\workspaces\Hook\EntityOperations::entityInsert()
   if ($hook === 'entity_insert') {
     $group = $implementations['workspaces'];
     unset($implementations['workspaces']);
-- 
GitLab