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