function Tables::addField

Same name in this branch
  1. 11.x core/modules/workspaces/src/EntityQuery/Tables.php \Drupal\workspaces\EntityQuery\Tables::addField()
Same name in other branches
  1. 9 core/modules/workspaces/src/EntityQuery/Tables.php \Drupal\workspaces\EntityQuery\Tables::addField()
  2. 9 core/lib/Drupal/Core/Entity/Query/Sql/Tables.php \Drupal\Core\Entity\Query\Sql\Tables::addField()
  3. 8.9.x core/modules/workspaces/src/EntityQuery/Tables.php \Drupal\workspaces\EntityQuery\Tables::addField()
  4. 8.9.x core/lib/Drupal/Core/Entity/Query/Sql/Tables.php \Drupal\Core\Entity\Query\Sql\Tables::addField()
  5. 10 core/modules/workspaces/src/EntityQuery/Tables.php \Drupal\workspaces\EntityQuery\Tables::addField()
  6. 10 core/lib/Drupal/Core/Entity/Query/Sql/Tables.php \Drupal\Core\Entity\Query\Sql\Tables::addField()

Overrides TablesInterface::addField

1 call to Tables::addField()
Tables::addField in core/modules/workspaces/src/EntityQuery/Tables.php
Adds a field to a database query.
1 method overrides Tables::addField()
Tables::addField in core/modules/workspaces/src/EntityQuery/Tables.php
Adds a field to a database query.

File

core/lib/Drupal/Core/Entity/Query/Sql/Tables.php, line 80

Class

Tables
Adds database tables and fields to the SQL entity query.

Namespace

Drupal\Core\Entity\Query\Sql

Code

public function addField($field, $type, $langcode) {
    $entity_type_id = $this->sqlQuery
        ->getMetaData('entity_type');
    $all_revisions = $this->sqlQuery
        ->getMetaData('all_revisions');
    // This variable ensures grouping works correctly. For example, given the
    // following conditions:
    // ->condition('tags', 2, '>')
    // ->condition('tags', 20, '<')
    // ->condition('node_reference.nid.entity.tags', 2)
    // The first two should use the same table but the last one needs to be a
    // new table. So for the first two, the table array index will be 'tags'
    // while the third will be 'node_reference.nid.tags'.
    $index_prefix = '';
    $specifiers = explode('.', $field);
    $base_table = 'base_table';
    $count = count($specifiers) - 1;
    // This will contain the definitions of the last specifier seen by the
    // system.
    $propertyDefinitions = [];
    $entity_type = $this->entityTypeManager
        ->getActiveDefinition($entity_type_id);
    $field_storage_definitions = $this->entityFieldManager
        ->getActiveFieldStorageDefinitions($entity_type_id);
    for ($key = 0; $key <= $count; $key++) {
        // This can either be the name of an entity base field or a configurable
        // field.
        $specifier = $specifiers[$key];
        if (isset($field_storage_definitions[$specifier])) {
            $field_storage = $field_storage_definitions[$specifier];
            $column = $field_storage->getMainPropertyName();
        }
        else {
            $field_storage = FALSE;
            $column = NULL;
        }
        // If there is revision support, all the revisions are being queried, and
        // the field is revisionable or the revision ID field itself, then use the
        // revision ID. Otherwise, the entity ID will do.
        // Where there is revision support, all the revisions are being queried:
        // so if the field is revisionable then use the revision ID, otherwise use
        // the entity ID.
        $query_revisions = $all_revisions && $field_storage && ($field_storage->isRevisionable() || $field_storage->getName() === $entity_type->getKey('revision'));
        if ($query_revisions) {
            // This contains the relevant SQL field to be used when joining entity
            // tables.
            $entity_id_field = $entity_type->getKey('revision');
            // This contains the relevant SQL field to be used when joining field
            // tables.
            $field_id_field = 'revision_id';
        }
        else {
            $entity_id_field = $entity_type->getKey('id');
            $field_id_field = 'entity_id';
        }
        
        /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
        $table_mapping = $this->entityTypeManager
            ->getStorage($entity_type_id)
            ->getTableMapping();
        // Check whether this field is stored in a dedicated table.
        if ($field_storage && $table_mapping->requiresDedicatedTableStorage($field_storage)) {
            $delta = NULL;
            if ($key < $count) {
                $next = $specifiers[$key + 1];
                // If this is a numeric specifier we're adding a condition on the
                // specific delta.
                if (is_numeric($next)) {
                    $delta = $next;
                    $index_prefix .= ".{$delta}";
                    // Do not process it again.
                    $key++;
                    $next = $specifiers[$key + 1];
                }
                elseif ($next == TableMappingInterface::DELTA) {
                    $index_prefix .= TableMappingInterface::DELTA;
                    // Do not process it again.
                    $key++;
                    // If there are more specifiers to work with then continue
                    // processing. If this is the last specifier then use the reserved
                    // keyword as a column name.
                    if ($key < $count) {
                        $next = $specifiers[$key + 1];
                    }
                    else {
                        $column = TableMappingInterface::DELTA;
                    }
                }
                // Is this a field column?
                $columns = $field_storage->getColumns();
                if (isset($columns[$next]) || in_array($next, $table_mapping->getReservedColumns())) {
                    // Use it.
                    $column = $next;
                    // Do not process it again.
                    $key++;
                }
                // If there are more specifiers, the next one must be a
                // relationship. Either the field name followed by a relationship
                // specifier, for example $node->field_image->entity, or a field
                // column followed by a relationship specifier, for example
                // $node->field_image->fid->entity. In both cases, prepare the
                // property definitions for the relationship. In the first case,
                // also use the property definitions for column.
                if ($key < $count) {
                    $relationship_specifier = $specifiers[$key + 1];
                    $propertyDefinitions = $field_storage->getPropertyDefinitions();
                    // Prepare the next index prefix.
                    $next_index_prefix = "{$relationship_specifier}.{$column}";
                }
            }
            $table = $this->ensureFieldTable($index_prefix, $field_storage, $type, $langcode, $base_table, $entity_id_field, $field_id_field, $delta);
            $sql_column = $table_mapping->getFieldColumnName($field_storage, $column);
        }
        else {
            // ensureEntityTable() determines whether an entity property will be
            // queried from the data table or the base table depending on where it
            // first finds the property. The data table is preferred, which is why
            // it gets added before the base table.
            $entity_tables = [];
            $revision_table = NULL;
            if ($query_revisions) {
                $data_table = $entity_type->getRevisionDataTable();
                $entity_base_table = $entity_type->getRevisionTable();
            }
            else {
                $data_table = $entity_type->getDataTable();
                $entity_base_table = $entity_type->getBaseTable();
                if ($field_storage && $field_storage->isRevisionable() && in_array($field_storage->getName(), $entity_type->getRevisionMetadataKeys())) {
                    $revision_table = $entity_type->getRevisionTable();
                }
            }
            if ($data_table) {
                $this->sqlQuery
                    ->addMetaData('simple_query', FALSE);
                $entity_tables[$data_table] = $this->getTableMapping($data_table, $entity_type_id);
            }
            if ($revision_table) {
                $entity_tables[$revision_table] = $this->getTableMapping($revision_table, $entity_type_id);
            }
            $entity_tables[$entity_base_table] = $this->getTableMapping($entity_base_table, $entity_type_id);
            $sql_column = $specifier;
            // If there are more specifiers, get the right sql column name if the
            // next one is a column of this field.
            if ($key < $count) {
                $next = $specifiers[$key + 1];
                // If this specifier is the reserved keyword "%delta" we're adding a
                // condition on a delta range.
                if ($next == TableMappingInterface::DELTA) {
                    $key++;
                    if ($key < $count) {
                        $next = $specifiers[$key + 1];
                    }
                    else {
                        return 0;
                    }
                }
                // If this is a numeric specifier then, add a condition on the
                // specific delta. Since this is a single value base field, the only
                // value that makes sense is 0.
                if (is_numeric($next)) {
                    if ($next > 0) {
                        $this->sqlQuery
                            ->alwaysFalse();
                    }
                    $key++;
                    $next = $specifiers[$key + 1];
                }
                // Is this a field column?
                $columns = $field_storage->getColumns();
                if (isset($columns[$next]) || in_array($next, $table_mapping->getReservedColumns())) {
                    // Use it.
                    $sql_column = $table_mapping->getFieldColumnName($field_storage, $next);
                    // Do not process it again.
                    $key++;
                }
            }
            elseif ($field_storage && $column) {
                $columns = $field_storage->getColumns();
                if (isset($columns[$column])) {
                    $sql_column = $table_mapping->getFieldColumnName($field_storage, $column);
                }
            }
            $table = $this->ensureEntityTable($index_prefix, $sql_column, $type, $langcode, $base_table, $entity_id_field, $entity_tables);
        }
        // If there is both a field storage and a field column then check for case
        // sensitivity.
        if ($field_storage && $column) {
            $property_definitions = $field_storage->getPropertyDefinitions();
            if (isset($property_definitions[$column])) {
                $this->caseSensitiveFields[$field] = $property_definitions[$column]->getSetting('case_sensitive');
            }
        }
        // If there are more specifiers to come, it is a relationship.
        if ($field_storage && $key < $count) {
            // Computed fields have prepared their property definition already, do
            // it for properties as well.
            if (!$propertyDefinitions) {
                $propertyDefinitions = $field_storage->getPropertyDefinitions();
                $relationship_specifier = $specifiers[$key + 1];
                $next_index_prefix = $relationship_specifier;
            }
            $entity_type_id = NULL;
            // Relationship specifier can also contain the entity type ID, i.e.
            // entity:node, entity:user or entity:taxonomy.
            if (str_contains($relationship_specifier, ':')) {
                [
                    $relationship_specifier,
                    $entity_type_id,
                ] = explode(':', $relationship_specifier, 2);
            }
            // Check for a valid relationship.
            if (isset($propertyDefinitions[$relationship_specifier]) && $propertyDefinitions[$relationship_specifier] instanceof DataReferenceDefinitionInterface) {
                // If it is valid then use the entity type if it is already specified,
                // otherwise use the definition.
                $target_definition = $propertyDefinitions[$relationship_specifier]->getTargetDefinition();
                if (!$entity_type_id && $target_definition instanceof EntityDataDefinitionInterface) {
                    $entity_type_id = $target_definition->getEntityTypeId();
                }
                $entity_type = $this->entityTypeManager
                    ->getActiveDefinition($entity_type_id);
                $field_storage_definitions = $this->entityFieldManager
                    ->getActiveFieldStorageDefinitions($entity_type_id);
                // Add the new entity base table using the table and sql column.
                $base_table = $this->addNextBaseTable($entity_type, $table, $sql_column, $field_storage);
                $propertyDefinitions = [];
                $key++;
                $index_prefix .= "{$next_index_prefix}.";
            }
            else {
                throw new QueryException("Invalid specifier '{$relationship_specifier}'");
            }
        }
    }
    return "{$table}.{$sql_column}";
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.