fea26177e29ab5f7e3427b9764f772d07d77c9cd
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Query.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
6 * All rights reserved
7 *
8 * This class is a backport of the corresponding class of FLOW3.
9 * All credits go to the v5 team.
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 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * The Query class used to run queries against the database
30 *
31 * @package Extbase
32 * @subpackage Persistence
33 * @version $Id$
34 * @scope prototype
35 * @api
36 */
37 class Tx_Extbase_Persistence_Query implements Tx_Extbase_Persistence_QueryInterface, Tx_Extbase_Persistence_QuerySettingsInterface {
38
39 /**
40 * @var string
41 */
42 protected $className;
43
44 /**
45 * @var Tx_Extbase_Persistence_DataMapper
46 */
47 protected $dataMapper;
48
49 /**
50 * @var Tx_Extbase_Persistence_Manager
51 */
52 protected $persistenceManager;
53
54 /**
55 * @var Tx_Extbase_Persistence_QOM_QueryObjectModelFactoryInterface
56 */
57 protected $qomFactory;
58
59 /**
60 * @var Tx_Extbase_Persistence_ValueFactoryInterface
61 */
62 protected $valueFactory;
63
64 /**
65 * @var Tx_Extbase_Persistence_QOM_SourceInterface
66 */
67 protected $source;
68
69 /**
70 * @var Tx_Extbase_Persistence_QOM_ConstraintInterface
71 */
72 protected $constraint;
73
74 /**
75 * An array of named variables and their values from the operators
76 * @var array
77 */
78 protected $operands = array();
79
80 /**
81 * @var int
82 */
83 protected $orderings = array();
84
85 /**
86 * @var int
87 */
88 protected $columns = array();
89
90 /**
91 * @var int
92 */
93 protected $limit;
94
95 /**
96 * @var int
97 */
98 protected $offset;
99
100 /**
101 * The query settings.
102 *
103 * @var Tx_Extbase_Persistence_QuerySettingsInterface
104 */
105 protected $querySettings;
106
107 /**
108 * Constructs a query object working on the given class name
109 *
110 * @param string $className
111 */
112 public function __construct($className) {
113 $this->className = $className;
114 }
115
116 /**
117 * Injects the persistence manager, used to fetch the CR session
118 *
119 * @param Tx_Extbase_Persistence_ManagerInterface $persistenceManager
120 * @return void
121 */
122 public function injectPersistenceManager(Tx_Extbase_Persistence_ManagerInterface $persistenceManager) {
123 $this->persistenceManager = $persistenceManager;
124 $this->qomFactory = $this->persistenceManager->getBackend()->getQomFactory();
125 $this->valueFactory = $this->persistenceManager->getBackend()->getValueFactory();
126 }
127
128 /**
129 * Injects the DataMapper to map nodes to objects
130 *
131 * @param Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper
132 * @return void
133 */
134 public function injectDataMapper(Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper) {
135 $this->dataMapper = $dataMapper;
136 }
137
138 /**
139 * Sets the Query Settings. These Query settings must match the settings expected by
140 * the specific Storage Backend.
141 *
142 * @param Tx_Extbase_Persistence_QuerySettingsInterface $querySettings The Query Settings
143 * @return void
144 */
145 public function setQuerySettings(Tx_Extbase_Persistence_QuerySettingsInterface $querySettings) {
146 $this->querySettings = $querySettings;
147 }
148
149 /**
150 * Returns the Query Settings.
151 *
152 * @return Tx_Extbase_Persistence_QuerySettingsInterface $querySettings The Query Settings
153 */
154 public function getQuerySettings() {
155 if (!($this->querySettings instanceof Tx_Extbase_Persistence_QuerySettingsInterface)) throw new Tx_Extbase_Persistence_Exception('Tried to get the query settings without seting them before.', 1248689115);
156 return $this->querySettings;
157 }
158
159 /**
160 * Returns the class name the query handles
161 *
162 * @return string The class name
163 */
164 public function getClassName() {
165 return $this->className;
166 }
167
168 /**
169 * Sets the source to fetch the result from
170 *
171 * @param Tx_Extbase_Persistence_QOM_SourceInterface $source
172 */
173 public function setSource(Tx_Extbase_Persistence_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 * @return string The selector name
181 */
182 protected function getSelectorName() {
183 if ($this->getSource() instanceof Tx_Extbase_Persistence_QOM_SelectorInterface) {
184 return $this->source->getSelectorName();
185 } else {
186 return '';
187 }
188 }
189
190 /**
191 * Gets the node-tuple source for this query.
192 *
193 * @return Tx_Extbase_Persistence_QOM_SourceInterface the node-tuple source; non-null
194 */
195 public function getSource() {
196 if ($this->source === NULL) {
197 $this->source = $this->qomFactory->selector($this->className, $this->dataMapper->convertClassNameToTableName($this->className));
198 }
199 return $this->source;
200 }
201
202 /**
203 * Executes the query against the database and returns the result
204 *
205 * @return array<object> The query result as an array of objects
206 * @api
207 */
208 public function execute() {
209 $rows = $this->persistenceManager->getObjectDataByQuery($this);
210 if ($this->getQuerySettings()->getReturnRawQueryResult() === TRUE) {
211 return $rows;
212 } else {
213 return $this->dataMapper->map($this->className, $rows);
214 }
215 }
216
217 /**
218 * Executes the number of matching objects for the query
219 *
220 * @return integer The number of matching objects
221 * @api
222 */
223 public function count() {
224 return $this->persistenceManager->getObjectCountByQuery($this);
225 }
226
227 /**
228 * Prepares and returns a Query Object Model
229 *
230 * @return Tx_Extbase_Persistence_QOM_QueryObjectModelInterface The prepared query object
231 */
232 protected function getPreparedQueryObjectModel() {
233 if ($this->source === NULL) {
234 $this->source = $this->qomFactory->selector($this->className, $this->dataMapper->convertClassNameToTableName($this->className));
235 }
236 if ($this->constraint instanceof Tx_Extbase_Persistence_QOM_StatementInterface) {
237 $query = $this->qomFactory->createQuery(
238 $this->source,
239 $this->constraint,
240 array(),
241 array()
242 );
243 } else {
244 $query = $this->qomFactory->createQuery(
245 $this->source,
246 $this->constraint,
247 $this->orderings,
248 $this->columns // TODO implement selection of columns
249 );
250
251 if ($this->limit !== NULL) {
252 $query->setLimit($this->limit);
253 }
254 if ($this->offset !== NULL) {
255 $query->setOffset($this->offset);
256 }
257
258 }
259
260 foreach ($this->operands as $name => $value) {
261 if (is_array($value)) {
262 $newValue = array();
263 foreach ($value as $valueItem) {
264 $newValue[] = $this->valueFactory->createValue($valueItem);
265 }
266 $query->bindValue($name, $this->valueFactory->createValue($newValue));
267 } else {
268 $query->bindValue($name, $this->valueFactory->createValue($value));
269 }
270 }
271 $query->setQuerySettings($this->getQuerySettings());
272 return $query;
273 }
274
275 /**
276 * Sets the property names to order the result by. Expected like this:
277 * array(
278 * 'foo' => Tx_Extbase_Persistence_QueryInterface::ORDER_ASCENDING,
279 * 'bar' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING
280 * )
281 * where 'foo' and 'bar' are property names.
282 *
283 * @param array $orderings The property names to order by
284 * @return Tx_Extbase_Persistence_QueryInterface
285 * @api
286 */
287 public function setOrderings(array $orderings) {
288 $parsedOrderings = array();
289 foreach ($orderings as $propertyName => $order) {
290 if ($order === Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING) {
291 $parsedOrderings[] = $this->qomFactory->descending($this->qomFactory->propertyValue($propertyName));
292 } elseif ($order === Tx_Extbase_Persistence_QueryInterface::ORDER_ASCENDING) {
293 $parsedOrderings[] = $this->qomFactory->ascending($this->qomFactory->propertyValue($propertyName));
294 } else {
295 throw new Tx_Extbase_Persistence_Exception_UnsupportedOrder('The order you specified for your query is not supported.', 1253785630);
296 }
297 }
298 $this->orderings = $parsedOrderings;
299 return $this;
300 }
301
302 /**
303 * Returns the property names to order the result by. Like this:
304 * array(
305 * 'foo' => \F3\FLOW3\Persistence\QueryInterface::ORDER_ASCENDING,
306 * 'bar' => \F3\FLOW3\Persistence\QueryInterface::ORDER_DESCENDING
307 * )
308 *
309 * @return array
310 * @api
311 */
312 public function getOrderings() {
313 return $this->orderings;
314 }
315
316 /**
317 * Sets the maximum size of the result set to limit. Returns $this to allow
318 * for chaining (fluid interface)
319 *
320 * @param integer $limit
321 * @return Tx_Extbase_Persistence_QueryInterface
322 * @api
323 */
324 public function setLimit($limit) {
325 if (!is_int($limit) || $limit < 1) throw new InvalidArgumentException('The limit must be an integer >= 1', 1245071870);
326 $this->limit = $limit;
327 return $this;
328 }
329
330 /**
331 * Returns the maximum size of the result set to limit.
332 *
333 * @param integer
334 * @api
335 */
336 public function getLimit() {
337 return $this->limit;
338 }
339
340 /**
341 * Sets the start offset of the result set to offset. Returns $this to
342 * allow for chaining (fluid interface)
343 *
344 * @param integer $offset
345 * @return Tx_Extbase_Persistence_QueryInterface
346 * @api
347 */
348 public function setOffset($offset) {
349 if (!is_int($offset) || $offset < 0) throw new InvalidArgumentException('The offset must be a positive integer', 1245071872);
350 $this->offset = $offset;
351 return $this;
352 }
353
354 /**
355 * Returns the start offset of the result set.
356 *
357 * @return integer
358 * @api
359 */
360 public function getOffset() {
361 return $this->offset;
362 }
363
364 /**
365 * The constraint used to limit the result set. Returns $this to allow
366 * for chaining (fluid interface)
367 *
368 * @param Tx_Extbase_Persistence_QOM_ConstraintInterface $constraint
369 * @return Tx_Extbase_Persistence_QueryInterface
370 * @api
371 */
372 public function matching($constraint) {
373 $this->constraint = $constraint;
374 return $this;
375 }
376
377 /**
378 * Sets the statement of this query programmatically. If you use this, you will lose the abstraction from a concrete storage
379 * backend (database).
380 *
381 * @param string $statement The statement
382 * @param array $paramerters An array of parameters. These will be bound to placeholders '?' in the $statement.
383 * @param object $language The language of the statement. Must be a supported languanguage defined as Tx_Extbase_Persistence_QOM_QueryObjectModelInterface::JCR_* or Tx_Extbase_Persistence_QOM_QueryObjectModelInterface::TYPO3_* or
384 * @return Tx_Extbase_Persistence_QOM_StatementInterface
385 */
386 public function statement($statement, array $parameters = array(), $language = Tx_Extbase_Persistence_QOM_QueryObjectModelInterface::TYPO3_SQL_MYSQL) {
387 $this->constraint = $this->qomFactory->statement($statement, $parameters, $language);
388 return $this;
389 }
390
391 /**
392 * Gets the constraint for this query.
393 *
394 * @return Tx_Extbase_Persistence_QOM_Constraint the constraint, or null if none
395 * @author Karsten Dambekalns <karsten@typo3.org>
396 * @api
397 */
398 public function getConstraint() {
399 return $this->constraint;
400 }
401
402 /**
403 * Performs a logical conjunction of the two given constraints.
404 *
405 * @param object $constraint1 First constraint
406 * @param object $constraint2 Second constraint
407 * @return Tx_Extbase_Persistence_QOM_AndInterface
408 * @api
409 */
410 public function logicalAnd($constraint1, $constraint2) {
411 return $this->qomFactory->_and(
412 $constraint1,
413 $constraint2
414 );
415 }
416
417 /**
418 * Performs a logical disjunction of the two given constraints
419 *
420 * @param object $constraint1 First constraint
421 * @param object $constraint2 Second constraint
422 * @return Tx_Extbase_Persistence_QOM_OrInterface
423 * @api
424 */
425 public function logicalOr($constraint1, $constraint2) {
426 return $this->qomFactory->_or(
427 $constraint1,
428 $constraint2
429 );
430 }
431
432 /**
433 * Performs a logical negation of the given constraint
434 *
435 * @param object $constraint Constraint to negate
436 * @return Tx_Extbase_Persistence_QOM_NotInterface
437 * @api
438 */
439 public function logicalNot($constraint) {
440 return $this->qomFactory->not($constraint);
441 }
442
443 /**
444 * Matches against the (internal) uid.
445 *
446 * @param int $uid The uid to match against
447 * @return Tx_Extbase_Persistence_QOM_ComparisonInterface
448 * @api
449 */
450 public function withUid($operand) {
451 return $this->qomFactory->comparison(
452 $this->qomFactory->propertyValue('uid', $this->getSelectorName()),
453 Tx_Extbase_Persistence_QueryInterface::OPERATOR_EQUAL_TO,
454 $operand
455 );
456 }
457
458 /**
459 * Returns an equals criterion used for matching objects against a query
460 *
461 * @param string $propertyName The name of the property to compare against
462 * @param mixed $operand The value to compare with
463 * @param boolean $caseSensitive Whether the equality test should be done case-sensitive
464 * @return Tx_Extbase_Persistence_QOM_ComparisonInterface
465 */
466 public function equals($propertyName, $operand, $caseSensitive = TRUE) {
467 if (is_object($operand) || $caseSensitive) {
468 $comparison = $this->qomFactory->comparison(
469 $this->qomFactory->propertyValue($propertyName, $this->getSelectorName()),
470 Tx_Extbase_Persistence_QueryInterface::OPERATOR_EQUAL_TO,
471 $operand
472 );
473 } else {
474 $comparison = $this->qomFactory->comparison(
475 $this->qomFactory->lowerCase(
476 $this->qomFactory->propertyValue($propertyName, $this->getSelectorName())
477 ),
478 Tx_Extbase_Persistence_QueryInterface::OPERATOR_EQUAL_TO,
479 strtolower($operand)
480 );
481 }
482
483 return $comparison;
484 }
485
486 /**
487 * Returns a like criterion used for matching objects against a query
488 *
489 * @param string $propertyName The name of the property to compare against
490 * @param mixed $operand The value to compare with
491 * @return Tx_Extbase_Persistence_QOM_ComparisonInterface
492 */
493 public function like($propertyName, $operand) {
494 return $this->qomFactory->comparison(
495 $this->qomFactory->propertyValue($propertyName, $this->getSelectorName()),
496 Tx_Extbase_Persistence_QueryInterface::OPERATOR_LIKE,
497 $operand
498 );
499 }
500
501 /**
502 * Returns a "contains" criterion used for matching objects against a query.
503 * It matches if the multivalued property contains the given operand.
504 *
505 * @param string $propertyName The name of the (multivalued) property to compare against
506 * @param mixed $operand The value to compare with
507 * @return Tx_Extbase_Persistence_QOM_ComparisonInterface
508 * @api
509 */
510 public function contains($propertyName, $operand){
511 return $this->qomFactory->comparison(
512 $this->qomFactory->propertyValue($propertyName, $this->getSelectorName()),
513 Tx_Extbase_Persistence_QueryInterface::OPERATOR_CONTAINS,
514 $operand
515 );
516 }
517
518 /**
519 * Returns an "in" criterion used for matching objects against a query. It
520 * matches if the property's value is contained in the multivalued operand.
521 *
522 * @param string $propertyName The name of the property to compare against
523 * @param mixed $operand The value to compare with, multivalued
524 * @return Tx_Extbase_Persistence_QOM_ComparisonInterface
525 * @api
526 */
527 public function in($propertyName, $operand) {
528 if (!is_array($operand) && (!$operand instanceof ArrayAccess) && (!$operand instanceof Traversable)) {
529 throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('The "in" operator must be given a mutlivalued operand (array, ArrayAccess, Traversable).', 1264678095);
530 }
531
532 return $this->qomFactory->comparison(
533 $this->qomFactory->propertyValue($propertyName, $this->getSelectorName()),
534 Tx_Extbase_Persistence_QueryInterface::OPERATOR_IN,
535 $operand
536 );
537 }
538
539 /**
540 * Returns a less than criterion used for matching objects against a query
541 *
542 * @param string $propertyName The name of the property to compare against
543 * @param mixed $operand The value to compare with
544 * @return Tx_Extbase_Persistence_QOM_ComparisonInterface
545 */
546 public function lessThan($propertyName, $operand) {
547 return $this->qomFactory->comparison(
548 $this->qomFactory->propertyValue($propertyName, $this->getSelectorName()),
549 Tx_Extbase_Persistence_QueryInterface::OPERATOR_LESS_THAN,
550 $operand
551 );
552 }
553
554 /**
555 * Returns a less or equal than criterion used for matching objects against a query
556 *
557 * @param string $propertyName The name of the property to compare against
558 * @param mixed $operand The value to compare with
559 * @return Tx_Extbase_Persistence_QOM_ComparisonInterface
560 */
561 public function lessThanOrEqual($propertyName, $operand) {
562 return $this->qomFactory->comparison(
563 $this->qomFactory->propertyValue($propertyName, $this->getSelectorName()),
564 Tx_Extbase_Persistence_QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO,
565 $operand
566 );
567 }
568
569 /**
570 * Returns a greater than criterion used for matching objects against a query
571 *
572 * @param string $propertyName The name of the property to compare against
573 * @param mixed $operand The value to compare with
574 * @return Tx_Extbase_Persistence_QOM_ComparisonInterface
575 */
576 public function greaterThan($propertyName, $operand) {
577 return $this->qomFactory->comparison(
578 $this->qomFactory->propertyValue($propertyName, $this->getSelectorName()),
579 Tx_Extbase_Persistence_QueryInterface::OPERATOR_GREATER_THAN,
580 $operand
581 );
582 }
583
584 /**
585 * Returns a greater than or equal criterion used for matching objects against a query
586 *
587 * @param string $propertyName The name of the property to compare against
588 * @param mixed $operand The value to compare with
589 * @return Tx_Extbase_Persistence_QOM_ComparisonInterface
590 */
591 public function greaterThanOrEqual($propertyName, $operand) {
592 return $this->qomFactory->comparison(
593 $this->qomFactory->propertyValue($propertyName, $this->getSelectorName()),
594 Tx_Extbase_Persistence_QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO,
595 $operand
596 );
597 }
598
599 }
600 ?>