2 declare(strict_types
=1);
3 namespace TYPO3\CMS\Core\Database\Query
;
6 * This file is part of the TYPO3 CMS project.
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
15 * The TYPO3 project - inspiring people to share!
18 use Doctrine\DBAL\Platforms\SQLServerPlatform
;
19 use Doctrine\DBAL\Query\Expression\CompositeExpression
;
20 use TYPO3\CMS\Core\Database\Connection
;
21 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder
;
22 use TYPO3\CMS\Core\Database\Query\Restriction\DefaultRestrictionContainer
;
23 use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionContainerInterface
;
24 use TYPO3\CMS\Core\Utility\GeneralUtility
;
27 * Object oriented approach to building SQL queries.
29 * It's a facade to the Doctrine DBAL QueryBuilder that implements PHP7 type hinting and automatic
30 * quoting of table and column names.
33 * $query->select('aField', 'anotherField')
35 * ->where($query->expr()->eq('aField', 1))
36 * ->andWhere($query->expr()->gte('anotherField',10'))
40 * Additional functionality included is support for COUNT() and TRUNCATE() statements.
45 * The DBAL Connection.
49 protected $connection;
52 * @var \Doctrine\DBAL\Query\QueryBuilder
54 protected $concreteQueryBuilder;
57 * @var QueryRestrictionContainerInterface
59 protected $restrictionContainer;
62 * Initializes a new QueryBuilder.
64 * @param Connection $connection The DBAL Connection.
65 * @param QueryRestrictionContainerInterface $restrictionContainer
66 * @param \Doctrine\DBAL\Query\QueryBuilder $concreteQueryBuilder
68 public function __construct(
69 Connection
$connection,
70 QueryRestrictionContainerInterface
$restrictionContainer = null,
71 \Doctrine\DBAL\Query\QueryBuilder
$concreteQueryBuilder = null
73 $this->connection
= $connection;
74 $this->restrictionContainer
= $restrictionContainer ?
: GeneralUtility
::makeInstance(DefaultRestrictionContainer
::class);
75 $this->concreteQueryBuilder
= $concreteQueryBuilder ?
: GeneralUtility
::makeInstance(\Doctrine\DBAL\Query\QueryBuilder
::class, $connection);
79 * @return QueryRestrictionContainerInterface
81 public function getRestrictions()
83 return $this->restrictionContainer
;
87 * @param QueryRestrictionContainerInterface $restrictionContainer
89 public function setRestrictions(QueryRestrictionContainerInterface
$restrictionContainer)
91 $this->restrictionContainer
= $restrictionContainer;
95 * Re-apply default restrictions
97 public function resetRestrictions()
99 $this->restrictionContainer
= GeneralUtility
::makeInstance(DefaultRestrictionContainer
::class);
103 * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
104 * This producer method is intended for convenient inline usage. Example:
106 * For more complex expression construction, consider storing the expression
107 * builder object in a local variable.
109 * @return ExpressionBuilder
111 public function expr(): ExpressionBuilder
113 return $this->connection
->getExpressionBuilder();
117 * Gets the type of the currently built query.
122 public function getType(): int
124 return $this->concreteQueryBuilder
->getType();
128 * Gets the associated DBAL Connection for this query builder.
132 public function getConnection(): Connection
134 return $this->connection
;
138 * Gets the state of this query builder instance.
140 * @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
143 public function getState(): int
145 return $this->concreteQueryBuilder
->getState();
149 * Gets the concrete implementation of the query builder
151 * @return \Doctrine\DBAL\Query\QueryBuilder
154 public function getConcreteQueryBuilder(): \Doctrine\DBAL\Query\QueryBuilder
156 return $this->concreteQueryBuilder
;
160 * Executes this query using the bound parameters and their types.
162 * @return \Doctrine\DBAL\Driver\Statement|int
164 public function execute()
166 if ($this->getType() !== \Doctrine\DBAL\Query\QueryBuilder
::SELECT
) {
167 return $this->concreteQueryBuilder
->execute();
170 // Set additional query restrictions
171 $originalWhereConditions = $this->addAdditionalWhereConditions();
173 $result = $this->concreteQueryBuilder
->execute();
175 // Restore the original query conditions in case the user keeps
176 // on modifying the state.
177 $this->concreteQueryBuilder
->add('where', $originalWhereConditions, false);
183 * Gets the complete SQL string formed by the current specifications of this QueryBuilder.
185 * If the statement is a SELECT TYPE query restrictions based on TCA settings will
186 * automatically be applied based on the current QuerySettings.
188 * @return string The SQL query string.
190 public function getSQL(): string
192 if ($this->getType() !== \Doctrine\DBAL\Query\QueryBuilder
::SELECT
) {
193 return $this->concreteQueryBuilder
->getSQL();
196 // Set additional query restrictions
197 $originalWhereConditions = $this->addAdditionalWhereConditions();
199 $sql = $this->concreteQueryBuilder
->getSQL();
201 // Restore the original query conditions in case the user keeps
202 // on modifying the state.
203 $this->concreteQueryBuilder
->add('where', $originalWhereConditions, false);
209 * Sets a query parameter for the query being constructed.
211 * @param string|int $key The parameter position or name.
212 * @param mixed $value The parameter value.
213 * @param int|null $type One of the Connection::PARAM_* constants.
215 * @return QueryBuilder This QueryBuilder instance.
217 public function setParameter($key, $value, int $type = null): QueryBuilder
219 $this->concreteQueryBuilder
->setParameter($key, $value, $type);
225 * Sets a collection of query parameters for the query being constructed.
227 * @param array $params The query parameters to set.
228 * @param array $types The query parameters types to set.
230 * @return QueryBuilder This QueryBuilder instance.
232 public function setParameters(array $params, array $types = []): QueryBuilder
234 $this->concreteQueryBuilder
->setParameters($params, $types);
240 * Gets all defined query parameters for the query being constructed indexed by parameter index or name.
242 * @return array The currently defined query parameters indexed by parameter index or name.
244 public function getParameters(): array
246 return $this->concreteQueryBuilder
->getParameters();
250 * Gets a (previously set) query parameter of the query being constructed.
252 * @param string|int $key The key (index or name) of the bound parameter.
254 * @return mixed The value of the bound parameter.
256 public function getParameter($key)
258 return $this->concreteQueryBuilder
->getParameter($key);
262 * Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
264 * @return array The currently defined query parameter types indexed by parameter index or name.
266 public function getParameterTypes(): array
268 return $this->concreteQueryBuilder
->getParameterTypes();
272 * Gets a (previously set) query parameter type of the query being constructed.
274 * @param string|int $key The key (index or name) of the bound parameter type.
276 * @return mixed The value of the bound parameter type.
278 public function getParameterType($key)
280 return $this->concreteQueryBuilder
->getParameterType($key);
284 * Sets the position of the first result to retrieve (the "offset").
286 * @param int $firstResult The first result to return.
288 * @return QueryBuilder This QueryBuilder instance.
290 public function setFirstResult(int $firstResult): QueryBuilder
292 $this->concreteQueryBuilder
->setFirstResult($firstResult);
298 * Gets the position of the first result the query object was set to retrieve (the "offset").
299 * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
301 * @return int The position of the first result.
303 public function getFirstResult(): int
305 return (int)$this->concreteQueryBuilder
->getFirstResult();
309 * Sets the maximum number of results to retrieve (the "limit").
311 * @param int $maxResults The maximum number of results to retrieve.
313 * @return QueryBuilder This QueryBuilder instance.
315 public function setMaxResults(int $maxResults): QueryBuilder
317 $this->concreteQueryBuilder
->setMaxResults($maxResults);
323 * Gets the maximum number of results the query object was set to retrieve (the "limit").
324 * Returns 0 if setMaxResults was not applied to this query builder.
326 * @return int The maximum number of results.
328 public function getMaxResults(): int
330 return (int)$this->concreteQueryBuilder
->getMaxResults();
334 * Either appends to or replaces a single, generic query part.
336 * The available parts are: 'select', 'from', 'set', 'where',
337 * 'groupBy', 'having' and 'orderBy'.
339 * @param string $sqlPartName
340 * @param string $sqlPart
341 * @param bool $append
343 * @return QueryBuilder This QueryBuilder instance.
345 public function add(string $sqlPartName, string $sqlPart, bool $append = false): QueryBuilder
347 $this->concreteQueryBuilder
->add($sqlPartName, $sqlPart, $append);
353 * Specifies the item that is to be counted in the query result.
354 * Replaces any previously specified selections, if any.
356 * @param string $item Will be quoted according to database platform automatically.
357 * @return QueryBuilder This QueryBuilder instance.
359 public function count(string $item): QueryBuilder
361 $countExpr = $this->getConnection()->getDatabasePlatform()->getCountExpression(
362 $item === '*' ?
$item : $this->quoteIdentifier($item)
364 $this->concreteQueryBuilder
->select($countExpr);
370 * Specifies items that are to be returned in the query result.
371 * Replaces any previously specified selections, if any.
373 * @param string[] $selects
374 * @return QueryBuilder This QueryBuilder instance.
376 public function select(string ...$selects): QueryBuilder
378 $this->concreteQueryBuilder
->select(...$this->quoteIdentifiersForSelect($selects));
384 * Adds an item that is to be returned in the query result.
386 * @param string[] $selects The selection expression.
388 * @return QueryBuilder This QueryBuilder instance.
390 public function addSelect(string ...$selects): QueryBuilder
392 $this->concreteQueryBuilder
->addSelect(...$this->quoteIdentifiersForSelect($selects));
398 * Specifies items that are to be returned in the query result.
399 * Replaces any previously specified selections, if any.
400 * This should only be used for literal SQL expressions as no
401 * quoting/escaping of any kind will be performed on the items.
403 * @param string[] $selects Literal SQL expressions to be selected. Warning: No quoting will be done!
404 * @return QueryBuilder This QueryBuilder instance.
406 public function selectLiteral(string ...$selects): QueryBuilder
408 $this->concreteQueryBuilder
->select(...$selects);
414 * Adds an item that is to be returned in the query result. This should
415 * only be used for literal SQL expressions as no quoting/escaping of
416 * any kind will be performed on the items.
418 * @param string[] $selects Literal SQL expressions to be selected.
419 * @return QueryBuilder This QueryBuilder instance.
421 public function addSelectLiteral(string ...$selects): QueryBuilder
423 $this->concreteQueryBuilder
->addSelect(...$selects);
429 * Turns the query being built into a bulk delete query that ranges over
432 * @param string $delete The table whose rows are subject to the deletion.
433 * Will be quoted according to database platform automatically.
434 * @param string $alias The table alias used in the constructed query.
435 * Will be quoted according to database platform automatically.
437 * @return QueryBuilder This QueryBuilder instance.
439 public function delete(string $delete, string $alias = null): QueryBuilder
441 $this->concreteQueryBuilder
->delete(
442 $this->quoteIdentifier($delete),
443 empty($alias) ?
$alias : $this->quoteIdentifier($alias)
450 * Turns the query being built into a bulk update query that ranges over
453 * @param string $update The table whose rows are subject to the update.
454 * @param string $alias The table alias used in the constructed query.
456 * @return QueryBuilder This QueryBuilder instance.
458 public function update(string $update, string $alias = null): QueryBuilder
460 $this->concreteQueryBuilder
->update(
461 $this->quoteIdentifier($update),
462 empty($alias) ?
$alias : $this->quoteIdentifier($alias)
469 * Turns the query being built into an insert query that inserts into
472 * @param string $insert The table into which the rows should be inserted.
474 * @return QueryBuilder This QueryBuilder instance.
476 public function insert(string $insert): QueryBuilder
478 $this->concreteQueryBuilder
->insert($this->quoteIdentifier($insert));
484 * Creates and adds a query root corresponding to the table identified by the
485 * given alias, forming a cartesian product with any existing query roots.
487 * @param string $from The table. Will be quoted according to database platform automatically.
488 * @param string $alias The alias of the table. Will be quoted according to database platform automatically.
490 * @return QueryBuilder This QueryBuilder instance.
492 public function from(string $from, string $alias = null): QueryBuilder
494 $this->concreteQueryBuilder
->from(
495 $this->quoteIdentifier($from),
496 empty($alias) ?
$alias : $this->quoteIdentifier($alias)
503 * Creates and adds a join to the query.
505 * @param string $fromAlias The alias that points to a from clause.
506 * @param string $join The table name to join.
507 * @param string $alias The alias of the join table.
508 * @param string $condition The condition for the join.
510 * @return QueryBuilder This QueryBuilder instance.
512 public function join(string $fromAlias, string $join, string $alias, string $condition = null): QueryBuilder
514 $this->concreteQueryBuilder
->innerJoin(
515 $this->quoteIdentifier($fromAlias),
516 $this->quoteIdentifier($join),
517 $this->quoteIdentifier($alias),
525 * Creates and adds a join to the query.
527 * @param string $fromAlias The alias that points to a from clause.
528 * @param string $join The table name to join.
529 * @param string $alias The alias of the join table.
530 * @param string $condition The condition for the join.
532 * @return QueryBuilder This QueryBuilder instance.
534 public function innerJoin(string $fromAlias, string $join, string $alias, string $condition = null): QueryBuilder
536 $this->concreteQueryBuilder
->innerJoin(
537 $this->quoteIdentifier($fromAlias),
538 $this->quoteIdentifier($join),
539 $this->quoteIdentifier($alias),
547 * Creates and adds a left join to the query.
549 * @param string $fromAlias The alias that points to a from clause.
550 * @param string $join The table name to join.
551 * @param string $alias The alias of the join table.
552 * @param string $condition The condition for the join.
554 * @return QueryBuilder This QueryBuilder instance.
556 public function leftJoin(string $fromAlias, string $join, string $alias, string $condition = null): QueryBuilder
558 $this->concreteQueryBuilder
->leftJoin(
559 $this->quoteIdentifier($fromAlias),
560 $this->quoteIdentifier($join),
561 $this->quoteIdentifier($alias),
569 * Creates and adds a right join to the query.
571 * @param string $fromAlias The alias that points to a from clause.
572 * @param string $join The table name to join.
573 * @param string $alias The alias of the join table.
574 * @param string $condition The condition for the join.
576 * @return QueryBuilder This QueryBuilder instance.
578 public function rightJoin(string $fromAlias, string $join, string $alias, string $condition = null): QueryBuilder
580 $this->concreteQueryBuilder
->rightJoin(
581 $this->quoteIdentifier($fromAlias),
582 $this->quoteIdentifier($join),
583 $this->quoteIdentifier($alias),
591 * Sets a new value for a column in a bulk update query.
593 * @param string $key The column to set.
594 * @param string $value The value, expression, placeholder, etc.
595 * @param bool $createNamedParameter Automatically create a named parameter for the value
597 * @return QueryBuilder This QueryBuilder instance.
599 public function set(string $key, $value, bool $createNamedParameter = true): QueryBuilder
601 $this->concreteQueryBuilder
->set(
602 $this->quoteIdentifier($key),
603 $createNamedParameter ?
$this->createNamedParameter($value) : $value
610 * Specifies one or more restrictions to the query result.
611 * Replaces any previously specified restrictions, if any.
613 * @param mixed,... $predicates
614 * @return QueryBuilder This QueryBuilder instance.
616 public function where(...$predicates): QueryBuilder
618 $this->concreteQueryBuilder
->where(...$predicates);
624 * Adds one or more restrictions to the query results, forming a logical
625 * conjunction with any previously specified restrictions.
627 * @param mixed,... $where The query restrictions.
629 * @return QueryBuilder This QueryBuilder instance.
633 public function andWhere(...$where): QueryBuilder
635 $this->concreteQueryBuilder
->andWhere(...$where);
641 * Adds one or more restrictions to the query results, forming a logical
642 * disjunction with any previously specified restrictions.
644 * @param mixed,... $where The WHERE statement.
646 * @return QueryBuilder This QueryBuilder instance.
650 public function orWhere(...$where): QueryBuilder
652 $this->concreteQueryBuilder
->orWhere(...$where);
658 * Specifies a grouping over the results of the query.
659 * Replaces any previously specified groupings, if any.
661 * @param mixed,... $groupBy The grouping expression.
663 * @return QueryBuilder This QueryBuilder instance.
665 public function groupBy(...$groupBy): QueryBuilder
667 $this->concreteQueryBuilder
->groupBy(...$this->quoteIdentifiers($groupBy));
673 * Adds a grouping expression to the query.
675 * @param mixed,... $groupBy The grouping expression.
677 * @return QueryBuilder This QueryBuilder instance.
679 public function addGroupBy(...$groupBy): QueryBuilder
681 $this->concreteQueryBuilder
->addGroupBy(...$this->quoteIdentifiers($groupBy));
687 * Sets a value for a column in an insert query.
689 * @param string $column The column into which the value should be inserted.
690 * @param string $value The value that should be inserted into the column.
691 * @param bool $createNamedParameter Automatically create a named parameter for the value
693 * @return QueryBuilder This QueryBuilder instance.
695 public function setValue(string $column, $value, bool $createNamedParameter = true): QueryBuilder
697 $this->concreteQueryBuilder
->setValue(
698 $this->quoteIdentifier($column),
699 $createNamedParameter ?
$this->createNamedParameter($value) : $value
706 * Specifies values for an insert query indexed by column names.
707 * Replaces any previous values, if any.
709 * @param array $values The values to specify for the insert query indexed by column names.
710 * @param bool $createNamedParameters Automatically create named parameters for all values
712 * @return QueryBuilder This QueryBuilder instance.
714 public function values(array $values, bool $createNamedParameters = true): QueryBuilder
716 if ($createNamedParameters === true) {
717 foreach ($values as &$value) {
718 $value = $this->createNamedParameter($value);
722 $this->concreteQueryBuilder
->values($this->quoteColumnValuePairs($values));
728 * Specifies a restriction over the groups of the query.
729 * Replaces any previous having restrictions, if any.
731 * @param mixed,... $having The restriction over the groups.
733 * @return QueryBuilder This QueryBuilder instance.
735 public function having(...$having): QueryBuilder
737 $this->concreteQueryBuilder
->having(...$having);
742 * Adds a restriction over the groups of the query, forming a logical
743 * conjunction with any existing having restrictions.
745 * @param mixed,... $having The restriction to append.
747 * @return QueryBuilder This QueryBuilder instance.
749 public function andHaving(...$having): QueryBuilder
751 $this->concreteQueryBuilder
->andHaving(...$having);
757 * Adds a restriction over the groups of the query, forming a logical
758 * disjunction with any existing having restrictions.
760 * @param mixed,... $having The restriction to add.
762 * @return QueryBuilder This QueryBuilder instance.
764 public function orHaving(...$having): QueryBuilder
766 $this->concreteQueryBuilder
->orHaving(...$having);
772 * Specifies an ordering for the query results.
773 * Replaces any previously specified orderings, if any.
775 * @param string $fieldName The fieldName to order by. Will be quoted according to database platform automatically.
776 * @param string $order The ordering direction. No automatic quoting/escaping.
778 * @return QueryBuilder This QueryBuilder instance.
780 public function orderBy(string $fieldName, string $order = null): QueryBuilder
782 $this->concreteQueryBuilder
->orderBy($this->connection
->quoteIdentifier($fieldName), $order);
788 * Adds an ordering to the query results.
790 * @param string $fieldName The fieldName to order by. Will be quoted according to database platform automatically.
791 * @param string $order The ordering direction.
793 * @return QueryBuilder This QueryBuilder instance.
795 public function addOrderBy(string $fieldName, string $order = null): QueryBuilder
797 $this->concreteQueryBuilder
->addOrderBy($this->connection
->quoteIdentifier($fieldName), $order);
803 * Gets a query part by its name.
805 * @param string $queryPartName
809 public function getQueryPart(string $queryPartName)
811 return $this->concreteQueryBuilder
->getQueryPart($queryPartName);
815 * Gets all query parts.
819 public function getQueryParts(): array
821 return $this->concreteQueryBuilder
->getQueryParts();
827 * @param array|null $queryPartNames
829 * @return QueryBuilder This QueryBuilder instance.
831 public function resetQueryParts(array $queryPartNames = null): QueryBuilder
833 $this->concreteQueryBuilder
->resetQueryParts($queryPartNames);
839 * Resets a single SQL part.
841 * @param string $queryPartName
843 * @return QueryBuilder This QueryBuilder instance.
845 public function resetQueryPart($queryPartName): QueryBuilder
847 $this->concreteQueryBuilder
->resetQueryPart($queryPartName);
853 * Gets a string representation of this QueryBuilder which corresponds to
854 * the final SQL query being constructed.
856 * @return string The string representation of this QueryBuilder.
858 public function __toString(): string
860 return $this->getSQL();
864 * Creates a new named parameter and bind the value $value to it.
866 * This method provides a shortcut for PDOStatement::bindValue
867 * when using prepared statements.
869 * The parameter $value specifies the value that you want to bind. If
870 * $placeholder is not provided bindValue() will automatically create a
871 * placeholder for you. An automatic placeholder will be of the name
872 * ':dcValue1', ':dcValue2' etc.
874 * @param mixed $value
876 * @param string $placeHolder The name to bind with. The string must start with a colon ':'.
878 * @return string the placeholder name used.
880 public function createNamedParameter($value, int $type = \PDO
::PARAM_STR
, string $placeHolder = null): string
882 return $this->concreteQueryBuilder
->createNamedParameter($value, $type, $placeHolder);
886 * Creates a new positional parameter and bind the given value to it.
888 * Attention: If you are using positional parameters with the query builder you have
889 * to be very careful to bind all parameters in the order they appear in the SQL
890 * statement , otherwise they get bound in the wrong order which can lead to serious
893 * @param mixed $value
898 public function createPositionalParameter($value, int $type = \PDO
::PARAM_STR
): string
900 return $this->concreteQueryBuilder
->createPositionalParameter($value, $type);
904 * Quotes like wildcards for given string value.
906 * @param string $value The value to be quoted.
908 * @return string The quoted value.
910 public function escapeLikeWildcards(string $value): string
912 return addcslashes($value, '_%');
916 * Quotes a given input parameter.
918 * @param mixed $input The parameter to be quoted.
919 * @param int|null $type The type of the parameter.
921 * @return mixed Often string, but also int or float or similar depending on $input and platform
923 public function quote($input, int $type = null)
925 return $this->getConnection()->quote($input, $type);
929 * Quotes a string so it can be safely used as a table or column name, even if
930 * it is a reserved name.
932 * Delimiting style depends on the underlying database platform that is being used.
934 * @param string $identifier The name to be quoted.
936 * @return string The quoted name.
938 public function quoteIdentifier(string $identifier): string
940 return $this->getConnection()->quoteIdentifier($identifier);
944 * Quotes an array of column names so it can be safely used, even if the name is a reserved name.
946 * Delimiting style depends on the underlying database platform that is being used.
948 * @param array $input
952 public function quoteIdentifiers(array $input): array
954 return $this->getConnection()->quoteIdentifiers($input);
958 * Quotes an array of column names so it can be safely used, even if the name is a reserved name.
959 * Takes into account the special case of the * placeholder that can only be used in SELECT type
962 * Delimiting style depends on the underlying database platform that is being used.
964 * @param array $input
967 * @throws \InvalidArgumentException
969 public function quoteIdentifiersForSelect(array $input): array
971 foreach ($input as &$select) {
972 list($fieldName, $alias, $suffix) = GeneralUtility
::trimExplode(' AS ', str_ireplace(' as ', ' AS ', $select), 3);
973 if (!empty($suffix)) {
974 throw new \
InvalidArgumentException(
975 'QueryBuilder::quoteIdentifiersForSelect() could not parse the input "' . $input . '"',
980 // The SQL * operator must not be quoted. As it can only occur either by itself
981 // or preceded by a tablename (tablename.*) check if the last character of a select
982 // expression is the * and quote only prepended table name. In all other cases the
983 // full expression is being quoted.
984 if (substr($fieldName, -2) === '.*') {
985 $select = $this->quoteIdentifier(substr($fieldName, 0, -2)) . '.*';
986 } elseif ($fieldName !== '*') {
987 $select = $this->quoteIdentifier($fieldName);
990 // Quote the alias for the current fieldName, if given
991 if (!empty($alias)) {
992 $select .= ' AS ' . $this->quoteIdentifier($alias);
999 * Quotes an associative array of column-value so the column names can be safely used, even
1000 * if the name is a reserved name.
1002 * Delimiting style depends on the underlying database platform that is being used.
1004 * @param array $input
1008 public function quoteColumnValuePairs(array $input): array
1010 return $this->getConnection()->quoteColumnValuePairs($input);
1014 * Unquote a single identifier (no dot expansion). Used to unquote the table names
1015 * from the expressionBuilder so that the table can be found in the TCA definition.
1017 * @param string $identifier The identifier / table name
1018 * @return string The unquoted table name / identifier
1020 protected function unquoteSingleIdentifier(string $identifier): string
1022 $identifier = trim($identifier);
1023 $platform = $this->getConnection()->getDatabasePlatform();
1024 if ($platform instanceof SQLServerPlatform
) {
1025 // mssql quotes identifiers with [ and ], not a single character
1026 $identifier = ltrim($identifier, '[');
1027 $identifier = rtrim($identifier, ']');
1029 $quoteChar = $platform->getIdentifierQuoteCharacter();
1030 $identifier = trim($identifier, $quoteChar);
1031 $identifier = str_replace($quoteChar . $quoteChar, $quoteChar, $identifier);
1037 * Return all tables/aliases used in FROM or JOIN query parts from the query builder.
1039 * The table names are automatically unquoted. This is a helper for to build the list
1040 * of queried tables for the AbstractRestrictionContainer.
1044 protected function getQueriedTables(): array
1046 $queriedTables = [];
1048 // Loop through all FROM tables
1049 foreach ($this->getQueryPart('from') as $from) {
1050 $tableName = $this->unquoteSingleIdentifier($from['table']);
1051 $tableAlias = isset($from['alias']) ?
$this->unquoteSingleIdentifier($from['alias']) : $tableName;
1052 $queriedTables[$tableAlias] = $tableName;
1055 // Loop through all JOIN tables
1056 foreach ($this->getQueryPart('join') as $fromTable => $joins) {
1057 foreach ($joins as $join) {
1058 $tableName = $this->unquoteSingleIdentifier($join['joinTable']);
1059 $tableAlias = isset($join['joinAlias']) ?
$this->unquoteSingleIdentifier($join['joinAlias']) : $tableName;
1060 $queriedTables[$tableAlias] = $tableName;
1064 return $queriedTables;
1068 * Add the additional query conditions returned by the QueryRestrictionBuilder
1069 * to the current query and return the original set of conditions so that they
1070 * can be restored after the query has been built/executed.
1072 * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|mixed
1074 protected function addAdditionalWhereConditions()
1076 $originalWhereConditions = $this->concreteQueryBuilder
->getQueryPart('where');
1077 $expression = $this->restrictionContainer
->buildExpression($this->getQueriedTables(), $this->expr());
1078 // This check would be obsolete, as the composite expression would not add empty expressions anyway
1079 // But we keep it here to only clone the previous state, in case we really will change it.
1080 // Once we remove this state preserving functionality, we can remove the count check here
1081 // and just add the expression to the query builder.
1082 if ($expression->count() > 0) {
1083 if ($originalWhereConditions instanceof CompositeExpression
) {
1084 // Save the original query conditions so we can restore
1085 // them after the query has been built.
1086 $originalWhereConditions = clone $originalWhereConditions;
1088 $this->concreteQueryBuilder
->andWhere($expression);
1091 // @todo add hook to be able to add additional restrictions
1093 return $originalWhereConditions;
1097 * Deep clone of the QueryBuilder
1098 * @see \Doctrine\DBAL\Query\QueryBuilder::__clone()
1100 public function __clone()
1102 $this->concreteQueryBuilder
= clone $this->concreteQueryBuilder
;
1103 $this->restrictionContainer
= clone $this->restrictionContainer
;