annotate core/modules/workspaces/src/EntityQuery/Tables.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
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 }