View source
<?php
namespace Drupal\Core\Database\Query;
use Drupal\Core\Database\Database;
use Drupal\Core\Database\Connection;
class Select extends Query implements SelectInterface {
use QueryConditionTrait;
protected $fields = [];
protected $expressions = [];
protected $tables = [];
protected $order = [];
protected $group = [];
protected $having;
protected $distinct = FALSE;
protected $range;
protected $union = [];
protected $prepared = FALSE;
protected $forUpdate = FALSE;
public function __construct($table, $alias, Connection $connection, $options = []) {
$options['return'] = Database::RETURN_STATEMENT;
parent::__construct($connection, $options);
$conjunction = isset($options['conjunction']) ? $options['conjunction'] : 'AND';
$this->condition = $this->connection
->condition($conjunction);
$this->having = $this->connection
->condition($conjunction);
$this
->addJoin(NULL, $table, $alias);
}
public function addTag($tag) {
$this->alterTags[$tag] = 1;
return $this;
}
public function hasTag($tag) {
return isset($this->alterTags[$tag]);
}
public function hasAllTags() {
return !(bool) array_diff(func_get_args(), array_keys($this->alterTags));
}
public function hasAnyTag() {
return (bool) array_intersect(func_get_args(), array_keys($this->alterTags));
}
public function addMetaData($key, $object) {
$this->alterMetaData[$key] = $object;
return $this;
}
public function getMetaData($key) {
return isset($this->alterMetaData[$key]) ? $this->alterMetaData[$key] : NULL;
}
public function arguments() {
if (!$this
->compiled()) {
return NULL;
}
$args = $this->condition
->arguments() + $this->having
->arguments();
foreach ($this->tables as $table) {
if ($table['arguments']) {
$args += $table['arguments'];
}
if ($table['table'] instanceof SelectInterface) {
$args += $table['table']
->arguments();
}
if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
$args += $table['condition']
->arguments();
}
}
foreach ($this->expressions as $expression) {
if ($expression['arguments']) {
$args += $expression['arguments'];
}
}
foreach ($this->union as $union) {
$args += $union['query']
->arguments();
}
return $args;
}
public function compile(Connection $connection, PlaceholderInterface $queryPlaceholder) {
$this->condition
->compile($connection, $queryPlaceholder);
$this->having
->compile($connection, $queryPlaceholder);
foreach ($this->tables as $table) {
if ($table['table'] instanceof SelectInterface) {
$table['table']
->compile($connection, $queryPlaceholder);
}
if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
$table['condition']
->compile($connection, $queryPlaceholder);
}
}
foreach ($this->union as $union) {
$union['query']
->compile($connection, $queryPlaceholder);
}
}
public function compiled() {
if (!$this->condition
->compiled() || !$this->having
->compiled()) {
return FALSE;
}
foreach ($this->tables as $table) {
if ($table['table'] instanceof SelectInterface) {
if (!$table['table']
->compiled()) {
return FALSE;
}
}
if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
if (!$table['condition']
->compiled()) {
return FALSE;
}
}
}
foreach ($this->union as $union) {
if (!$union['query']
->compiled()) {
return FALSE;
}
}
return TRUE;
}
public function havingCondition($field, $value = NULL, $operator = NULL) {
$this->having
->condition($field, $value, $operator);
return $this;
}
public function &havingConditions() {
return $this->having
->conditions();
}
public function havingArguments() {
return $this->having
->arguments();
}
public function having($snippet, $args = []) {
$this->having
->where($snippet, $args);
return $this;
}
public function havingCompile(Connection $connection) {
$this->having
->compile($connection, $this);
}
public function extend($extender_name) {
$override_class = $extender_name . '_' . $this->connection
->driver();
if (class_exists($override_class)) {
$extender_name = $override_class;
}
return new $extender_name($this, $this->connection);
}
public function havingIsNull($field) {
$this->having
->isNull($field);
return $this;
}
public function havingIsNotNull($field) {
$this->having
->isNotNull($field);
return $this;
}
public function havingExists(SelectInterface $select) {
$this->having
->exists($select);
return $this;
}
public function havingNotExists(SelectInterface $select) {
$this->having
->notExists($select);
return $this;
}
public function forUpdate($set = TRUE) {
if (isset($set)) {
$this->forUpdate = $set;
}
return $this;
}
public function &getFields() {
return $this->fields;
}
public function &getExpressions() {
return $this->expressions;
}
public function &getOrderBy() {
return $this->order;
}
public function &getGroupBy() {
return $this->group;
}
public function &getTables() {
return $this->tables;
}
public function &getUnion() {
return $this->union;
}
public function escapeLike($string) {
return $this->connection
->escapeLike($string);
}
public function escapeField($string) {
return $this->connection
->escapeField($string);
}
public function getArguments(PlaceholderInterface $queryPlaceholder = NULL) {
if (!isset($queryPlaceholder)) {
$queryPlaceholder = $this;
}
$this
->compile($this->connection, $queryPlaceholder);
return $this
->arguments();
}
public function isPrepared() {
return $this->prepared;
}
public function preExecute(SelectInterface $query = NULL) {
if (!isset($query)) {
$query = $this;
}
if ($query
->isPrepared()) {
return TRUE;
}
if (isset($this->alterTags)) {
$term_access_tags = [
'term_access' => 1,
'taxonomy_term_access' => 1,
];
if (array_intersect_key($this->alterTags, $term_access_tags)) {
$this->alterTags += $term_access_tags;
}
$hooks = [
'query',
];
foreach ($this->alterTags as $tag => $value) {
$hooks[] = 'query_' . $tag;
}
\Drupal::moduleHandler()
->alter($hooks, $query);
}
$this->prepared = TRUE;
foreach ($this->tables as $table) {
if ($table['table'] instanceof SelectInterface) {
$table['table']
->preExecute();
}
}
foreach ($this->union as $union) {
$union['query']
->preExecute();
}
return $this->prepared;
}
public function execute() {
if (!$this
->preExecute()) {
return NULL;
}
$args = $this
->getArguments();
return $this->connection
->query((string) $this, $args, $this->queryOptions);
}
public function distinct($distinct = TRUE) {
$this->distinct = $distinct;
return $this;
}
public function addField($table_alias, $field, $alias = NULL) {
if (empty($alias)) {
$alias = $field;
}
if (!empty($this->fields[$alias])) {
$alias = $table_alias . '_' . $field;
}
$alias_candidate = $alias;
$count = 2;
while (!empty($this->fields[$alias_candidate])) {
$alias_candidate = $alias . '_' . $count++;
}
$alias = $alias_candidate;
$this->fields[$alias] = [
'field' => $field,
'table' => $table_alias,
'alias' => $alias,
];
return $alias;
}
public function fields($table_alias, array $fields = []) {
if ($fields) {
foreach ($fields as $field) {
$this
->addField($table_alias, $field);
}
}
else {
$this->tables[$table_alias]['all_fields'] = TRUE;
}
return $this;
}
public function addExpression($expression, $alias = NULL, $arguments = []) {
if (empty($alias)) {
$alias = 'expression';
}
$alias_candidate = $alias;
$count = 2;
while (!empty($this->expressions[$alias_candidate])) {
$alias_candidate = $alias . '_' . $count++;
}
$alias = $alias_candidate;
$this->expressions[$alias] = [
'expression' => $expression,
'alias' => $alias,
'arguments' => $arguments,
];
return $alias;
}
public function join($table, $alias = NULL, $condition = NULL, $arguments = []) {
return $this
->addJoin('INNER', $table, $alias, $condition, $arguments);
}
public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = []) {
return $this
->addJoin('INNER', $table, $alias, $condition, $arguments);
}
public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = []) {
return $this
->addJoin('LEFT OUTER', $table, $alias, $condition, $arguments);
}
public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = []) {
return $this
->addJoin('RIGHT OUTER', $table, $alias, $condition, $arguments);
}
public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = []) {
if (empty($alias)) {
if ($table instanceof SelectInterface) {
$alias = 'subquery';
}
else {
$alias = $table;
}
}
$alias_candidate = $alias;
$count = 2;
while (!empty($this->tables[$alias_candidate])) {
$alias_candidate = $alias . '_' . $count++;
}
$alias = $alias_candidate;
if (is_string($condition)) {
$condition = str_replace('%alias', $alias, $condition);
}
$this->tables[$alias] = [
'join type' => $type,
'table' => $table,
'alias' => $alias,
'condition' => $condition,
'arguments' => $arguments,
];
return $alias;
}
public function orderBy($field, $direction = 'ASC') {
$direction = strtoupper($direction) == 'DESC' ? 'DESC' : 'ASC';
$this->order[$field] = $direction;
return $this;
}
public function orderRandom() {
$alias = $this
->addExpression('RAND()', 'random_field');
$this
->orderBy($alias);
return $this;
}
public function range($start = NULL, $length = NULL) {
$this->range = $start !== NULL ? [
'start' => $start,
'length' => $length,
] : [];
return $this;
}
public function union(SelectInterface $query, $type = '') {
switch ($type) {
case 'DISTINCT':
case '':
$type = 'UNION';
break;
case 'ALL':
$type = 'UNION ALL';
default:
}
$this->union[] = [
'type' => $type,
'query' => $query,
];
return $this;
}
public function groupBy($field) {
$this->group[$field] = $field;
return $this;
}
public function countQuery() {
$count = $this
->prepareCountQuery();
$query = $this->connection
->select($count, NULL, $this->queryOptions);
$query
->addExpression('COUNT(*)');
return $query;
}
protected function prepareCountQuery() {
$count = clone $this;
$group_by = $count
->getGroupBy();
$having = $count
->havingConditions();
if (!$count->distinct && !isset($having[0])) {
$fields =& $count
->getFields();
foreach (array_keys($fields) as $field) {
if (empty($group_by[$field])) {
unset($fields[$field]);
}
}
$expressions =& $count
->getExpressions();
foreach (array_keys($expressions) as $field) {
if (empty($group_by[$field])) {
unset($expressions[$field]);
}
}
foreach ($count->tables as &$table) {
unset($table['all_fields']);
}
}
$count
->addExpression('1');
$orders =& $count
->getOrderBy();
$orders = [];
if ($count->distinct && !empty($group_by)) {
$count->distinct = FALSE;
}
foreach ($count->union as &$union) {
$union['query'] = $union['query']
->prepareCountQuery();
}
return $count;
}
public function __toString() {
if (!$this
->compiled()) {
$this
->compile($this->connection, $this);
}
$comments = $this->connection
->makeComment($this->comments);
$query = $comments . 'SELECT ';
if ($this->distinct) {
$query .= 'DISTINCT ';
}
$fields = [];
foreach ($this->tables as $alias => $table) {
if (!empty($table['all_fields'])) {
$fields[] = $this->connection
->escapeAlias($alias) . '.*';
}
}
foreach ($this->fields as $field) {
$table = isset($field['table']) ? $this->connection
->escapeAlias($field['table']) . '.' : '';
$fields[] = $table . $this->connection
->escapeField($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);
$query .= "\nFROM";
foreach ($this->tables as $table) {
$query .= "\n";
if (isset($table['join type'])) {
$query .= $table['join type'] . ' JOIN ';
}
if ($table['table'] instanceof SelectInterface) {
$subquery = $table['table'];
$subquery
->preExecute();
$table_string = '(' . (string) $subquery . ')';
}
else {
$table_string = $this->connection
->escapeTable($table['table']);
if (strpos($table_string, '.') === FALSE) {
$table_string = '{' . $table_string . '}';
}
}
$query .= $table_string . ' ' . $this->connection
->escapeAlias($table['alias']);
if (!empty($table['condition'])) {
$query .= ' ON ' . (string) $table['condition'];
}
}
if (count($this->condition)) {
$query .= "\nWHERE " . $this->condition;
}
if ($this->group) {
$query .= "\nGROUP BY " . implode(', ', $this->group);
}
if (count($this->having)) {
$query .= "\nHAVING " . $this->having;
}
if ($this->union) {
foreach ($this->union as $union) {
$query .= ' ' . $union['type'] . ' ' . (string) $union['query'];
}
}
if ($this->order) {
$query .= "\nORDER BY ";
$fields = [];
foreach ($this->order as $field => $direction) {
$fields[] = $this->connection
->escapeField($field) . ' ' . $direction;
}
$query .= implode(', ', $fields);
}
if (!empty($this->range)) {
$query .= "\nLIMIT " . (int) $this->range['length'] . " OFFSET " . (int) $this->range['start'];
}
if ($this->forUpdate) {
$query .= ' FOR UPDATE';
}
return $query;
}
public function __clone() {
parent::__clone();
$this->condition = clone $this->condition;
$this->having = clone $this->having;
foreach ($this->union as $key => $aggregate) {
$this->union[$key]['query'] = clone $aggregate['query'];
}
foreach ($this->tables as $alias => $table) {
if ($table['table'] instanceof SelectInterface) {
$this->tables[$alias]['table'] = clone $table['table'];
}
}
}
}