9ae727b5e774608f5f81b2a8651badedc31091fd
[yaffs-website] / web / core / modules / workspaces / src / EntityQuery / Tables.php
1 <?php
2
3 namespace Drupal\workspaces\EntityQuery;
4
5 use Drupal\Core\Database\Query\SelectInterface;
6 use Drupal\Core\Entity\EntityType;
7 use Drupal\Core\Entity\Query\Sql\Tables as BaseTables;
8 use Drupal\Core\Field\FieldStorageDefinitionInterface;
9
10 /**
11  * Alters entity queries to use a workspace revision instead of the default one.
12  */
13 class Tables extends BaseTables {
14
15   /**
16    * The workspace manager.
17    *
18    * @var \Drupal\workspaces\WorkspaceManagerInterface
19    */
20   protected $workspaceManager;
21
22   /**
23    * Workspace association table array, key is base table name, value is alias.
24    *
25    * @var array
26    */
27   protected $contentWorkspaceTables = [];
28
29   /**
30    * Keeps track of the entity type IDs for each base table of the query.
31    *
32    * The array is keyed by the base table alias and the values are entity type
33    * IDs.
34    *
35    * @var array
36    */
37   protected $baseTablesEntityType = [];
38
39   /**
40    * {@inheritdoc}
41    */
42   public function __construct(SelectInterface $sql_query) {
43     parent::__construct($sql_query);
44
45     $this->workspaceManager = \Drupal::service('workspaces.manager');
46
47     // The join between the first 'workspace_association' table and base table
48     // of the query is done in
49     // \Drupal\workspaces\EntityQuery\QueryTrait::prepare(), so we need to
50     // initialize its entry manually.
51     if ($this->sqlQuery->getMetaData('active_workspace_id')) {
52       $this->contentWorkspaceTables['base_table'] = 'workspace_association';
53       $this->baseTablesEntityType['base_table'] = $this->sqlQuery->getMetaData('entity_type');
54     }
55   }
56
57   /**
58    * {@inheritdoc}
59    */
60   public function addField($field, $type, $langcode) {
61     // The parent method uses shared and dedicated revision tables only when the
62     // entity query is instructed to query all revisions. However, if we are
63     // looking for workspace-specific revisions, we have to force the parent
64     // method to always pick the revision tables if the field being queried is
65     // revisionable.
66     if ($active_workspace_id = $this->sqlQuery->getMetaData('active_workspace_id')) {
67       $previous_all_revisions = $this->sqlQuery->getMetaData('all_revisions');
68       $this->sqlQuery->addMetaData('all_revisions', TRUE);
69     }
70
71     $alias = parent::addField($field, $type, $langcode);
72
73     // Restore the 'all_revisions' metadata because we don't want to interfere
74     // with the rest of the query.
75     if (isset($previous_all_revisions)) {
76       $this->sqlQuery->addMetaData('all_revisions', $previous_all_revisions);
77     }
78
79     return $alias;
80   }
81
82   /**
83    * {@inheritdoc}
84    */
85   protected function addJoin($type, $table, $join_condition, $langcode, $delta = NULL) {
86     if ($this->sqlQuery->getMetaData('active_workspace_id')) {
87       // The join condition for a shared or dedicated field table is in the form
88       // of "%alias.$id_field = $base_table.$id_field". Whenever we join a field
89       // table we have to check:
90       // 1) if $base_table is of an entity type that can belong to a workspace;
91       // 2) if $id_field is the revision key of that entity type or the special
92       // 'revision_id' string used when joining dedicated field tables.
93       // If those two conditions are met, we have to update the join condition
94       // to also look for a possible workspace-specific revision using COALESCE.
95       $condition_parts = explode(' = ', $join_condition);
96       list($base_table, $id_field) = explode('.', $condition_parts[1]);
97
98       if (isset($this->baseTablesEntityType[$base_table])) {
99         $entity_type_id = $this->baseTablesEntityType[$base_table];
100         $revision_key = $this->entityManager->getDefinition($entity_type_id)->getKey('revision');
101
102         if ($id_field === $revision_key || $id_field === 'revision_id') {
103           $workspace_association_table = $this->contentWorkspaceTables[$base_table];
104           $join_condition = "{$condition_parts[0]} = COALESCE($workspace_association_table.target_entity_revision_id, {$condition_parts[1]})";
105         }
106       }
107     }
108
109     return parent::addJoin($type, $table, $join_condition, $langcode, $delta);
110   }
111
112   /**
113    * {@inheritdoc}
114    */
115   protected function addNextBaseTable(EntityType $entity_type, $table, $sql_column, FieldStorageDefinitionInterface $field_storage) {
116     $next_base_table_alias = parent::addNextBaseTable($entity_type, $table, $sql_column, $field_storage);
117
118     $active_workspace_id = $this->sqlQuery->getMetaData('active_workspace_id');
119     if ($active_workspace_id && $this->workspaceManager->isEntityTypeSupported($entity_type)) {
120       $this->addWorkspaceAssociationJoin($entity_type->id(), $next_base_table_alias, $active_workspace_id);
121     }
122
123     return $next_base_table_alias;
124   }
125
126   /**
127    * Adds a new join to the 'workspace_association' table for an entity base table.
128    *
129    * This method assumes that the active workspace has already been determined
130    * to be a non-default workspace.
131    *
132    * @param string $entity_type_id
133    *   The ID of the entity type whose base table we are joining.
134    * @param string $base_table_alias
135    *   The alias of the entity type's base table.
136    * @param string $active_workspace_id
137    *   The ID of the active workspace.
138    *
139    * @return string
140    *   The alias of the joined table.
141    */
142   public function addWorkspaceAssociationJoin($entity_type_id, $base_table_alias, $active_workspace_id) {
143     if (!isset($this->contentWorkspaceTables[$base_table_alias])) {
144       $entity_type = $this->entityManager->getDefinition($entity_type_id);
145       $id_field = $entity_type->getKey('id');
146
147       // LEFT join the Workspace association entity's table so we can properly
148       // include live content along with a possible workspace-specific revision.
149       $this->contentWorkspaceTables[$base_table_alias] = $this->sqlQuery->leftJoin('workspace_association', NULL, "%alias.target_entity_type_id = '$entity_type_id' AND %alias.target_entity_id = $base_table_alias.$id_field AND %alias.workspace = '$active_workspace_id'");
150
151       $this->baseTablesEntityType[$base_table_alias] = $entity_type->id();
152     }
153     return $this->contentWorkspaceTables[$base_table_alias];
154   }
155
156 }