[+BUGFIX] Extbase (Persistence): Check for existing Value Objects respects now delete...
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Storage / Typo3DbBackend.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 * A Storage backend
30 *
31 * @package Extbase
32 * @subpackage Persistence\Storage
33 * @version $Id: $
34 */
35 class Tx_Extbase_Persistence_Storage_Typo3DbBackend implements Tx_Extbase_Persistence_Storage_BackendInterface, t3lib_Singleton {
36
37 const OPERATOR_EQUAL_TO_NULL = 'operatorEqualToNull';
38 const OPERATOR_NOT_EQUAL_TO_NULL = 'operatorNotEqualToNull';
39
40 /**
41 * The TYPO3 database object
42 *
43 * @var t3lib_db
44 */
45 protected $databaseHandle;
46
47 /**
48 * @var Tx_Extbase_Persistence_DataMapper
49 */
50 protected $dataMapper;
51
52 /**
53 * The TYPO3 page select object. Used for language and workspace overlay
54 *
55 * @var t3lib_pageSelect
56 */
57 protected $pageSelectObject;
58
59 /**
60 * Constructs this Storage Backend instance
61 *
62 * @param t3lib_db $databaseHandle The database handle
63 */
64 public function __construct($databaseHandle) {
65 $this->databaseHandle = $databaseHandle;
66 }
67
68 /**
69 * Injects the DataMapper to map nodes to objects
70 *
71 * @param Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper
72 * @return void
73 */
74 public function injectDataMapper(Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper) {
75 $this->dataMapper = $dataMapper;
76 }
77
78 /**
79 * Adds a row to the storage
80 *
81 * @param string $tableName The database table name
82 * @param array $row The row to be inserted
83 * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
84 * @return int The uid of the inserted row
85 */
86 public function addRow($tableName, array $row, $isRelation = FALSE) {
87 $fields = array();
88 $values = array();
89 $parameters = array();
90 unset($row['uid']); // TODO Check if the offset exists
91 foreach ($row as $columnName => $value) {
92 $fields[] = $columnName;
93 $values[] = '?';
94 $parameters[] = $value;
95 }
96
97 $sqlString = 'INSERT INTO ' . $tableName . ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')';
98 $this->replacePlaceholders($sqlString, $parameters);
99 $this->databaseHandle->sql_query($sqlString);
100 $this->checkSqlErrors();
101 $uid = $this->databaseHandle->sql_insert_id();
102 if (!$isRelation) {
103 $this->clearPageCache($tableName, $uid);
104 }
105 return $uid;
106 }
107
108 /**
109 * Updates a row in the storage
110 *
111 * @param string $tableName The database table name
112 * @param array $row The row to be updated
113 * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
114 * @return void
115 */
116 public function updateRow($tableName, array $row, $isRelation = FALSE) {
117 if (!isset($row['uid'])) throw new InvalidArgumentException('The given row must contain a value for "uid".');
118 $uid = (int)$row['uid'];
119 unset($row['uid']);
120 $fields = array();
121 $parameters = array();
122 foreach ($row as $columnName => $value) {
123 $fields[] = $columnName . '=?';
124 $parameters[] = $value;
125 }
126 $parameters[] = $uid;
127
128 $sqlString = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $fields) . ' WHERE uid=?';
129 $this->replacePlaceholders($sqlString, $parameters);
130
131 $returnValue = $this->databaseHandle->sql_query($sqlString);
132 $this->checkSqlErrors();
133 if (!$isRelation) {
134 $this->clearPageCache($tableName, $uid);
135 }
136 return $returnValue;
137 }
138
139 /**
140 * Deletes a row in the storage
141 *
142 * @param string $tableName The database table name
143 * @param array $uid The uid of the row to be deleted
144 * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default
145 * @return void
146 */
147 public function removeRow($tableName, $uid, $isRelation = FALSE) {
148 $sqlString = 'DELETE FROM ' . $tableName . ' WHERE uid=?';
149 $this->replacePlaceholders($sqlString, array((int)$uid));
150 if (!$isRelation) {
151 $this->clearPageCache($tableName, $uid, $isRelation);
152 }
153 $returnValue = $this->databaseHandle->sql_query($sqlString);
154 $this->checkSqlErrors();
155 return $returnValue;
156 }
157
158 /**
159 * Returns an array with tuples matching the query.
160 *
161 * @param Tx_Extbase_Persistence_QOM_QueryObjectModelInterface $query
162 * @return array The matching tuples
163 */
164 public function getRows(Tx_Extbase_Persistence_QOM_QueryObjectModelInterface $query) {
165 $statement = $this->parseQuery($query);
166 $result = $this->databaseHandle->sql_query($statement);
167 $this->checkSqlErrors();
168 if ($result) {
169 $tuples = $this->getRowsFromResult($query->getSource(), $result);
170 }
171
172 return $tuples;
173 }
174
175 /**
176 * Returns an array with tuples matching the query.
177 *
178 * @param Tx_Extbase_Persistence_QOM_QueryObjectModelInterface $query
179 * @return array The matching tuples
180 */
181 public function parseQuery(Tx_Extbase_Persistence_QOM_QueryObjectModelInterface $query) {
182 $statement = '';
183 $parameters = array();
184 $constraint = $query->getConstraint();
185 if($constraint instanceof Tx_Extbase_Persistence_QOM_StatementInterface) {
186 if ($constraint->getLanguage() === Tx_Extbase_Persistence_QOM_QueryObjectModelInterface::TYPO3_SQL_MYSQL) {
187 $statement = $constraint->getStatement();
188 $parameters= $query->getBoundVariableValues();
189 } else {
190 throw new Tx_Extbase_Persistence_Exception('Unsupported query language.', 1248701951);
191 }
192 } else {
193 $sql = array();
194 $sql['tables'] = array();
195 $sql['fields'] = array();
196 $sql['where'] = array();
197 $sql['additionalWhereClause'] = array();
198 $sql['orderings'] = array();
199 $sql['limit'] = array();
200 $tuples = array();
201
202 $source = $query->getSource();
203 $this->parseSource($query, $source, $sql, $parameters);
204
205 $statement = 'SELECT ' . implode(',', $sql['fields']) . ' FROM ' . implode(' ', $sql['tables']);
206
207 $this->parseConstraint($query->getConstraint(), $source, $sql, $parameters, $query->getBoundVariableValues());
208
209 if (!empty($sql['where'])) {
210 $statement .= ' WHERE ' . implode('', $sql['where']);
211 if (!empty($sql['additionalWhereClause'])) {
212 $statement .= ' AND ' . implode(' AND ', $sql['additionalWhereClause']);
213 }
214 } elseif (!empty($sql['additionalWhereClause'])) {
215 $statement .= ' WHERE ' . implode(' AND ', $sql['additionalWhereClause']);
216 }
217
218 $this->parseOrderings($query->getOrderings(), $source, $sql, $parameters, $query->getBoundVariableValues());
219 if (!empty($sql['orderings'])) {
220 $statement .= ' ORDER BY ' . implode(', ', $sql['orderings']);
221 }
222
223 $this->parseLimitAndOffset($query->getLimit(), $query->getOffset(), $sql);
224 if (!empty($sql['limit'])) {
225 $statement .= ' LIMIT ' . $sql['limit'];
226 }
227 }
228
229 $this->replacePlaceholders($statement, $parameters);
230
231 return $statement;
232 }
233
234 /**
235 * Checks if a Value Object equal to the given Object exists in the data base
236 *
237 * @param array $properties The properties of the Value Object
238 * @param Tx_Extbase_Persistence_Mapper_DataMap $dataMap The Data Map
239 * @return array The matching tuples
240 */
241 public function hasValueObject(array $properties, Tx_Extbase_Persistence_Mapper_DataMap $dataMap) {
242 $fields = array();
243 $parameters = array();
244 foreach ($properties as $propertyName => $propertyValue) {
245 if ($dataMap->isPersistableProperty($propertyName) && ($propertyName !== 'uid')) {
246 $fields[] = $dataMap->getColumnMap($propertyName)->getColumnName() . '=?';
247 $parameters[] = $dataMap->convertPropertyValueToFieldValue($propertyValue);
248 }
249 }
250 $fields[] = 'deleted!=1';
251 $fields[] = 'hidden!=1';
252
253 $sqlString = 'SELECT * FROM ' . $dataMap->getTableName() . ' WHERE ' . implode(' AND ', $fields);
254 $this->replacePlaceholders($sqlString, $parameters);
255 $res = $this->databaseHandle->sql_query($sqlString);
256 $this->checkSqlErrors();
257 $row = $this->databaseHandle->sql_fetch_assoc($res);
258 if ($row !== FALSE) {
259 return $row['uid'];
260 } else {
261 return FALSE;
262 }
263 }
264
265 /**
266 * Transforms a Query Source into SQL and parameter arrays
267 *
268 * @param Tx_Extbase_Persistence_QOM_QueryObjectModel $query
269 * @param Tx_Extbase_Persistence_QOM_SourceInterface $source The source
270 * @param array &$sql
271 * @param array &$parameters
272 * @return void
273 */
274 protected function parseSource(Tx_Extbase_Persistence_QOM_QueryObjectModelInterface $query, Tx_Extbase_Persistence_QOM_SourceInterface $source, array &$sql, array &$parameters) {
275 if ($source instanceof Tx_Extbase_Persistence_QOM_SelectorInterface) {
276 $tableName = $source->getSelectorName();
277 $sql['fields'][] = $tableName . '.*';
278 $sql['tables'][] = $tableName;
279 $querySettings = $query->getQuerySettings();
280 if ($querySettings instanceof Tx_Extbase_Persistence_Typo3QuerySettingsInterface) {
281 if ($querySettings->getRespectEnableFields()) {
282 $this->addEnableFieldsStatement($tableName, $sql);
283 }
284 if ($querySettings->getRespectStoragePage()) {
285 $this->addPageIdStatement($tableName, $sql);
286 }
287 }
288 } elseif ($source instanceof Tx_Extbase_Persistence_QOM_JoinInterface) {
289 $this->parseJoin($query, $source, $sql, $parameters);
290 }
291 }
292
293 /**
294 * Transforms a Join into SQL and parameter arrays
295 *
296 * @param Tx_Extbase_Persistence_QOM_QueryObjectModel $query
297 * @param Tx_Extbase_Persistence_QOM_JoinInterface $join
298 * @param array &$sql
299 * @param array &$parameters
300 * @return void
301 */
302 protected function parseJoin(Tx_Extbase_Persistence_QOM_QueryObjectModelInterface $query, Tx_Extbase_Persistence_QOM_JoinInterface $join, array &$sql, array &$parameters) {
303 $leftSource = $join->getLeft();
304 $leftTableName = $leftSource->getSelectorName();
305 $rightSource = $join->getRight();
306 $rightTableName = $rightSource->getSelectorName();
307
308 $sql['fields'][] = $leftTableName . '.*';
309 $sql['fields'][] = $rightTableName . '.*';
310
311 // TODO Implement support for different join types and nested joins
312 $sql['tables'][] = $leftTableName . ' LEFT JOIN ' . $rightTableName;
313
314 $joinCondition = $join->getJoinCondition();
315 // TODO Check the parsing of the join
316 if ($joinCondition instanceof Tx_Extbase_Persistence_QOM_EquiJoinCondition) {
317 $column1Name = $this->dataMapper->convertPropertyNameToColumnName($joinCondition->getProperty1Name(), $leftSource->getNodeTypeName());
318 $column2Name = $this->dataMapper->convertPropertyNameToColumnName($joinCondition->getProperty2Name(), $rightSource->getNodeTypeName());
319 $sql['tables'][] = 'ON ' . $joinCondition->getSelector1Name() . '.' . $column1Name . ' = ' . $joinCondition->getSelector2Name() . '.' . $column2Name;
320 }
321 // TODO Implement childtableWhere
322
323 $querySettings = $query->getQuerySettings();
324 if ($querySettings instanceof Tx_Extbase_Persistence_Typo3QuerySettingsInterface) {
325 if ($querySettings->getRespectEnableFields()) {
326 $this->addEnableFieldsStatement($leftTableName, $sql);
327 $this->addEnableFieldsStatement($rightTableName, $sql);
328 }
329 if ($querySettings->getRespectStoragePage()) {
330 $this->addPageIdStatement($leftTableName, $sql);
331 $this->addPageIdStatement($rightTableName, $sql);
332 }
333 }
334 }
335
336 /**
337 * Transforms a constraint into SQL and parameter arrays
338 *
339 * @param Tx_Extbase_Persistence_QOM_ConstraintInterface $constraint
340 * @param Tx_Extbase_Persistence_QOM_SourceInterface $source The source
341 * @param array &$sql
342 * @param array &$parameters
343 * @param array $boundVariableValues
344 * @return void
345 */
346 protected function parseConstraint(Tx_Extbase_Persistence_QOM_ConstraintInterface $constraint = NULL, Tx_Extbase_Persistence_QOM_SourceInterface $source, array &$sql, array &$parameters, array $boundVariableValues) {
347 if ($constraint instanceof Tx_Extbase_Persistence_QOM_AndInterface) {
348 $sql['where'][] = '(';
349 $this->parseConstraint($constraint->getConstraint1(), $source, $sql, $parameters, $boundVariableValues);
350 $sql['where'][] = ' AND ';
351 $this->parseConstraint($constraint->getConstraint2(), $source, $sql, $parameters, $boundVariableValues);
352 $sql['where'][] = ')';
353 } elseif ($constraint instanceof Tx_Extbase_Persistence_QOM_OrInterface) {
354 $sql['where'][] = '(';
355 $this->parseConstraint($constraint->getConstraint1(), $source, $sql, $parameters, $boundVariableValues);
356 $sql['where'][] = ' OR ';
357 $this->parseConstraint($constraint->getConstraint2(), $source, $sql, $parameters, $boundVariableValues);
358 $sql['where'][] = ')';
359 } elseif ($constraint instanceof Tx_Extbase_Persistence_QOM_NotInterface) {
360 $sql['where'][] = 'NOT (';
361 $this->parseConstraint($constraint->getConstraint(), $source, $sql, $parameters, $boundVariableValues);
362 $sql['where'][] = ')';
363 } elseif ($constraint instanceof Tx_Extbase_Persistence_QOM_ComparisonInterface) {
364 $this->parseComparison($constraint, $source, $sql, $parameters, $boundVariableValues);
365 } elseif ($constraint instanceof Tx_Extbase_Persistence_QOM_RelatedInterface) {
366 $this->parseRelated($constraint, $sql, $parameters, $boundVariableValues);
367 }
368 }
369
370 /**
371 * Parse a Comparison into SQL and parameter arrays.
372 *
373 * @param Tx_Extbase_Persistence_QOM_ComparisonInterface $comparison The comparison to parse
374 * @param Tx_Extbase_Persistence_QOM_SourceInterface $source The source
375 * @param array &$sql SQL query parts to add to
376 * @param array &$parameters Parameters to bind to the SQL
377 * @param array $boundVariableValues The bound variables in the query and their values
378 * @return void
379 */
380 protected function parseComparison(Tx_Extbase_Persistence_QOM_ComparisonInterface $comparison, Tx_Extbase_Persistence_QOM_SourceInterface $source, array &$sql, array &$parameters, array $boundVariableValues) {
381 if (!($comparison->getOperand2() instanceof Tx_Extbase_Persistence_QOM_BindVariableValueInterface)) throw new Tx_Extbase_Persistence_Exception('Type of operand is not supported', 1247581135);
382
383 $value = $boundVariableValues[$comparison->getOperand2()->getBindVariableName()];
384 $operator = $comparison->getOperator();
385 if ($value === NULL) {
386 if ($operator === Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_OPERATOR_EQUAL_TO) {
387 $operator = self::OPERATOR_EQUAL_TO_NULL;
388 } elseif ($operator === Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_OPERATOR_NOT_EQUAL_TO) {
389 $operator = self::OPERATOR_NOT_EQUAL_TO_NULL;
390 } else {
391 // TODO Throw exception
392 }
393 }
394 $parameters[] = $value;
395
396 $this->parseDynamicOperand($comparison->getOperand1(), $operator, $source, $sql, $parameters);
397 }
398
399 /**
400 * Parse a DynamicOperand into SQL and parameter arrays.
401 *
402 * @param Tx_Extbase_Persistence_QOM_DynamicOperandInterface $operand
403 * @param string $operator One of the JCR_OPERATOR_* constants
404 * @param Tx_Extbase_Persistence_QOM_SourceInterface $source The source
405 * @param array &$sql SQL query parts to add to
406 * @param array &$parameters
407 * @param string $valueFunction an aoptional SQL function to apply to the operand value
408 * @return void
409 */
410 protected function parseDynamicOperand(Tx_Extbase_Persistence_QOM_DynamicOperandInterface $operand, $operator, Tx_Extbase_Persistence_QOM_SourceInterface $source, array &$sql, array &$parameters, $valueFunction = NULL) {
411 if ($operand instanceof Tx_Extbase_Persistence_QOM_LowerCaseInterface) {
412 $this->parseDynamicOperand($operand->getOperand(), $operator, $sql, $parameters, 'LOWER');
413 } elseif ($operand instanceof Tx_Extbase_Persistence_QOM_UpperCaseInterface) {
414 $this->parseDynamicOperand($operand->getOperand(), $operator, $sql, $parameters, 'UPPER');
415 } elseif ($operand instanceof Tx_Extbase_Persistence_QOM_PropertyValueInterface) {
416 $tableName = $operand->getSelectorName();
417 // FIXME Discuss the translation from propertyName to columnName
418 if ($source instanceof Tx_Extbase_Persistence_QOM_SelectorInterface) {
419 $className = $source->getNodeTypeName();
420 } else {
421 $className = '';
422 }
423 $columnName = $this->dataMapper->convertPropertyNameToColumnName($operand->getPropertyName(), $className);
424 $operator = $this->resolveOperator($operator);
425
426 if ($valueFunction === NULL) {
427 $constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $operator . ' ?';
428 } else {
429 $constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $operator . ' ?';
430 }
431
432 $sql['where'][] = $constraintSQL;
433 }
434 }
435
436 /**
437 * Returns the SQL operator for the given JCR operator type.
438 *
439 * @param string $operator One of the JCR_OPERATOR_* constants
440 * @return string an SQL operator
441 */
442 protected function resolveOperator($operator) {
443 switch ($operator) {
444 case self::OPERATOR_EQUAL_TO_NULL:
445 $operator = 'IS';
446 break;
447 case self::OPERATOR_NOT_EQUAL_TO_NULL:
448 $operator = 'IS NOT';
449 break;
450 case Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_OPERATOR_EQUAL_TO:
451 $operator = '=';
452 break;
453 case Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_OPERATOR_NOT_EQUAL_TO:
454 $operator = '!=';
455 break;
456 case Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_OPERATOR_LESS_THAN:
457 $operator = '<';
458 break;
459 case Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO:
460 $operator = '<=';
461 break;
462 case Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_OPERATOR_GREATER_THAN:
463 $operator = '>';
464 break;
465 case Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO:
466 $operator = '>=';
467 break;
468 case Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_OPERATOR_LIKE:
469 $operator = 'LIKE';
470 break;
471 default:
472 throw new Tx_Extbase_Persistence_Exception('Unsupported operator encountered.', 1242816073);
473 }
474
475 return $operator;
476 }
477
478 /**
479 * Replace query placeholders in a query part by the given
480 * parameters.
481 *
482 * @param string $queryPart The query part with placeholders
483 * @param array $parameters The parameters
484 * @return string The query part with replaced placeholders
485 */
486 protected function replacePlaceholders(&$sqlString, array $parameters) {
487 if (substr_count($sqlString, '?') !== count($parameters)) throw new Tx_Extbase_Persistence_Exception('The number of question marks to replace must be equal to the number of parameters.', 1242816074);
488 $offset = 0;
489 foreach ($parameters as $parameter) {
490 $markPosition = strpos($sqlString, '?', $offset);
491 if ($markPosition !== FALSE) {
492 if ($parameter === NULL) {
493 $parameter = 'NULL';
494 } else {
495 $parameter = $this->databaseHandle->fullQuoteStr($parameter, 'foo'); // FIXME This may not work with DBAL; check this
496 }
497 $sqlString = substr($sqlString, 0, $markPosition) . $parameter . substr($sqlString, $markPosition + 1);
498 }
499 $offset = $markPosition + strlen($parameter);
500 }
501 }
502
503 /**
504 * Builds the enable fields statement
505 *
506 * @param string $tableName The database table name
507 * @param array &$sql The query parts
508 * @return void
509 */
510 protected function addEnableFieldsStatement($tableName, array &$sql) {
511 if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
512 if (TYPO3_MODE === 'FE') {
513 $statement = substr($GLOBALS['TSFE']->sys_page->enableFields($tableName), 5);
514 } else { // TYPO3_MODE === 'BE'
515 $statement = substr(t3lib_BEfunc::BEenableFields($tableName), 5);
516 }
517 if(!empty($statement)) {
518 $sql['additionalWhereClause'][] = $statement;
519 }
520 }
521 }
522
523 /**
524 * Builds the page ID checking statement
525 *
526 * @param string $tableName The database table name
527 * @param array &$sql The query parts
528 * @return void
529 */
530 protected function addPageIdStatement($tableName, array &$sql) {
531 // TODO We have to call the appropriate API method if we are in TYPO3BE mode
532 if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
533 $extbaseFrameworkConfiguration = Tx_Extbase_Dispatcher::getExtbaseFrameworkConfiguration();
534 $sql['additionalWhereClause'][] = $tableName . '.pid IN (' . implode(', ', t3lib_div::intExplode(',', $extbaseFrameworkConfiguration['persistence']['storagePid'])) . ')';
535 }
536 }
537
538 /**
539 * Transforms orderings into SQL
540 *
541 * @param array $orderings
542 * @param array &$sql
543 * @param array &$parameters
544 * @param array $boundVariableValues
545 * @return void
546 */
547 protected function parseOrderings(array $orderings, Tx_Extbase_Persistence_QOM_SourceInterface $source, array &$sql, array &$parameters, array $boundVariableValues) {
548 foreach ($orderings as $ordering) {
549 $operand = $ordering->getOperand();
550 $order = $ordering->getOrder();
551 if ($operand instanceof Tx_Extbase_Persistence_QOM_PropertyValue) {
552 switch ($order) {
553 case Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_ORDER_ASCENDING:
554 $order = 'ASC';
555 break;
556 case Tx_Extbase_Persistence_QOM_QueryObjectModelConstantsInterface::JCR_ORDER_DESCENDING:
557 $order = 'DESC';
558 break;
559 default:
560 throw new Tx_Extbase_Persistence_Exception('Unsupported order encountered.', 1242816074);
561 }
562 $columnName = $this->dataMapper->convertPropertyNameToColumnName($ordering->getOperand()->getPropertyName(), $source->getNodeTypeName());
563 $sql['orderings'][] = $columnName . ' ' . $order;
564 }
565 }
566 }
567
568 /**
569 * Transforms limit and offset into SQL
570 *
571 * @param int $limit
572 * @param int $offset
573 * @param array &$sql
574 * @return void
575 */
576 protected function parseLimitAndOffset($limit, $offset, array &$sql) {
577 if ($limit !== NULL && $offset !== NULL) {
578 $sql['limit'] = $offset . ', ' . $limit;
579 } elseif ($limit !== NULL) {
580 $sql['limit'] = $limit;
581 }
582 }
583
584 /**
585 * Transforms a Resource from a database query to an array of rows. Performs the language and
586 * workspace overlay before.
587 *
588 * @param Tx_Extbase_Persistence_QOM_SourceInterface $source The source (selector od join)
589 *
590 * @return array The result as an array of rows (tuples)
591 */
592 protected function getRowsFromResult(Tx_Extbase_Persistence_QOM_SourceInterface $source, $res) {
593 $rows = array();
594 while ($row = $this->databaseHandle->sql_fetch_assoc($res)) {
595 if ($source instanceof Tx_Extbase_Persistence_QOM_SelectorInterface) {
596 // FIXME The overlay is only performed if we query a single table; no joins
597 $row = $this->doLanguageAndWorkspaceOverlay($source->getSelectorName(), $row);
598 }
599 if (is_array($row)) {
600 // TODO Check if this is necessary, maybe the last line is enough
601 $arrayKeys = range(0,count($row));
602 array_fill_keys($arrayKeys, $row);
603 $rows[] = $row;
604 }
605 }
606 return $rows;
607 }
608
609 /**
610 * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
611 * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
612 *
613 * @param Tx_Extbase_Persistence_Mapper_DataMap $dataMap
614 * @param array $row The row array (as reference)
615 * @param string $languageUid The language id
616 * @param string $workspaceUidUid The workspace id
617 * @return void
618 */
619 protected function doLanguageAndWorkspaceOverlay($tableName, array $row, $languageUid = NULL, $workspaceUid = NULL) {
620 if (!($this->pageSelectObject instanceof t3lib_pageSelect)) {
621 if (TYPO3_MODE == 'FE') {
622 if (is_object($GLOBALS['TSFE'])) {
623 $this->pageSelectObject = $GLOBALS['TSFE']->sys_page;
624 if ($languageUid === NULL) {
625 $languageUid = $GLOBALS['TSFE']->sys_language_content;
626 }
627 } else {
628 require_once(PATH_t3lib . 'class.t3lib_page.php');
629 $this->pageSelectObject = t3lib_div::makeInstance('t3lib_pageSelect');
630 if ($languageUid === NULL) {
631 $languageUid = intval(t3lib_div::_GP('L'));
632 }
633 }
634 if ($workspaceUid !== NULL) {
635 $this->pageSelectObject->versioningWorkspaceId = $workspaceUid;
636 }
637 } else {
638 require_once(PATH_t3lib . 'class.t3lib_page.php');
639 $this->pageSelectObject = t3lib_div::makeInstance( 't3lib_pageSelect' );
640 if ($workspaceUid === NULL) {
641 $workspaceUid = $GLOBALS['BE_USER']->workspace;
642 }
643 $this->pageSelectObject->versioningWorkspaceId = $workspaceUid;
644 }
645 }
646
647 $this->pageSelectObject->versionOL($tableName, $row, TRUE);
648 $row = $this->pageSelectObject->getRecordOverlay($tableName, $row, $languageUid, ''); //'hideNonTranslated'
649 // TODO Skip if empty languageoverlay (languagevisibility)
650 return $row;
651 }
652
653 /**
654 * Checks if there are SQL errors in the last query, and if yes, throw an exception.
655 *
656 * @return void
657 * @throws Tx_Extbase_Persistence_Storage_Exception_SqlError
658 */
659 protected function checkSqlErrors() {
660 $error = $this->databaseHandle->sql_error();
661 if ($error !== '') {
662 throw new Tx_Extbase_Persistence_Storage_Exception_SqlError($error, 1247602160);
663 }
664 }
665
666 /**
667 * Clear the TYPO3 page cache for the given record.
668 * Much of this functionality is taken from t3lib_tcemain::clear_cache() which unfortunately only works with logged-in BE user.
669 *
670 * @param $tableName Table name of the record
671 * @param $uid UID of the record
672 * @return void
673 */
674 protected function clearPageCache($tableName, $uid) {
675 $extbaseSettings = Tx_Extbase_Dispatcher::getExtbaseFrameworkConfiguration();
676 if (isset($extbaseSettings['persistence']['enableAutomaticCacheClearing']) && $extbaseSettings['persistence']['enableAutomaticCacheClearing'] === '1') {
677 } else {
678 // if disabled, return
679 return;
680 }
681
682 $result = $this->databaseHandle->exec_SELECTquery('pid', $tableName, 'uid='.intval($uid));
683
684 $pageIdsToClear = array();
685 if ($row = $this->databaseHandle->sql_fetch_assoc($result)) {
686 $storagePage = $row['pid'];
687 $pageIdsToClear[] = $storagePage;
688 }
689 if (!$storagePage) {
690 return;
691 }
692
693 $pageTSConfig = t3lib_BEfunc::getPagesTSconfig($storagePage);
694 if (isset($pageTSConfig['TCEMAIN.']['clearCacheCmd'])) {
695 $clearCacheCommands = t3lib_div::trimExplode(',',strtolower($pageTSConfig['TCEMAIN.']['clearCacheCmd']),1);
696 $clearCacheCommands = array_unique($clearCacheCommands);
697 foreach ($clearCacheCommands as $clearCacheCommand) {
698 if (t3lib_div::testInt($clearCacheCommand)) {
699 $pageIdsToClear[] = $clearCacheCommand;
700 }
701 }
702 }
703
704 Tx_Extbase_Utility_Cache::clearPageCache($pageIdsToClear);
705 }
706 }
707
708 ?>