[CLEANUP] The correct case must be used for standard PHP types in phpdoc
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Generic / Query.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Persistence\Generic;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18 use TYPO3\CMS\Extbase\Persistence\QueryInterface;
19
20 /**
21 * The Query class used to run queries against the database
22 *
23 * @api
24 */
25 class Query implements QueryInterface
26 {
27 /**
28 * An inner join.
29 */
30 const JCR_JOIN_TYPE_INNER = '{http://www.jcp.org/jcr/1.0}joinTypeInner';
31
32 /**
33 * A left-outer join.
34 */
35 const JCR_JOIN_TYPE_LEFT_OUTER = '{http://www.jcp.org/jcr/1.0}joinTypeLeftOuter';
36
37 /**
38 * A right-outer join.
39 */
40 const JCR_JOIN_TYPE_RIGHT_OUTER = '{http://www.jcp.org/jcr/1.0}joinTypeRightOuter';
41
42 /**
43 * Charset of strings in QOM
44 */
45 const CHARSET = 'utf-8';
46
47 /**
48 * @var string
49 */
50 protected $type;
51
52 /**
53 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
54 */
55 protected $objectManager;
56
57 /**
58 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper
59 */
60 protected $dataMapper;
61
62 /**
63 * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
64
65 */
66 protected $persistenceManager;
67
68 /**
69 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory
70 */
71 protected $qomFactory;
72
73 /**
74 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface
75 */
76 protected $source;
77
78 /**
79 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface
80 */
81 protected $constraint;
82
83 /**
84 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Statement
85 */
86 protected $statement;
87
88 /**
89 * @var int
90 */
91 protected $orderings = [];
92
93 /**
94 * @var int
95 */
96 protected $limit;
97
98 /**
99 * @var int
100 */
101 protected $offset;
102
103 /**
104 * The query settings.
105 *
106 * @var QuerySettingsInterface
107 */
108 protected $querySettings;
109
110 /**
111 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
112 */
113 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
114 {
115 $this->objectManager = $objectManager;
116 }
117
118 /**
119 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper
120 */
121 public function injectDataMapper(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper)
122 {
123 $this->dataMapper = $dataMapper;
124 }
125
126 /**
127 * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
128 */
129 public function injectPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager)
130 {
131 $this->persistenceManager = $persistenceManager;
132 }
133
134 /**
135 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory $qomFactory
136 */
137 public function injectQomFactory(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory $qomFactory)
138 {
139 $this->qomFactory = $qomFactory;
140 }
141
142 /**
143 * Constructs a query object working on the given class name
144 *
145 * @param string $type
146 */
147 public function __construct($type)
148 {
149 $this->type = $type;
150 }
151
152 /**
153 * Sets the Query Settings. These Query settings must match the settings expected by
154 * the specific Storage Backend.
155 *
156 * @param QuerySettingsInterface $querySettings The Query Settings
157 * @api This method is not part of TYPO3.Flow API
158 */
159 public function setQuerySettings(QuerySettingsInterface $querySettings)
160 {
161 $this->querySettings = $querySettings;
162 }
163
164 /**
165 * Returns the Query Settings.
166 *
167 * @throws Exception
168 * @return QuerySettingsInterface $querySettings The Query Settings
169 * @api This method is not part of TYPO3.Flow API
170 */
171 public function getQuerySettings()
172 {
173 if (!$this->querySettings instanceof QuerySettingsInterface) {
174 throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception('Tried to get the query settings without seting them before.', 1248689115);
175 }
176 return $this->querySettings;
177 }
178
179 /**
180 * Returns the type this query cares for.
181 *
182 * @return string
183 * @api
184 */
185 public function getType()
186 {
187 return $this->type;
188 }
189
190 /**
191 * Sets the source to fetch the result from
192 *
193 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source
194 */
195 public function setSource(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source)
196 {
197 $this->source = $source;
198 }
199
200 /**
201 * Returns the selectorn name or an empty string, if the source is not a selector
202 * @todo This has to be checked at another place
203 *
204 * @return string The selector name
205 */
206 protected function getSelectorName()
207 {
208 $source = $this->getSource();
209 if ($source instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SelectorInterface) {
210 return $source->getSelectorName();
211 }
212 return '';
213 }
214
215 /**
216 * Gets the node-tuple source for this query.
217 *
218 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface the node-tuple source; non-null
219 */
220 public function getSource()
221 {
222 if ($this->source === null) {
223 $this->source = $this->qomFactory->selector($this->getType(), $this->dataMapper->convertClassNameToTableName($this->getType()));
224 }
225 return $this->source;
226 }
227
228 /**
229 * Executes the query against the database and returns the result
230 *
231 * @param bool $returnRawQueryResult avoids the object mapping by the persistence
232 * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array The query result object or an array if $returnRawQueryResult is TRUE
233 * @api
234 */
235 public function execute($returnRawQueryResult = false)
236 {
237 if ($returnRawQueryResult) {
238 return $this->persistenceManager->getObjectDataByQuery($this);
239 }
240 return $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\QueryResultInterface::class, $this);
241 }
242
243 /**
244 * Sets the property names to order the result by. Expected like this:
245 * array(
246 * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
247 * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
248 * )
249 * where 'foo' and 'bar' are property names.
250 *
251 * @param array $orderings The property names to order by
252 * @return QueryInterface
253 * @api
254 */
255 public function setOrderings(array $orderings)
256 {
257 $this->orderings = $orderings;
258 return $this;
259 }
260
261 /**
262 * Returns the property names to order the result by. Like this:
263 * array(
264 * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
265 * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
266 * )
267 *
268 * @return array
269 * @api
270 */
271 public function getOrderings()
272 {
273 return $this->orderings;
274 }
275
276 /**
277 * Sets the maximum size of the result set to limit. Returns $this to allow
278 * for chaining (fluid interface)
279 *
280 * @param int $limit
281 * @throws \InvalidArgumentException
282 * @return QueryInterface
283 * @api
284 */
285 public function setLimit($limit)
286 {
287 if (!is_int($limit) || $limit < 1) {
288 throw new \InvalidArgumentException('The limit must be an integer >= 1', 1245071870);
289 }
290 $this->limit = $limit;
291 return $this;
292 }
293
294 /**
295 * Resets a previously set maximum size of the result set. Returns $this to allow
296 * for chaining (fluid interface)
297 *
298 * @return QueryInterface
299 * @api
300 */
301 public function unsetLimit()
302 {
303 unset($this->limit);
304 return $this;
305 }
306
307 /**
308 * Returns the maximum size of the result set to limit.
309 *
310 * @return int
311 * @api
312 */
313 public function getLimit()
314 {
315 return $this->limit;
316 }
317
318 /**
319 * Sets the start offset of the result set to offset. Returns $this to
320 * allow for chaining (fluid interface)
321 *
322 * @param int $offset
323 * @throws \InvalidArgumentException
324 * @return QueryInterface
325 * @api
326 */
327 public function setOffset($offset)
328 {
329 if (!is_int($offset) || $offset < 0) {
330 throw new \InvalidArgumentException('The offset must be a positive integer', 1245071872);
331 }
332 $this->offset = $offset;
333 return $this;
334 }
335
336 /**
337 * Returns the start offset of the result set.
338 *
339 * @return int
340 * @api
341 */
342 public function getOffset()
343 {
344 return $this->offset;
345 }
346
347 /**
348 * The constraint used to limit the result set. Returns $this to allow
349 * for chaining (fluid interface)
350 *
351 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint
352 * @return QueryInterface
353 * @api
354 */
355 public function matching($constraint)
356 {
357 $this->constraint = $constraint;
358 return $this;
359 }
360
361 /**
362 * Sets the statement of this query. If you use this, you will lose the abstraction from a concrete storage
363 * backend (database).
364 *
365 * @param string|\TYPO3\CMS\Core\Database\PreparedStatement|\TYPO3\CMS\Core\Database\Query\QueryBuilder|\Doctrine\DBAL\Statement $statement The statement
366 * @param array $parameters An array of parameters. These will be bound to placeholders '?' in the $statement.
367 * @return QueryInterface
368 */
369 public function statement($statement, array $parameters = [])
370 {
371 $this->statement = $this->qomFactory->statement($statement, $parameters);
372 return $this;
373 }
374
375 /**
376 * Returns the statement of this query.
377 *
378 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Statement
379 */
380 public function getStatement()
381 {
382 return $this->statement;
383 }
384
385 /**
386 * Gets the constraint for this query.
387 *
388 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface|null the constraint, or null if none
389 * @api
390 */
391 public function getConstraint()
392 {
393 return $this->constraint;
394 }
395
396 /**
397 * Performs a logical conjunction of the given constraints. The method takes one or more constraints and concatenates them with a boolean AND.
398 * It also accepts a single array of constraints to be concatenated.
399 *
400 * @param mixed $constraint1 The first of multiple constraints or an array of constraints.
401 * @throws Exception\InvalidNumberOfConstraintsException
402 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\AndInterface
403 * @api
404 */
405 public function logicalAnd($constraint1)
406 {
407 if (is_array($constraint1)) {
408 $resultingConstraint = array_shift($constraint1);
409 $constraints = $constraint1;
410 } else {
411 $constraints = func_get_args();
412 $resultingConstraint = array_shift($constraints);
413 }
414 if ($resultingConstraint === null) {
415 throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidNumberOfConstraintsException('There must be at least one constraint or a non-empty array of constraints given.', 1268056288);
416 }
417 foreach ($constraints as $constraint) {
418 $resultingConstraint = $this->qomFactory->_and($resultingConstraint, $constraint);
419 }
420 return $resultingConstraint;
421 }
422
423 /**
424 * Performs a logical disjunction of the two given constraints
425 *
426 * @param mixed $constraint1 The first of multiple constraints or an array of constraints.
427 * @throws Exception\InvalidNumberOfConstraintsException
428 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\OrInterface
429 * @api
430 */
431 public function logicalOr($constraint1)
432 {
433 if (is_array($constraint1)) {
434 $resultingConstraint = array_shift($constraint1);
435 $constraints = $constraint1;
436 } else {
437 $constraints = func_get_args();
438 $resultingConstraint = array_shift($constraints);
439 }
440 if ($resultingConstraint === null) {
441 throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidNumberOfConstraintsException('There must be at least one constraint or a non-empty array of constraints given.', 1268056289);
442 }
443 foreach ($constraints as $constraint) {
444 $resultingConstraint = $this->qomFactory->_or($resultingConstraint, $constraint);
445 }
446 return $resultingConstraint;
447 }
448
449 /**
450 * Performs a logical negation of the given constraint
451 *
452 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint Constraint to negate
453 * @throws \RuntimeException
454 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\NotInterface
455 * @api
456 */
457 public function logicalNot(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint)
458 {
459 return $this->qomFactory->not($constraint);
460 }
461
462 /**
463 * Returns an equals criterion used for matching objects against a query
464 *
465 * @param string $propertyName The name of the property to compare against
466 * @param mixed $operand The value to compare with
467 * @param bool $caseSensitive Whether the equality test should be done case-sensitive
468 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
469 * @api
470 */
471 public function equals($propertyName, $operand, $caseSensitive = true)
472 {
473 if (is_object($operand) || $caseSensitive) {
474 $comparison = $this->qomFactory->comparison(
475 $this->qomFactory->propertyValue($propertyName, $this->getSelectorName()),
476 QueryInterface::OPERATOR_EQUAL_TO,
477 $operand
478 );
479 } else {
480 $comparison = $this->qomFactory->comparison(
481 $this->qomFactory->lowerCase($this->qomFactory->propertyValue($propertyName, $this->getSelectorName())),
482 QueryInterface::OPERATOR_EQUAL_TO,
483 mb_strtolower($operand, \TYPO3\CMS\Extbase\Persistence\Generic\Query::CHARSET)
484 );
485 }
486 return $comparison;
487 }
488
489 /**
490 * Returns a like criterion used for matching objects against a query
491 *
492 * @param string $propertyName The name of the property to compare against
493 * @param mixed $operand The value to compare with
494 * @param bool $caseSensitive, deprecated since TYPO3 v8, will be removed in TYPO3 v9
495 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
496 * @api
497 */
498 public function like($propertyName, $operand, $caseSensitive = null)
499 {
500 if ($caseSensitive !== null) {
501 GeneralUtility::deprecationLog(
502 'The parameter $caseSensitive for the Extbase like criterion has been deprecated.' .
503 'A case sensitive comparison cannot be reliably done as it is dependent on Database ' .
504 'Server settings. For MySQL switch the field to a case sensitive collation to achieve ' .
505 'the desired result.'
506 );
507 }
508
509 return $this->qomFactory->comparison(
510 $this->qomFactory->propertyValue($propertyName, $this->getSelectorName()),
511 QueryInterface::OPERATOR_LIKE,
512 $operand
513 );
514 }
515
516 /**
517 * Returns a "contains" criterion used for matching objects against a query.
518 * It matches if the multivalued property contains the given operand.
519 *
520 * @param string $propertyName The name of the (multivalued) property to compare against
521 * @param mixed $operand The value to compare with
522 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
523 * @api
524 */
525 public function contains($propertyName, $operand)
526 {
527 return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_CONTAINS, $operand);
528 }
529
530 /**
531 * Returns an "in" criterion used for matching objects against a query. It
532 * matches if the property's value is contained in the multivalued operand.
533 *
534 * @param string $propertyName The name of the property to compare against
535 * @param mixed $operand The value to compare with, multivalued
536 * @throws Exception\UnexpectedTypeException
537 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
538 * @api
539 */
540 public function in($propertyName, $operand)
541 {
542 if (!\TYPO3\CMS\Extbase\Utility\TypeHandlingUtility::isValidTypeForMultiValueComparison($operand)) {
543 throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException('The "in" operator must be given a multivalued operand (array, ArrayAccess, Traversable).', 1264678095);
544 }
545 return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_IN, $operand);
546 }
547
548 /**
549 * Returns a less than criterion used for matching objects against a query
550 *
551 * @param string $propertyName The name of the property to compare against
552 * @param mixed $operand The value to compare with
553 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
554 * @api
555 */
556 public function lessThan($propertyName, $operand)
557 {
558 return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LESS_THAN, $operand);
559 }
560
561 /**
562 * Returns a less or equal than criterion used for matching objects against a query
563 *
564 * @param string $propertyName The name of the property to compare against
565 * @param mixed $operand The value to compare with
566 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
567 * @api
568 */
569 public function lessThanOrEqual($propertyName, $operand)
570 {
571 return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO, $operand);
572 }
573
574 /**
575 * Returns a greater than criterion used for matching objects against a query
576 *
577 * @param string $propertyName The name of the property to compare against
578 * @param mixed $operand The value to compare with
579 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
580 * @api
581 */
582 public function greaterThan($propertyName, $operand)
583 {
584 return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_GREATER_THAN, $operand);
585 }
586
587 /**
588 * Returns a greater than or equal criterion used for matching objects against a query
589 *
590 * @param string $propertyName The name of the property to compare against
591 * @param mixed $operand The value to compare with
592 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface
593 * @api
594 */
595 public function greaterThanOrEqual($propertyName, $operand)
596 {
597 return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO, $operand);
598 }
599
600 /**
601 * Returns a greater than or equal criterion used for matching objects against a query
602 *
603 * @param string $propertyName The name of the property to compare against
604 * @param mixed $operandLower The value of the lower boundary to compare against
605 * @param mixed $operandUpper The value of the upper boundary to compare against
606 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\AndInterface
607 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidNumberOfConstraintsException
608 * @api
609 */
610 public function between($propertyName, $operandLower, $operandUpper)
611 {
612 return $this->logicalAnd(
613 $this->greaterThanOrEqual($propertyName, $operandLower),
614 $this->lessThanOrEqual($propertyName, $operandUpper)
615 );
616 }
617
618 /**
619 */
620 public function __wakeup()
621 {
622 $this->objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
623 $this->persistenceManager = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface::class);
624 $this->dataMapper = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper::class);
625 $this->qomFactory = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory::class);
626 }
627
628 /**
629 * @return array
630 */
631 public function __sleep()
632 {
633 return ['type', 'source', 'constraint', 'statement', 'orderings', 'limit', 'offset', 'querySettings'];
634 }
635
636 /**
637 * Returns the query result count.
638 *
639 * @return int The query result count
640 * @api
641 */
642 public function count()
643 {
644 return $this->execute()->count();
645 }
646
647 /**
648 * Returns an "isEmpty" criterion used for matching objects against a query.
649 * It matches if the multivalued property contains no values or is NULL.
650 *
651 * @param string $propertyName The name of the multivalued property to compare against
652 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException
653 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException if used on a single-valued property
654 * @return bool
655 * @api
656 */
657 public function isEmpty($propertyName)
658 {
659 throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException(__METHOD__, 1476122265);
660 }
661 }