annotate core/modules/workspaces/src/EntityQuery/Tables.php @ 5:12f9dff5fda9 tip

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