[BUGFIX] Fix several typos in php comments
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Database / Query / QueryBuilder.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Core\Database\Query;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
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.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
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;
25
26 /**
27 * Object oriented approach to building SQL queries.
28 *
29 * It's a facade to the Doctrine DBAL QueryBuilder that implements PHP7 type hinting and automatic
30 * quoting of table and column names.
31 *
32 * <code>
33 * $query->select('aField', 'anotherField')
34 * ->from('aTable')
35 * ->where($query->expr()->eq('aField', 1))
36 * ->andWhere($query->expr()->gte('anotherField',10'))
37 * ->execute()
38 * </code>
39 *
40 * Additional functionality included is support for COUNT() and TRUNCATE() statements.
41 */
42 class QueryBuilder
43 {
44 /**
45 * The DBAL Connection.
46 *
47 * @var Connection
48 */
49 protected $connection;
50
51 /**
52 * @var \Doctrine\DBAL\Query\QueryBuilder
53 */
54 protected $concreteQueryBuilder;
55
56 /**
57 * @var QueryRestrictionContainerInterface
58 */
59 protected $restrictionContainer;
60
61 /**
62 * @var array
63 */
64 protected $additionalRestrictions;
65
66 /**
67 * Initializes a new QueryBuilder.
68 *
69 * @param Connection $connection The DBAL Connection.
70 * @param QueryRestrictionContainerInterface $restrictionContainer
71 * @param \Doctrine\DBAL\Query\QueryBuilder $concreteQueryBuilder
72 * @param array $additionalRestrictions
73 */
74 public function __construct(
75 Connection $connection,
76 QueryRestrictionContainerInterface $restrictionContainer = null,
77 \Doctrine\DBAL\Query\QueryBuilder $concreteQueryBuilder = null,
78 array $additionalRestrictions = null
79 ) {
80 $this->connection = $connection;
81 $this->additionalRestrictions = $additionalRestrictions ?: $GLOBALS['TYPO3_CONF_VARS']['DB']['additionalQueryRestrictions'] ?? [];
82 $this->setRestrictions($restrictionContainer ?: GeneralUtility::makeInstance(DefaultRestrictionContainer::class));
83 $this->concreteQueryBuilder = $concreteQueryBuilder ?: GeneralUtility::makeInstance(\Doctrine\DBAL\Query\QueryBuilder::class, $connection);
84 }
85
86 /**
87 * @return QueryRestrictionContainerInterface
88 */
89 public function getRestrictions()
90 {
91 return $this->restrictionContainer;
92 }
93
94 /**
95 * @param QueryRestrictionContainerInterface $restrictionContainer
96 */
97 public function setRestrictions(QueryRestrictionContainerInterface $restrictionContainer)
98 {
99 foreach ($this->additionalRestrictions as $restrictionClass => $options) {
100 if (empty($options['disabled'])) {
101 $restriction = GeneralUtility::makeInstance($restrictionClass);
102 $restrictionContainer->add($restriction);
103 }
104 }
105 $this->restrictionContainer = $restrictionContainer;
106 }
107
108 /**
109 * Re-apply default restrictions
110 */
111 public function resetRestrictions()
112 {
113 $this->setRestrictions(GeneralUtility::makeInstance(DefaultRestrictionContainer::class));
114 }
115
116 /**
117 * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
118 * This producer method is intended for convenient inline usage. Example:
119 *
120 * For more complex expression construction, consider storing the expression
121 * builder object in a local variable.
122 *
123 * @return ExpressionBuilder
124 */
125 public function expr(): ExpressionBuilder
126 {
127 return $this->connection->getExpressionBuilder();
128 }
129
130 /**
131 * Gets the type of the currently built query.
132 *
133 * @return int
134 * @internal
135 */
136 public function getType(): int
137 {
138 return $this->concreteQueryBuilder->getType();
139 }
140
141 /**
142 * Gets the associated DBAL Connection for this query builder.
143 *
144 * @return Connection
145 */
146 public function getConnection(): Connection
147 {
148 return $this->connection;
149 }
150
151 /**
152 * Gets the state of this query builder instance.
153 *
154 * @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
155 * @internal
156 */
157 public function getState(): int
158 {
159 return $this->concreteQueryBuilder->getState();
160 }
161
162 /**
163 * Gets the concrete implementation of the query builder
164 *
165 * @return \Doctrine\DBAL\Query\QueryBuilder
166 * @internal
167 */
168 public function getConcreteQueryBuilder(): \Doctrine\DBAL\Query\QueryBuilder
169 {
170 return $this->concreteQueryBuilder;
171 }
172
173 /**
174 * Executes this query using the bound parameters and their types.
175 *
176 * @return \Doctrine\DBAL\Driver\Statement|int
177 */
178 public function execute()
179 {
180 if ($this->getType() !== \Doctrine\DBAL\Query\QueryBuilder::SELECT) {
181 return $this->concreteQueryBuilder->execute();
182 }
183
184 // Set additional query restrictions
185 $originalWhereConditions = $this->addAdditionalWhereConditions();
186
187 $result = $this->concreteQueryBuilder->execute();
188
189 // Restore the original query conditions in case the user keeps
190 // on modifying the state.
191 $this->concreteQueryBuilder->add('where', $originalWhereConditions, false);
192
193 return $result;
194 }
195
196 /**
197 * Gets the complete SQL string formed by the current specifications of this QueryBuilder.
198 *
199 * If the statement is a SELECT TYPE query restrictions based on TCA settings will
200 * automatically be applied based on the current QuerySettings.
201 *
202 * @return string The SQL query string.
203 */
204 public function getSQL(): string
205 {
206 if ($this->getType() !== \Doctrine\DBAL\Query\QueryBuilder::SELECT) {
207 return $this->concreteQueryBuilder->getSQL();
208 }
209
210 // Set additional query restrictions
211 $originalWhereConditions = $this->addAdditionalWhereConditions();
212
213 $sql = $this->concreteQueryBuilder->getSQL();
214
215 // Restore the original query conditions in case the user keeps
216 // on modifying the state.
217 $this->concreteQueryBuilder->add('where', $originalWhereConditions, false);
218
219 return $sql;
220 }
221
222 /**
223 * Sets a query parameter for the query being constructed.
224 *
225 * @param string|int $key The parameter position or name.
226 * @param mixed $value The parameter value.
227 * @param int|null $type One of the Connection::PARAM_* constants.
228 *
229 * @return QueryBuilder This QueryBuilder instance.
230 */
231 public function setParameter($key, $value, int $type = null): QueryBuilder
232 {
233 $this->concreteQueryBuilder->setParameter($key, $value, $type);
234
235 return $this;
236 }
237
238 /**
239 * Sets a collection of query parameters for the query being constructed.
240 *
241 * @param array $params The query parameters to set.
242 * @param array $types The query parameters types to set.
243 *
244 * @return QueryBuilder This QueryBuilder instance.
245 */
246 public function setParameters(array $params, array $types = []): QueryBuilder
247 {
248 $this->concreteQueryBuilder->setParameters($params, $types);
249
250 return $this;
251 }
252
253 /**
254 * Gets all defined query parameters for the query being constructed indexed by parameter index or name.
255 *
256 * @return array The currently defined query parameters indexed by parameter index or name.
257 */
258 public function getParameters(): array
259 {
260 return $this->concreteQueryBuilder->getParameters();
261 }
262
263 /**
264 * Gets a (previously set) query parameter of the query being constructed.
265 *
266 * @param string|int $key The key (index or name) of the bound parameter.
267 *
268 * @return mixed The value of the bound parameter.
269 */
270 public function getParameter($key)
271 {
272 return $this->concreteQueryBuilder->getParameter($key);
273 }
274
275 /**
276 * Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
277 *
278 * @return array The currently defined query parameter types indexed by parameter index or name.
279 */
280 public function getParameterTypes(): array
281 {
282 return $this->concreteQueryBuilder->getParameterTypes();
283 }
284
285 /**
286 * Gets a (previously set) query parameter type of the query being constructed.
287 *
288 * @param string|int $key The key (index or name) of the bound parameter type.
289 *
290 * @return mixed The value of the bound parameter type.
291 */
292 public function getParameterType($key)
293 {
294 return $this->concreteQueryBuilder->getParameterType($key);
295 }
296
297 /**
298 * Sets the position of the first result to retrieve (the "offset").
299 *
300 * @param int $firstResult The first result to return.
301 *
302 * @return QueryBuilder This QueryBuilder instance.
303 */
304 public function setFirstResult(int $firstResult): QueryBuilder
305 {
306 $this->concreteQueryBuilder->setFirstResult($firstResult);
307
308 return $this;
309 }
310
311 /**
312 * Gets the position of the first result the query object was set to retrieve (the "offset").
313 * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
314 *
315 * @return int The position of the first result.
316 */
317 public function getFirstResult(): int
318 {
319 return (int)$this->concreteQueryBuilder->getFirstResult();
320 }
321
322 /**
323 * Sets the maximum number of results to retrieve (the "limit").
324 *
325 * @param int $maxResults The maximum number of results to retrieve.
326 *
327 * @return QueryBuilder This QueryBuilder instance.
328 */
329 public function setMaxResults(int $maxResults): QueryBuilder
330 {
331 $this->concreteQueryBuilder->setMaxResults($maxResults);
332
333 return $this;
334 }
335
336 /**
337 * Gets the maximum number of results the query object was set to retrieve (the "limit").
338 * Returns 0 if setMaxResults was not applied to this query builder.
339 *
340 * @return int The maximum number of results.
341 */
342 public function getMaxResults(): int
343 {
344 return (int)$this->concreteQueryBuilder->getMaxResults();
345 }
346
347 /**
348 * Either appends to or replaces a single, generic query part.
349 *
350 * The available parts are: 'select', 'from', 'set', 'where',
351 * 'groupBy', 'having' and 'orderBy'.
352 *
353 * @param string $sqlPartName
354 * @param string|array $sqlPart
355 * @param bool $append
356 *
357 * @return QueryBuilder This QueryBuilder instance.
358 */
359 public function add(string $sqlPartName, $sqlPart, bool $append = false): QueryBuilder
360 {
361 $this->concreteQueryBuilder->add($sqlPartName, $sqlPart, $append);
362
363 return $this;
364 }
365
366 /**
367 * Specifies the item that is to be counted in the query result.
368 * Replaces any previously specified selections, if any.
369 *
370 * @param string $item Will be quoted according to database platform automatically.
371 * @return QueryBuilder This QueryBuilder instance.
372 */
373 public function count(string $item): QueryBuilder
374 {
375 $countExpr = $this->getConnection()->getDatabasePlatform()->getCountExpression(
376 $item === '*' ? $item : $this->quoteIdentifier($item)
377 );
378 $this->concreteQueryBuilder->select($countExpr);
379
380 return $this;
381 }
382
383 /**
384 * Specifies items that are to be returned in the query result.
385 * Replaces any previously specified selections, if any.
386 *
387 * @param string[] $selects
388 * @return QueryBuilder This QueryBuilder instance.
389 */
390 public function select(string ...$selects): QueryBuilder
391 {
392 $this->concreteQueryBuilder->select(...$this->quoteIdentifiersForSelect($selects));
393
394 return $this;
395 }
396
397 /**
398 * Adds an item that is to be returned in the query result.
399 *
400 * @param string[] $selects The selection expression.
401 *
402 * @return QueryBuilder This QueryBuilder instance.
403 */
404 public function addSelect(string ...$selects): QueryBuilder
405 {
406 $this->concreteQueryBuilder->addSelect(...$this->quoteIdentifiersForSelect($selects));
407
408 return $this;
409 }
410
411 /**
412 * Specifies items that are to be returned in the query result.
413 * Replaces any previously specified selections, if any.
414 * This should only be used for literal SQL expressions as no
415 * quoting/escaping of any kind will be performed on the items.
416 *
417 * @param string[] $selects Literal SQL expressions to be selected. Warning: No quoting will be done!
418 * @return QueryBuilder This QueryBuilder instance.
419 */
420 public function selectLiteral(string ...$selects): QueryBuilder
421 {
422 $this->concreteQueryBuilder->select(...$selects);
423
424 return $this;
425 }
426
427 /**
428 * Adds an item that is to be returned in the query result. This should
429 * only be used for literal SQL expressions as no quoting/escaping of
430 * any kind will be performed on the items.
431 *
432 * @param string[] $selects Literal SQL expressions to be selected.
433 * @return QueryBuilder This QueryBuilder instance.
434 */
435 public function addSelectLiteral(string ...$selects): QueryBuilder
436 {
437 $this->concreteQueryBuilder->addSelect(...$selects);
438
439 return $this;
440 }
441
442 /**
443 * Turns the query being built into a bulk delete query that ranges over
444 * a certain table.
445 *
446 * @param string $delete The table whose rows are subject to the deletion.
447 * Will be quoted according to database platform automatically.
448 * @param string $alias The table alias used in the constructed query.
449 * Will be quoted according to database platform automatically.
450 *
451 * @return QueryBuilder This QueryBuilder instance.
452 */
453 public function delete(string $delete, string $alias = null): QueryBuilder
454 {
455 $this->concreteQueryBuilder->delete(
456 $this->quoteIdentifier($delete),
457 empty($alias) ? $alias : $this->quoteIdentifier($alias)
458 );
459
460 return $this;
461 }
462
463 /**
464 * Turns the query being built into a bulk update query that ranges over
465 * a certain table
466 *
467 * @param string $update The table whose rows are subject to the update.
468 * @param string $alias The table alias used in the constructed query.
469 *
470 * @return QueryBuilder This QueryBuilder instance.
471 */
472 public function update(string $update, string $alias = null): QueryBuilder
473 {
474 $this->concreteQueryBuilder->update(
475 $this->quoteIdentifier($update),
476 empty($alias) ? $alias : $this->quoteIdentifier($alias)
477 );
478
479 return $this;
480 }
481
482 /**
483 * Turns the query being built into an insert query that inserts into
484 * a certain table
485 *
486 * @param string $insert The table into which the rows should be inserted.
487 *
488 * @return QueryBuilder This QueryBuilder instance.
489 */
490 public function insert(string $insert): QueryBuilder
491 {
492 $this->concreteQueryBuilder->insert($this->quoteIdentifier($insert));
493
494 return $this;
495 }
496
497 /**
498 * Creates and adds a query root corresponding to the table identified by the
499 * given alias, forming a cartesian product with any existing query roots.
500 *
501 * @param string $from The table. Will be quoted according to database platform automatically.
502 * @param string $alias The alias of the table. Will be quoted according to database platform automatically.
503 *
504 * @return QueryBuilder This QueryBuilder instance.
505 */
506 public function from(string $from, string $alias = null): QueryBuilder
507 {
508 $this->concreteQueryBuilder->from(
509 $this->quoteIdentifier($from),
510 empty($alias) ? $alias : $this->quoteIdentifier($alias)
511 );
512
513 return $this;
514 }
515
516 /**
517 * Creates and adds a join to the query.
518 *
519 * @param string $fromAlias The alias that points to a from clause.
520 * @param string $join The table name to join.
521 * @param string $alias The alias of the join table.
522 * @param string $condition The condition for the join.
523 *
524 * @return QueryBuilder This QueryBuilder instance.
525 */
526 public function join(string $fromAlias, string $join, string $alias, string $condition = null): QueryBuilder
527 {
528 $this->concreteQueryBuilder->innerJoin(
529 $this->quoteIdentifier($fromAlias),
530 $this->quoteIdentifier($join),
531 $this->quoteIdentifier($alias),
532 $condition
533 );
534
535 return $this;
536 }
537
538 /**
539 * Creates and adds a join to the query.
540 *
541 * @param string $fromAlias The alias that points to a from clause.
542 * @param string $join The table name to join.
543 * @param string $alias The alias of the join table.
544 * @param string $condition The condition for the join.
545 *
546 * @return QueryBuilder This QueryBuilder instance.
547 */
548 public function innerJoin(string $fromAlias, string $join, string $alias, string $condition = null): QueryBuilder
549 {
550 $this->concreteQueryBuilder->innerJoin(
551 $this->quoteIdentifier($fromAlias),
552 $this->quoteIdentifier($join),
553 $this->quoteIdentifier($alias),
554 $condition
555 );
556
557 return $this;
558 }
559
560 /**
561 * Creates and adds a left join to the query.
562 *
563 * @param string $fromAlias The alias that points to a from clause.
564 * @param string $join The table name to join.
565 * @param string $alias The alias of the join table.
566 * @param string $condition The condition for the join.
567 *
568 * @return QueryBuilder This QueryBuilder instance.
569 */
570 public function leftJoin(string $fromAlias, string $join, string $alias, string $condition = null): QueryBuilder
571 {
572 $this->concreteQueryBuilder->leftJoin(
573 $this->quoteIdentifier($fromAlias),
574 $this->quoteIdentifier($join),
575 $this->quoteIdentifier($alias),
576 $condition
577 );
578
579 return $this;
580 }
581
582 /**
583 * Creates and adds a right join to the query.
584 *
585 * @param string $fromAlias The alias that points to a from clause.
586 * @param string $join The table name to join.
587 * @param string $alias The alias of the join table.
588 * @param string $condition The condition for the join.
589 *
590 * @return QueryBuilder This QueryBuilder instance.
591 */
592 public function rightJoin(string $fromAlias, string $join, string $alias, string $condition = null): QueryBuilder
593 {
594 $this->concreteQueryBuilder->rightJoin(
595 $this->quoteIdentifier($fromAlias),
596 $this->quoteIdentifier($join),
597 $this->quoteIdentifier($alias),
598 $condition
599 );
600
601 return $this;
602 }
603
604 /**
605 * Sets a new value for a column in a bulk update query.
606 *
607 * @param string $key The column to set.
608 * @param string $value The value, expression, placeholder, etc.
609 * @param bool $createNamedParameter Automatically create a named parameter for the value
610 * @param int $type
611 *
612 * @return QueryBuilder This QueryBuilder instance.
613 */
614 public function set(string $key, $value, bool $createNamedParameter = true, int $type = \PDO::PARAM_STR): QueryBuilder
615 {
616 $this->concreteQueryBuilder->set(
617 $this->quoteIdentifier($key),
618 $createNamedParameter ? $this->createNamedParameter($value, $type) : $value
619 );
620
621 return $this;
622 }
623
624 /**
625 * Specifies one or more restrictions to the query result.
626 * Replaces any previously specified restrictions, if any.
627 *
628 * @param mixed $predicates
629 * @return QueryBuilder This QueryBuilder instance.
630 */
631 public function where(...$predicates): QueryBuilder
632 {
633 $this->concreteQueryBuilder->where(...$predicates);
634
635 return $this;
636 }
637
638 /**
639 * Adds one or more restrictions to the query results, forming a logical
640 * conjunction with any previously specified restrictions.
641 *
642 * @param mixed $where The query restrictions.
643 *
644 * @return QueryBuilder This QueryBuilder instance.
645 *
646 * @see where()
647 */
648 public function andWhere(...$where): QueryBuilder
649 {
650 $this->concreteQueryBuilder->andWhere(...$where);
651
652 return $this;
653 }
654
655 /**
656 * Adds one or more restrictions to the query results, forming a logical
657 * disjunction with any previously specified restrictions.
658 *
659 * @param mixed $where The WHERE statement.
660 *
661 * @return QueryBuilder This QueryBuilder instance.
662 *
663 * @see where()
664 */
665 public function orWhere(...$where): QueryBuilder
666 {
667 $this->concreteQueryBuilder->orWhere(...$where);
668
669 return $this;
670 }
671
672 /**
673 * Specifies a grouping over the results of the query.
674 * Replaces any previously specified groupings, if any.
675 *
676 * @param mixed $groupBy The grouping expression.
677 *
678 * @return QueryBuilder This QueryBuilder instance.
679 */
680 public function groupBy(...$groupBy): QueryBuilder
681 {
682 $this->concreteQueryBuilder->groupBy(...$this->quoteIdentifiers($groupBy));
683
684 return $this;
685 }
686
687 /**
688 * Adds a grouping expression to the query.
689 *
690 * @param mixed $groupBy The grouping expression.
691 *
692 * @return QueryBuilder This QueryBuilder instance.
693 */
694 public function addGroupBy(...$groupBy): QueryBuilder
695 {
696 $this->concreteQueryBuilder->addGroupBy(...$this->quoteIdentifiers($groupBy));
697
698 return $this;
699 }
700
701 /**
702 * Sets a value for a column in an insert query.
703 *
704 * @param string $column The column into which the value should be inserted.
705 * @param string $value The value that should be inserted into the column.
706 * @param bool $createNamedParameter Automatically create a named parameter for the value
707 *
708 * @return QueryBuilder This QueryBuilder instance.
709 */
710 public function setValue(string $column, $value, bool $createNamedParameter = true): QueryBuilder
711 {
712 $this->concreteQueryBuilder->setValue(
713 $this->quoteIdentifier($column),
714 $createNamedParameter ? $this->createNamedParameter($value) : $value
715 );
716
717 return $this;
718 }
719
720 /**
721 * Specifies values for an insert query indexed by column names.
722 * Replaces any previous values, if any.
723 *
724 * @param array $values The values to specify for the insert query indexed by column names.
725 * @param bool $createNamedParameters Automatically create named parameters for all values
726 *
727 * @return QueryBuilder This QueryBuilder instance.
728 */
729 public function values(array $values, bool $createNamedParameters = true): QueryBuilder
730 {
731 if ($createNamedParameters === true) {
732 foreach ($values as &$value) {
733 $value = $this->createNamedParameter($value);
734 }
735 }
736
737 $this->concreteQueryBuilder->values($this->quoteColumnValuePairs($values));
738
739 return $this;
740 }
741
742 /**
743 * Specifies a restriction over the groups of the query.
744 * Replaces any previous having restrictions, if any.
745 *
746 * @param mixed $having The restriction over the groups.
747 *
748 * @return QueryBuilder This QueryBuilder instance.
749 */
750 public function having(...$having): QueryBuilder
751 {
752 $this->concreteQueryBuilder->having(...$having);
753 return $this;
754 }
755
756 /**
757 * Adds a restriction over the groups of the query, forming a logical
758 * conjunction with any existing having restrictions.
759 *
760 * @param mixed $having The restriction to append.
761 *
762 * @return QueryBuilder This QueryBuilder instance.
763 */
764 public function andHaving(...$having): QueryBuilder
765 {
766 $this->concreteQueryBuilder->andHaving(...$having);
767
768 return $this;
769 }
770
771 /**
772 * Adds a restriction over the groups of the query, forming a logical
773 * disjunction with any existing having restrictions.
774 *
775 * @param mixed $having The restriction to add.
776 *
777 * @return QueryBuilder This QueryBuilder instance.
778 */
779 public function orHaving(...$having): QueryBuilder
780 {
781 $this->concreteQueryBuilder->orHaving(...$having);
782
783 return $this;
784 }
785
786 /**
787 * Specifies an ordering for the query results.
788 * Replaces any previously specified orderings, if any.
789 *
790 * @param string $fieldName The fieldName to order by. Will be quoted according to database platform automatically.
791 * @param string $order The ordering direction. No automatic quoting/escaping.
792 *
793 * @return QueryBuilder This QueryBuilder instance.
794 */
795 public function orderBy(string $fieldName, string $order = null): QueryBuilder
796 {
797 $this->concreteQueryBuilder->orderBy($this->connection->quoteIdentifier($fieldName), $order);
798
799 return $this;
800 }
801
802 /**
803 * Adds an ordering to the query results.
804 *
805 * @param string $fieldName The fieldName to order by. Will be quoted according to database platform automatically.
806 * @param string $order The ordering direction.
807 *
808 * @return QueryBuilder This QueryBuilder instance.
809 */
810 public function addOrderBy(string $fieldName, string $order = null): QueryBuilder
811 {
812 $this->concreteQueryBuilder->addOrderBy($this->connection->quoteIdentifier($fieldName), $order);
813
814 return $this;
815 }
816
817 /**
818 * Gets a query part by its name.
819 *
820 * @param string $queryPartName
821 *
822 * @return mixed
823 */
824 public function getQueryPart(string $queryPartName)
825 {
826 return $this->concreteQueryBuilder->getQueryPart($queryPartName);
827 }
828
829 /**
830 * Gets all query parts.
831 *
832 * @return array
833 */
834 public function getQueryParts(): array
835 {
836 return $this->concreteQueryBuilder->getQueryParts();
837 }
838
839 /**
840 * Resets SQL parts.
841 *
842 * @param array|null $queryPartNames
843 *
844 * @return QueryBuilder This QueryBuilder instance.
845 */
846 public function resetQueryParts(array $queryPartNames = null): QueryBuilder
847 {
848 $this->concreteQueryBuilder->resetQueryParts($queryPartNames);
849
850 return $this;
851 }
852
853 /**
854 * Resets a single SQL part.
855 *
856 * @param string $queryPartName
857 *
858 * @return QueryBuilder This QueryBuilder instance.
859 */
860 public function resetQueryPart($queryPartName): QueryBuilder
861 {
862 $this->concreteQueryBuilder->resetQueryPart($queryPartName);
863
864 return $this;
865 }
866
867 /**
868 * Gets a string representation of this QueryBuilder which corresponds to
869 * the final SQL query being constructed.
870 *
871 * @return string The string representation of this QueryBuilder.
872 */
873 public function __toString(): string
874 {
875 return $this->getSQL();
876 }
877
878 /**
879 * Creates a new named parameter and bind the value $value to it.
880 *
881 * This method provides a shortcut for PDOStatement::bindValue
882 * when using prepared statements.
883 *
884 * The parameter $value specifies the value that you want to bind. If
885 * $placeholder is not provided bindValue() will automatically create a
886 * placeholder for you. An automatic placeholder will be of the name
887 * ':dcValue1', ':dcValue2' etc.
888 *
889 * @param mixed $value
890 * @param int $type
891 * @param string $placeHolder The name to bind with. The string must start with a colon ':'.
892 *
893 * @return string the placeholder name used.
894 */
895 public function createNamedParameter($value, int $type = \PDO::PARAM_STR, string $placeHolder = null): string
896 {
897 return $this->concreteQueryBuilder->createNamedParameter($value, $type, $placeHolder);
898 }
899
900 /**
901 * Creates a new positional parameter and bind the given value to it.
902 *
903 * Attention: If you are using positional parameters with the query builder you have
904 * to be very careful to bind all parameters in the order they appear in the SQL
905 * statement , otherwise they get bound in the wrong order which can lead to serious
906 * bugs in your code.
907 *
908 * @param mixed $value
909 * @param int $type
910 *
911 * @return string
912 */
913 public function createPositionalParameter($value, int $type = \PDO::PARAM_STR): string
914 {
915 return $this->concreteQueryBuilder->createPositionalParameter($value, $type);
916 }
917
918 /**
919 * Quotes like wildcards for given string value.
920 *
921 * @param string $value The value to be quoted.
922 *
923 * @return string The quoted value.
924 */
925 public function escapeLikeWildcards(string $value): string
926 {
927 return addcslashes($value, '_%');
928 }
929
930 /**
931 * Quotes a given input parameter.
932 *
933 * @param mixed $input The parameter to be quoted.
934 * @param int|null $type The type of the parameter.
935 *
936 * @return mixed Often string, but also int or float or similar depending on $input and platform
937 */
938 public function quote($input, int $type = null)
939 {
940 return $this->getConnection()->quote($input, $type);
941 }
942
943 /**
944 * Quotes a string so it can be safely used as a table or column name, even if
945 * it is a reserved name.
946 *
947 * Delimiting style depends on the underlying database platform that is being used.
948 *
949 * @param string $identifier The name to be quoted.
950 *
951 * @return string The quoted name.
952 */
953 public function quoteIdentifier(string $identifier): string
954 {
955 return $this->getConnection()->quoteIdentifier($identifier);
956 }
957
958 /**
959 * Quotes an array of column names so it can be safely used, even if the name is a reserved name.
960 *
961 * Delimiting style depends on the underlying database platform that is being used.
962 *
963 * @param array $input
964 *
965 * @return array
966 */
967 public function quoteIdentifiers(array $input): array
968 {
969 return $this->getConnection()->quoteIdentifiers($input);
970 }
971
972 /**
973 * Quotes an array of column names so it can be safely used, even if the name is a reserved name.
974 * Takes into account the special case of the * placeholder that can only be used in SELECT type
975 * statements.
976 *
977 * Delimiting style depends on the underlying database platform that is being used.
978 *
979 * @param array $input
980 *
981 * @return array
982 * @throws \InvalidArgumentException
983 */
984 public function quoteIdentifiersForSelect(array $input): array
985 {
986 foreach ($input as &$select) {
987 list($fieldName, $alias, $suffix) = array_pad(
988 GeneralUtility::trimExplode(
989 ' AS ',
990 str_ireplace(' as ', ' AS ', $select),
991 true,
992 3
993 ),
994 3,
995 null
996 );
997 if (!empty($suffix)) {
998 throw new \InvalidArgumentException(
999 'QueryBuilder::quoteIdentifiersForSelect() could not parse the select ' . $select . '.',
1000 1461170686
1001 );
1002 }
1003
1004 // The SQL * operator must not be quoted. As it can only occur either by itself
1005 // or preceded by a tablename (tablename.*) check if the last character of a select
1006 // expression is the * and quote only prepended table name. In all other cases the
1007 // full expression is being quoted.
1008 if (substr($fieldName, -2) === '.*') {
1009 $select = $this->quoteIdentifier(substr($fieldName, 0, -2)) . '.*';
1010 } elseif ($fieldName !== '*') {
1011 $select = $this->quoteIdentifier($fieldName);
1012 }
1013
1014 // Quote the alias for the current fieldName, if given
1015 if (!empty($alias)) {
1016 $select .= ' AS ' . $this->quoteIdentifier($alias);
1017 }
1018 }
1019 return $input;
1020 }
1021
1022 /**
1023 * Quotes an associative array of column-value so the column names can be safely used, even
1024 * if the name is a reserved name.
1025 *
1026 * Delimiting style depends on the underlying database platform that is being used.
1027 *
1028 * @param array $input
1029 *
1030 * @return array
1031 */
1032 public function quoteColumnValuePairs(array $input): array
1033 {
1034 return $this->getConnection()->quoteColumnValuePairs($input);
1035 }
1036
1037 /**
1038 * Unquote a single identifier (no dot expansion). Used to unquote the table names
1039 * from the expressionBuilder so that the table can be found in the TCA definition.
1040 *
1041 * @param string $identifier The identifier / table name
1042 * @return string The unquoted table name / identifier
1043 */
1044 protected function unquoteSingleIdentifier(string $identifier): string
1045 {
1046 $identifier = trim($identifier);
1047 $platform = $this->getConnection()->getDatabasePlatform();
1048 if ($platform instanceof SQLServerPlatform) {
1049 // mssql quotes identifiers with [ and ], not a single character
1050 $identifier = ltrim($identifier, '[');
1051 $identifier = rtrim($identifier, ']');
1052 } else {
1053 $quoteChar = $platform->getIdentifierQuoteCharacter();
1054 $identifier = trim($identifier, $quoteChar);
1055 $identifier = str_replace($quoteChar . $quoteChar, $quoteChar, $identifier);
1056 }
1057 return $identifier;
1058 }
1059
1060 /**
1061 * Return all tables/aliases used in FROM or JOIN query parts from the query builder.
1062 *
1063 * The table names are automatically unquoted. This is a helper for to build the list
1064 * of queried tables for the AbstractRestrictionContainer.
1065 *
1066 * @return string[]
1067 */
1068 protected function getQueriedTables(): array
1069 {
1070 $queriedTables = [];
1071
1072 // Loop through all FROM tables
1073 foreach ($this->getQueryPart('from') as $from) {
1074 $tableName = $this->unquoteSingleIdentifier($from['table']);
1075 $tableAlias = isset($from['alias']) ? $this->unquoteSingleIdentifier($from['alias']) : $tableName;
1076 $queriedTables[$tableAlias] = $tableName;
1077 }
1078
1079 // Loop through all JOIN tables
1080 foreach ($this->getQueryPart('join') as $fromTable => $joins) {
1081 foreach ($joins as $join) {
1082 $tableName = $this->unquoteSingleIdentifier($join['joinTable']);
1083 $tableAlias = isset($join['joinAlias']) ? $this->unquoteSingleIdentifier($join['joinAlias']) : $tableName;
1084 $queriedTables[$tableAlias] = $tableName;
1085 }
1086 }
1087
1088 return $queriedTables;
1089 }
1090
1091 /**
1092 * Add the additional query conditions returned by the QueryRestrictionBuilder
1093 * to the current query and return the original set of conditions so that they
1094 * can be restored after the query has been built/executed.
1095 *
1096 * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|mixed
1097 */
1098 protected function addAdditionalWhereConditions()
1099 {
1100 $originalWhereConditions = $this->concreteQueryBuilder->getQueryPart('where');
1101 $expression = $this->restrictionContainer->buildExpression($this->getQueriedTables(), $this->expr());
1102 // This check would be obsolete, as the composite expression would not add empty expressions anyway
1103 // But we keep it here to only clone the previous state, in case we really will change it.
1104 // Once we remove this state preserving functionality, we can remove the count check here
1105 // and just add the expression to the query builder.
1106 if ($expression->count() > 0) {
1107 if ($originalWhereConditions instanceof CompositeExpression) {
1108 // Save the original query conditions so we can restore
1109 // them after the query has been built.
1110 $originalWhereConditions = clone $originalWhereConditions;
1111 }
1112 $this->concreteQueryBuilder->andWhere($expression);
1113 }
1114
1115 return $originalWhereConditions;
1116 }
1117
1118 /**
1119 * Deep clone of the QueryBuilder
1120 * @see \Doctrine\DBAL\Query\QueryBuilder::__clone()
1121 */
1122 public function __clone()
1123 {
1124 $this->concreteQueryBuilder = clone $this->concreteQueryBuilder;
1125 $this->restrictionContainer = clone $this->restrictionContainer;
1126 }
1127 }