You are here

class Tables in Drupal 9

Same name in this branch
  1. 9 core/modules/workspaces/src/EntityQuery/Tables.php \Drupal\workspaces\EntityQuery\Tables
  2. 9 core/lib/Drupal/Core/Entity/Query/Sql/Tables.php \Drupal\Core\Entity\Query\Sql\Tables
Same name and namespace in other branches
  1. 8 core/modules/workspaces/src/EntityQuery/Tables.php \Drupal\workspaces\EntityQuery\Tables

Alters entity queries to use a workspace revision instead of the default one.

Hierarchy

Expanded class hierarchy of Tables

1 string reference to 'Tables'
Query::getTables in core/lib/Drupal/Core/Entity/Query/Sql/Query.php
Gets the Tables object for this query.

File

core/modules/workspaces/src/EntityQuery/Tables.php, line 13

Namespace

Drupal\workspaces\EntityQuery
View source
class Tables extends BaseTables {

  /**
   * The workspace manager.
   *
   * @var \Drupal\workspaces\WorkspaceManagerInterface
   */
  protected $workspaceManager;

  /**
   * Workspace association table array, key is base table name, value is alias.
   *
   * @var array
   */
  protected $contentWorkspaceTables = [];

  /**
   * Keeps track of the entity type IDs for each base table of the query.
   *
   * The array is keyed by the base table alias and the values are entity type
   * IDs.
   *
   * @var array
   */
  protected $baseTablesEntityType = [];

  /**
   * {@inheritdoc}
   */
  public function __construct(SelectInterface $sql_query) {
    parent::__construct($sql_query);
    $this->workspaceManager = \Drupal::service('workspaces.manager');

    // The join between the first 'workspace_association' table and base table
    // of the query is done in
    // \Drupal\workspaces\EntityQuery\QueryTrait::prepare(), so we need to
    // initialize its entry manually.
    if ($this->sqlQuery
      ->getMetaData('active_workspace_id')) {
      $this->contentWorkspaceTables['base_table'] = 'workspace_association';
      $this->baseTablesEntityType['base_table'] = $this->sqlQuery
        ->getMetaData('entity_type');
    }
  }

  /**
   * {@inheritdoc}
   */
  public function addField($field, $type, $langcode) {

    // The parent method uses shared and dedicated revision tables only when the
    // entity query is instructed to query all revisions. However, if we are
    // looking for workspace-specific revisions, we have to force the parent
    // method to always pick the revision tables if the field being queried is
    // revisionable.
    if ($this->sqlQuery
      ->getMetaData('active_workspace_id')) {
      $previous_all_revisions = $this->sqlQuery
        ->getMetaData('all_revisions');
      $this->sqlQuery
        ->addMetaData('all_revisions', TRUE);
    }
    $alias = parent::addField($field, $type, $langcode);

    // Restore the 'all_revisions' metadata because we don't want to interfere
    // with the rest of the query.
    if (isset($previous_all_revisions)) {
      $this->sqlQuery
        ->addMetaData('all_revisions', $previous_all_revisions);
    }
    return $alias;
  }

  /**
   * {@inheritdoc}
   */
  protected function addJoin($type, $table, $join_condition, $langcode, $delta = NULL) {
    if ($this->sqlQuery
      ->getMetaData('active_workspace_id')) {

      // The join condition for a shared or dedicated field table is in the form
      // of "%alias.$id_field = $base_table.$id_field". Whenever we join a field
      // table we have to check:
      // 1) if $base_table is of an entity type that can belong to a workspace;
      // 2) if $id_field is the revision key of that entity type or the special
      // 'revision_id' string used when joining dedicated field tables.
      // If those two conditions are met, we have to update the join condition
      // to also look for a possible workspace-specific revision using COALESCE.
      $condition_parts = explode(' = ', $join_condition);
      $condition_parts_1 = str_replace([
        '[',
        ']',
      ], '', $condition_parts[1]);
      list($base_table, $id_field) = explode('.', $condition_parts_1);
      if (isset($this->baseTablesEntityType[$base_table])) {
        $entity_type_id = $this->baseTablesEntityType[$base_table];
        $revision_key = $this->entityTypeManager
          ->getActiveDefinition($entity_type_id)
          ->getKey('revision');
        if ($id_field === $revision_key || $id_field === 'revision_id') {
          $workspace_association_table = $this->contentWorkspaceTables[$base_table];
          $join_condition = "{$condition_parts[0]} = COALESCE({$workspace_association_table}.target_entity_revision_id, {$condition_parts[1]})";
        }
      }
    }
    return parent::addJoin($type, $table, $join_condition, $langcode, $delta);
  }

  /**
   * {@inheritdoc}
   */
  protected function addNextBaseTable(EntityType $entity_type, $table, $sql_column, FieldStorageDefinitionInterface $field_storage) {
    $next_base_table_alias = parent::addNextBaseTable($entity_type, $table, $sql_column, $field_storage);
    $active_workspace_id = $this->sqlQuery
      ->getMetaData('active_workspace_id');
    if ($active_workspace_id && $this->workspaceManager
      ->isEntityTypeSupported($entity_type)) {
      $this
        ->addWorkspaceAssociationJoin($entity_type
        ->id(), $next_base_table_alias, $active_workspace_id);
    }
    return $next_base_table_alias;
  }

  /**
   * Adds a new join to the 'workspace_association' table for an entity base table.
   *
   * This method assumes that the active workspace has already been determined
   * to be a non-default workspace.
   *
   * @param string $entity_type_id
   *   The ID of the entity type whose base table we are joining.
   * @param string $base_table_alias
   *   The alias of the entity type's base table.
   * @param string $active_workspace_id
   *   The ID of the active workspace.
   *
   * @return string
   *   The alias of the joined table.
   */
  public function addWorkspaceAssociationJoin($entity_type_id, $base_table_alias, $active_workspace_id) {
    if (!isset($this->contentWorkspaceTables[$base_table_alias])) {
      $entity_type = $this->entityTypeManager
        ->getActiveDefinition($entity_type_id);
      $id_field = $entity_type
        ->getKey('id');

      // LEFT join the Workspace association entity's table so we can properly
      // include live content along with a possible workspace-specific revision.
      $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}'");
      $this->baseTablesEntityType[$base_table_alias] = $entity_type
        ->id();
    }
    return $this->contentWorkspaceTables[$base_table_alias];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Tables::$baseTablesEntityType protected property Keeps track of the entity type IDs for each base table of the query.
Tables::$caseSensitiveFields protected property List of case sensitive fields.
Tables::$contentWorkspaceTables protected property Workspace association table array, key is base table name, value is alias.
Tables::$entityFieldManager protected property The entity field manager.
Tables::$entityTables protected property Entity table array.
Tables::$entityTypeManager protected property The entity type manager.
Tables::$fieldTables protected property Field table array, key is table name, value is alias.
Tables::$sqlQuery protected property
Tables::$workspaceManager protected property The workspace manager.
Tables::addField public function Adds a field to a database query. Overrides Tables::addField
Tables::addJoin protected function Adds a join to a given table. Overrides Tables::addJoin
Tables::addNextBaseTable protected function Add the next entity base table. Overrides Tables::addNextBaseTable
Tables::addWorkspaceAssociationJoin public function Adds a new join to the 'workspace_association' table for an entity base table.
Tables::ensureEntityTable protected function Joins the entity table, if necessary, and returns the alias for it.
Tables::ensureFieldTable protected function Ensure the field table is joined if necessary.
Tables::getTableMapping protected function Gets the schema for the given table.
Tables::isFieldCaseSensitive public function Determines whether the given field is case sensitive. Overrides TablesInterface::isFieldCaseSensitive
Tables::__construct public function Overrides Tables::__construct