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