function Select::__toString

Same name in other branches
  1. 9 core/lib/Drupal/Core/Database/Query/Select.php \Drupal\Core\Database\Query\Select::__toString()
  2. 8.9.x core/lib/Drupal/Core/Database/Query/Select.php \Drupal\Core\Database\Query\Select::__toString()
  3. 11.x core/lib/Drupal/Core/Database/Query/Select.php \Drupal\Core\Database\Query\Select::__toString()

Overrides Query::__toString

File

core/lib/Drupal/Core/Database/Query/Select.php, line 802

Class

Select
Query builder for SELECT statements.

Namespace

Drupal\Core\Database\Query

Code

public function __toString() {
    if (!is_array($this->fields) || !is_array($this->expressions) || !is_array($this->tables) || !is_array($this->order) || !is_array($this->group) || !is_array($this->union)) {
        throw new \UnexpectedValueException();
    }
    // For convenience, we compile the query ourselves if the caller forgot
    // to do it. This allows constructs like "(string) $query" to work. When
    // the query will be executed, it will be recompiled using the proper
    // placeholder generator anyway.
    if (!$this->compiled()) {
        $this->compile($this->connection, $this);
    }
    // Create a sanitized comment string to prepend to the query.
    $comments = $this->connection
        ->makeComment($this->comments);
    // SELECT
    $query = $comments . 'SELECT ';
    if ($this->distinct) {
        $query .= 'DISTINCT ';
    }
    // FIELDS and EXPRESSIONS
    $fields = [];
    foreach ($this->tables as $alias => $table) {
        if (!empty($table['all_fields'])) {
            $fields[] = $this->connection
                ->escapeAlias($alias) . '.*';
        }
    }
    foreach ($this->fields as $field) {
        // Note that $field['table'] holds the table_alias.
        // @see \Drupal\Core\Database\Query\Select::addField
        $table = isset($field['table']) ? $field['table'] . '.' : '';
        // Always use the AS keyword for field aliases, as some
        // databases require it (e.g., PostgreSQL).
        $fields[] = $this->connection
            ->escapeField($table . $field['field']) . ' AS ' . $this->connection
            ->escapeAlias($field['alias']);
    }
    foreach ($this->expressions as $expression) {
        $fields[] = $expression['expression'] . ' AS ' . $this->connection
            ->escapeAlias($expression['alias']);
    }
    $query .= implode(', ', $fields);
    // FROM - We presume all queries have a FROM, as any query that doesn't won't need the query builder anyway.
    $query .= "\nFROM";
    foreach ($this->tables as $table) {
        $query .= "\n";
        if (isset($table['join type'])) {
            $query .= $table['join type'] . ' JOIN ';
        }
        // If the table is a subquery, compile it and integrate it into this query.
        if ($table['table'] instanceof SelectInterface) {
            // Run preparation steps on this sub-query before converting to string.
            $subquery = $table['table'];
            $subquery->preExecute();
            $table_string = '(' . (string) $subquery . ')';
        }
        else {
            $table_string = $this->connection
                ->escapeTable($table['table']);
            // Do not attempt prefixing cross database / schema queries.
            if (!str_contains($table_string, '.')) {
                $table_string = '{' . $table_string . '}';
            }
        }
        // Don't use the AS keyword for table aliases, as some
        // databases don't support it (e.g., Oracle).
        $query .= $table_string . ' ' . $this->connection
            ->escapeAlias($table['alias']);
        if (!empty($table['condition'])) {
            $query .= ' ON ' . (string) $table['condition'];
        }
    }
    // WHERE
    if (count($this->condition)) {
        // There is an implicit string cast on $this->condition.
        $query .= "\nWHERE " . $this->condition;
    }
    // GROUP BY
    if ($this->group) {
        $group_by_fields = array_map(function (string $field) : string {
            return $this->connection
                ->escapeField($field);
        }, $this->group);
        $query .= "\nGROUP BY " . implode(', ', $group_by_fields);
    }
    // HAVING
    if (count($this->having)) {
        // There is an implicit string cast on $this->having.
        $query .= "\nHAVING " . $this->having;
    }
    // UNION is a little odd, as the select queries to combine are passed into
    // this query, but syntactically they all end up on the same level.
    if ($this->union) {
        foreach ($this->union as $union) {
            $query .= ' ' . $union['type'] . ' ' . (string) $union['query'];
        }
    }
    // ORDER BY
    if ($this->order) {
        $query .= "\nORDER BY ";
        $fields = [];
        foreach ($this->order as $field => $direction) {
            $fields[] = $this->connection
                ->escapeField($field) . ' ' . $direction;
        }
        $query .= implode(', ', $fields);
    }
    // RANGE
    // There is no universal SQL standard for handling range or limit clauses.
    // Fortunately, all core-supported databases use the same range syntax.
    // Databases that need a different syntax can override this method and
    // do whatever alternate logic they need to.
    if (!empty($this->range)) {
        $query .= "\nLIMIT " . (int) $this->range['length'] . " OFFSET " . (int) $this->range['start'];
    }
    if ($this->forUpdate) {
        $query .= ' FOR UPDATE';
    }
    return $query;
}

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