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