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