Merge "[BUGFIX] ConfigurationManager use FrontendSimulator only if neccessary"
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Backend.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 persistence backend. This backend maps objects to the relational model of the storage backend.
30 * It persists all added, removed and changed objects.
31 *
32 * @package Extbase
33 * @subpackage Persistence
34 * @version $Id$
35 */
36 class Tx_Extbase_Persistence_Backend implements Tx_Extbase_Persistence_BackendInterface, t3lib_Singleton {
37
38 /**
39 * @var Tx_Extbase_Persistence_Session
40 */
41 protected $session;
42
43 /**
44 * @var Tx_Extbase_Persistence_ObjectStorage
45 */
46 protected $aggregateRootObjects;
47
48 /**
49 * @var Tx_Extbase_Persistence_ObjectStorage
50 */
51 protected $visitedDuringPersistence;
52
53 /**
54 * @var Tx_Extbase_Persistence_IdentityMap
55 **/
56 protected $identityMap;
57
58 /**
59 * @var Tx_Extbase_Reflection_Service
60 */
61 protected $reflectionService;
62
63 /**
64 * @var Tx_Extbase_Persistence_QueryFactoryInterface
65 */
66 protected $queryFactory;
67
68 /**
69 * @var Tx_Extbase_Persistence_QOM_QueryObjectModelFactory
70 */
71 protected $qomFactory;
72
73 /**
74 * @var Tx_Extbase_Persistence_Storage_BackendInterface
75 */
76 protected $storageBackend;
77
78 /**
79 * @var Tx_Extbase_Persistence_Mapper_DataMapper
80 */
81 protected $dataMapper;
82
83 /**
84 * The TYPO3 reference index object
85 *
86 * @var t3lib_refindex
87 **/
88 protected $referenceIndex;
89
90 /**
91 * @var Tx_Extbase_Configuration_ConfigurationManagerInterface
92 */
93 protected $configurationManager;
94
95 /**
96 * Constructs the backend
97 *
98 * @return void
99 */
100 public function __construct(Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager) {
101 $this->configurationManager = $configurationManager;
102 $frameworkConfiguration = $configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
103 if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
104 $this->referenceIndex = t3lib_div::makeInstance('t3lib_refindex');
105 }
106 }
107
108 /**
109 * @param Tx_Extbase_Persistence_Session $session
110 * @return void
111 */
112 public function injectSession(Tx_Extbase_Persistence_Session $session) {
113 $this->session = $session;
114 }
115
116 /**
117 * @param Tx_Extbase_Persistence_Storage_BackendInterface $storageBackend
118 * @return void
119 */
120 public function injectStorageBackend(Tx_Extbase_Persistence_Storage_BackendInterface $storageBackend) {
121 $this->storageBackend = $storageBackend;
122 }
123
124 /**
125 * Injects the DataMapper to map nodes to objects
126 *
127 * @param Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper
128 * @return void
129 */
130 public function injectDataMapper(Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper) {
131 $this->dataMapper = $dataMapper;
132 }
133
134 /**
135 * Injects the identity map
136 *
137 * @param Tx_Extbase_Persistence_IdentityMap $identityMap
138 * @return void
139 */
140 public function injectIdentityMap(Tx_Extbase_Persistence_IdentityMap $identityMap) {
141 $this->identityMap = $identityMap;
142 }
143
144 /**
145 * Injects the Reflection Service
146 *
147 * @param Tx_Extbase_Reflection_Service
148 * @return void
149 */
150 public function injectReflectionService(Tx_Extbase_Reflection_Service $reflectionService) {
151 $this->reflectionService = $reflectionService;
152 }
153
154 /**
155 * Injects the QueryFactory
156 *
157 * @param Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory
158 * @return void
159 */
160 public function injectQueryFactory(Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory) {
161 $this->queryFactory = $queryFactory;
162 }
163
164 /**
165 * Injects the QueryObjectModelFactory
166 *
167 * @param Tx_Extbase_Persistence_QOM_QueryObjectModelFactory $qomFactory
168 * @return void
169 */
170 public function injectQomFactory(Tx_Extbase_Persistence_QOM_QueryObjectModelFactory $qomFactory) {
171 $this->qomFactory = $qomFactory;
172 }
173
174 /**
175 * Returns the repository session
176 *
177 * @return Tx_Extbase_Persistence_Session
178 */
179 public function getSession() {
180 return $this->session;
181 }
182
183 /**
184 * Returns the Data Mapper
185 *
186 * @return Tx_Extbase_Persistence_Mapper_DataMapper
187 */
188 public function getDataMapper() {
189 return $this->dataMapper;
190 }
191
192 /**
193 * Returns the current QOM factory
194 *
195 * @return Tx_Extbase_Persistence_QOM_QueryObjectModelFactory
196 */
197 public function getQomFactory() {
198 return $this->qomFactory;
199 }
200
201 /**
202 * Returns the current identityMap
203 *
204 * @return Tx_Extbase_Persistence_IdentityMap
205 */
206 public function getIdentityMap() {
207 return $this->identityMap;
208 }
209
210 /**
211 * Returns the reflection service
212 *
213 * @return Tx_Extbase_Reflection_Service
214 */
215 public function getReflectionService() {
216 return $this->reflectionService;
217 }
218
219 /**
220 * Returns the number of records matching the query.
221 *
222 * @param Tx_Extbase_Persistence_QueryInterface $query
223 * @return integer
224 * @api
225 */
226 public function getObjectCountByQuery(Tx_Extbase_Persistence_QueryInterface $query) {
227 return $this->storageBackend->getObjectCountByQuery($query);
228 }
229
230 /**
231 * Returns the object data matching the $query.
232 *
233 * @param Tx_Extbase_Persistence_QueryInterface $query
234 * @return array
235 * @api
236 */
237 public function getObjectDataByQuery(Tx_Extbase_Persistence_QueryInterface $query) {
238 return $this->storageBackend->getObjectDataByQuery($query);
239 }
240
241 /**
242 * Returns the (internal) identifier for the object, if it is known to the
243 * backend. Otherwise NULL is returned.
244 *
245 * @param object $object
246 * @return string The identifier for the object if it is known, or NULL
247 */
248 public function getIdentifierByObject($object) {
249 if ($object instanceof Tx_Extbase_Persistence_LazyLoadingProxy) {
250 $object = $object->_loadRealInstance();
251 if (!is_object($object)) {
252 return NULL;
253 }
254 }
255 if ($this->identityMap->hasObject($object)) {
256 return $this->identityMap->getIdentifierByObject($object);
257 } else {
258 return NULL;
259 }
260 }
261
262 /**
263 * Returns the object with the (internal) identifier, if it is known to the
264 * backend. Otherwise NULL is returned.
265 *
266 * @param string $identifier
267 * @param string $className
268 * @return object The object for the identifier if it is known, or NULL
269 */
270 public function getObjectByIdentifier($identifier, $className) {
271 if ($this->identityMap->hasIdentifier($identifier, $className)) {
272 return $this->identityMap->getObjectByIdentifier($identifier, $className);
273 } else {
274 $query = $this->queryFactory->create($className);
275 return $query->matching(
276 $query->equals('uid', $identifier))
277 ->execute()
278 ->getFirst();
279 }
280 }
281
282 /**
283 * Checks if the given object has ever been persisted.
284 *
285 * @param object $object The object to check
286 * @return boolean TRUE if the object is new, FALSE if the object exists in the repository
287 */
288 public function isNewObject($object) {
289 return ($this->getIdentifierByObject($object) === NULL);
290 }
291
292 /**
293 * Replaces the given object by the second object.
294 *
295 * This method will unregister the existing object at the identity map and
296 * register the new object instead. The existing object must therefore
297 * already be registered at the identity map which is the case for all
298 * reconstituted objects.
299 *
300 * The new object will be identified by the uid which formerly belonged
301 * to the existing object. The existing object looses its uid.
302 *
303 * @param object $existingObject The existing object
304 * @param object $newObject The new object
305 * @return void
306 */
307 public function replaceObject($existingObject, $newObject) {
308 $existingUid = $this->getIdentifierByObject($existingObject);
309 if ($existingUid === NULL) throw new Tx_Extbase_Persistence_Exception_UnknownObject('The given object is unknown to this persistence backend.', 1238070163);
310
311 $this->identityMap->unregisterObject($existingObject);
312 $this->identityMap->registerObject($newObject, $existingUid);
313 }
314
315 /**
316 * Sets the aggregate root objects
317 *
318 * @param Tx_Extbase_Persistence_ObjectStorage $objects
319 * @return void
320 */
321 public function setAggregateRootObjects(Tx_Extbase_Persistence_ObjectStorage $objects) {
322 $this->aggregateRootObjects = $objects;
323 }
324
325 /**
326 * Sets the deleted objects
327 *
328 * @param Tx_Extbase_Persistence_ObjectStorage $objects
329 * @return void
330 */
331 public function setDeletedObjects(Tx_Extbase_Persistence_ObjectStorage $objects) {
332 $this->deletedObjects = $objects;
333 }
334
335 /**
336 * Commits the current persistence session.
337 *
338 * @return void
339 */
340 public function commit() {
341 $this->persistObjects();
342 $this->processDeletedObjects();
343 }
344
345 /**
346 * Traverse and persist all aggregate roots and their object graph.
347 *
348 * @return void
349 */
350 protected function persistObjects() {
351 $this->visitedDuringPersistence = new Tx_Extbase_Persistence_ObjectStorage();
352 foreach ($this->aggregateRootObjects as $object) {
353 if (!$this->identityMap->hasObject($object)) {
354 $this->insertObject($object);
355 }
356 }
357 foreach ($this->aggregateRootObjects as $object) {
358 $this->persistObject($object);
359 }
360 }
361
362 /**
363 * Persists the given object.
364 *
365 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be inserted
366 * @return void
367 */
368 protected function persistObject(Tx_Extbase_DomainObject_DomainObjectInterface $object) {
369 if (isset($this->visitedDuringPersistence[$object])) {
370 return;
371 }
372 $row = array();
373 $queue = array();
374 $dataMap = $this->dataMapper->getDataMap(get_class($object));
375 $properties = $object->_getProperties();
376 foreach ($properties as $propertyName => $propertyValue) {
377 if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) continue;
378 $columnMap = $dataMap->getColumnMap($propertyName);
379 if ($propertyValue instanceof Tx_Extbase_Persistence_ObjectStorage) {
380 if ($object->_isNew() || $propertyValue->_isDirty()) {
381 $this->persistObjectStorage($propertyValue, $object, $propertyName, $row);
382 }
383 foreach ($propertyValue as $containedObject) {
384 if ($containedObject instanceof Tx_Extbase_DomainObject_DomainObjectInterface) {
385 $queue[] = $containedObject;
386 }
387 }
388 } elseif ($propertyValue instanceof Tx_Extbase_DomainObject_DomainObjectInterface) {
389 if ($object->_isDirty($propertyName)) {
390 if ($propertyValue->_isNew()) {
391 $this->insertObject($propertyValue);
392 }
393 $row[$columnMap->getColumnName()] = $this->getPlainValue($propertyValue);
394 }
395 $queue[] = $propertyValue;
396 } elseif ($object->_isNew() || $object->_isDirty($propertyName)) {
397 $row[$columnMap->getColumnName()] = $this->getPlainValue($propertyValue);
398 }
399 }
400 if (count($row) > 0) {
401 $this->updateObject($object, $row);
402 $object->_memorizeCleanState();
403 }
404 $this->visitedDuringPersistence[$object] = $object->getUid();
405 foreach ($queue as $queuedObject) {
406 $this->persistObject($queuedObject);
407 }
408 }
409
410 /**
411 * Checks, if the property value is lazy loaded and was not initialized
412 *
413 * @param mixed $propertyValue The property value
414 * @return bool
415 */
416 protected function propertyValueIsLazyLoaded($propertyValue) {
417 if ($propertyValue instanceof Tx_Extbase_Persistence_LazyLoadingProxy) return TRUE;
418 if ($propertyValue instanceof Tx_Extbase_Persistence_LazyObjectStorage) {
419 if ($propertyValue->isInitialized() === FALSE) {
420 return TRUE;
421 }
422 }
423 return FALSE;
424 }
425
426 /**
427 * Persists a an object storage. Objects of a 1:n or m:n relation are queued and processed with the parent object. A 1:1 relation
428 * gets persisted immediately. Objects which were removed from the property were detached from the parent object. They will not be
429 * deleted by default. You have to annotate the property with "@cascade remove" if you want them to be deleted as well.
430 *
431 * @param Tx_Extbase_Persistence_ObjectStorage $objectStorage The object storage to be persisted.
432 * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object. One of the properties holds the object storage.
433 * @param string $propertyName The name of the property holding the object storage.
434 * @param array $row The row array of the parent object to be persisted. It's passed by reference and gets filled with either a comma separated list of uids (csv) or the number of contained objects.
435 * @return void
436 */
437 protected function persistObjectStorage(Tx_Extbase_Persistence_ObjectStorage $objectStorage, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $propertyName, array &$row) {
438 $className = get_class($parentObject);
439 $columnMap = $this->dataMapper->getDataMap($className)->getColumnMap($propertyName);
440 $columnName = $columnMap->getColumnName();
441 $propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
442
443 foreach ($this->getRemovedChildObjects($parentObject, $propertyName) as $removedObject) {
444 if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY && $propertyMetaData['cascade'] === 'remove') {
445 $this->removeObject($removedObject);
446 } else {
447 $this->detachObjectFromParentObject($removedObject, $parentObject, $propertyName);
448 }
449 }
450
451 if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
452 $this->deleteAllRelationsFromRelationtable($parentObject, $propertyName);
453 }
454
455 $currentUids = array();
456 $sortingPosition = 1;
457 foreach ($objectStorage as $object) {
458 if ($object->_isNew()) {
459 $this->insertObject($object);
460 }
461 $currentUids[] = $object->getUid();
462 $this->attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
463 $sortingPosition++;
464 }
465
466 if ($columnMap->getParentKeyFieldName() === NULL) {
467 $row[$columnMap->getColumnName()] = implode(',', $currentUids);
468 } else {
469 $row[$columnMap->getColumnName()] = $this->dataMapper->countRelated($parentObject, $propertyName);
470 }
471 }
472
473 /**
474 * Returns the removed objects determined by a comparison of the clean property value
475 * with the actual property value.
476 *
477 * @param Tx_Extbase_DomainObject_AbstractEntity $object The object
478 * @param string $parentPropertyName The name of the property
479 * @return array An array of removed objects
480 */
481 protected function getRemovedChildObjects(Tx_Extbase_DomainObject_AbstractEntity $object, $propertyName) {
482 $removedObjects = array();
483 $cleanPropertyValue = $object->_getCleanProperty($propertyName);
484 if (is_array($cleanPropertyValue) || $cleanPropertyValue instanceof Iterator) {
485 $propertyValue = $object->_getProperty($propertyName);
486 foreach ($cleanPropertyValue as $containedObject) {
487 if (!$propertyValue->contains($containedObject)) {
488 $removedObjects[] = $containedObject;
489 }
490 }
491 }
492 return $removedObjects;
493 }
494
495 /**
496 * Updates the fields defining the relation between the object and the parent object.
497 *
498 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object
499 * @param Tx_Extbase_DomainObject_AbstractEntity $parentObject
500 * @param string $parentPropertyName
501 * @return void
502 */
503 protected function attachObjectToParentObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, Tx_Extbase_DomainObject_AbstractEntity $parentObject, $parentPropertyName, $sortingPosition = 0) {
504 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
505 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
506 if ($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
507 $row = array();
508 $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
509 if ($parentKeyFieldName !== NULL) {
510 $row[$parentKeyFieldName] = $parentObject->getUid();
511 $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
512 if ($parentTableFieldName !== NULL) {
513 $row[$parentTableFieldName] = $parentDataMap->getTableName();
514 }
515 }
516 $childSortByFieldName = $parentColumnMap->getChildSortByFieldName();
517 if (!empty($childSortByFieldName)) {
518 $row[$childSortByFieldName] = $sortingPosition;
519 }
520 if (count($row) > 0) {
521 $this->updateObject($object, $row);
522 }
523 } elseif ($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
524 $this->insertRelationInRelationtable($object, $parentObject, $parentPropertyName, $sortingPosition);
525 }
526 }
527
528 /**
529 * Updates the fields defining the relation between the object and the parent object.
530 *
531 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object
532 * @param Tx_Extbase_DomainObject_AbstractEntity $parentObject
533 * @param string $parentPropertyName
534 * @return void
535 */
536 protected function detachObjectFromParentObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, Tx_Extbase_DomainObject_AbstractEntity $parentObject, $parentPropertyName) {
537 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
538 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
539 if ($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
540 $row = array();
541 $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
542 if ($parentKeyFieldName !== NULL) {
543 $row[$parentKeyFieldName] = '';
544 $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
545 if ($parentTableFieldName !== NULL) {
546 $row[$parentTableFieldName] = '';
547 }
548 }
549 $childSortByFieldName = $parentColumnMap->getChildSortByFieldName();
550 if (!empty($childSortByFieldName)) {
551 $row[$childSortByFieldName] = 0;
552 }
553 if (count($row) > 0) {
554 $this->updateObject($object, $row);
555 }
556 } elseif ($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
557 $this->deleteRelationFromRelationtable($object, $parentObject, $parentPropertyName);
558 }
559 }
560
561 /**
562 * Inserts an object in the storage backend
563 *
564 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be insterted in the storage
565 * @return void
566 */
567 protected function insertObject(Tx_Extbase_DomainObject_DomainObjectInterface $object) {
568 if ($object instanceof Tx_Extbase_DomainObject_AbstractValueObject) {
569 $result = $this->getUidOfAlreadyPersistedValueObject($object);
570 if ($result !== FALSE) {
571 $object->_setProperty('uid', (int)$result);
572 return;
573 }
574 }
575
576 $dataMap = $this->dataMapper->getDataMap(get_class($object));
577 $row = array();
578 $this->addCommonFieldsToRow($object, $row);
579 if($dataMap->getLanguageIdColumnName() !== NULL) {
580 $row[$dataMap->getLanguageIdColumnName()] = -1;
581 }
582 $uid = $this->storageBackend->addRow(
583 $dataMap->getTableName(),
584 $row
585 );
586 $object->_setProperty('uid', (int)$uid);
587 $frameworkConfiguration = $this->configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
588 if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
589 $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), $uid);
590 }
591 $this->identityMap->registerObject($object, $uid);
592 }
593
594 /**
595 * Tests, if the given Value Object already exists in the storage backend and if so, it returns the uid.
596 *
597 * @param Tx_Extbase_DomainObject_AbstractValueObject $object The object to be tested
598 * @return mixed The matching uid if an object was found, else FALSE
599 */
600 protected function getUidOfAlreadyPersistedValueObject(Tx_Extbase_DomainObject_AbstractValueObject $object) {
601 return $this->storageBackend->getUidOfAlreadyPersistedValueObject($object);
602 }
603
604 /**
605 * Inserts mm-relation into a relation table
606 *
607 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The related object
608 * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
609 * @param string $propertyName The name of the parent object's property where the related objects are stored in
610 * @param int $sortingPosition Defaults to NULL
611 * @return int The uid of the inserted row
612 */
613 protected function insertRelationInRelationtable(Tx_Extbase_DomainObject_DomainObjectInterface $object, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $propertyName, $sortingPosition = NULL) {
614 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
615 $columnMap = $dataMap->getColumnMap($propertyName);
616 $row = array(
617 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
618 $columnMap->getChildKeyFieldName() => (int)$object->getUid(),
619 $columnMap->getChildSortByFieldName() => !is_null($sortingPosition) ? (int)$sortingPosition : 0
620 );
621 $relationTableName = $columnMap->getRelationTableName();
622 // FIXME Reenable support for tablenames
623 // $childTableName = $columnMap->getChildTableName();
624 // if (isset($childTableName)) {
625 // $row['tablenames'] = $childTableName;
626 // }
627 if ($columnMap->getRelationTablePageIdColumnName() !== NULL) {
628 $row[$columnMap->getRelationTablePageIdColumnName()] = $this->determineStoragePageIdForNewRecord();
629 }
630
631 $relationTableInsertFields = $columnMap->getRelationTableInsertFields();
632 if (count($relationTableInsertFields)) {
633 foreach($relationTableInsertFields as $insertField => $insertValue) {
634 $row[$insertField] = $insertValue;
635 }
636 }
637
638 $res = $this->storageBackend->addRow(
639 $relationTableName,
640 $row,
641 TRUE);
642 return $res;
643 }
644
645 /**
646 * Delete all mm-relations of a parent from a relation table
647 *
648 * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
649 * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
650 * @return bool
651 */
652 protected function deleteAllRelationsFromRelationtable(Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $parentPropertyName) {
653 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
654 $columnMap = $dataMap->getColumnMap($parentPropertyName);
655 $relationTableName = $columnMap->getRelationTableName();
656
657 $relationMatchFields = array(
658 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid()
659 );
660
661 $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
662 if (is_array($relationTableMatchFields) && count($relationTableMatchFields) > 0) {
663 $relationMatchFields = array_merge($relationTableMatchFields,$relationMatchFields);
664 }
665
666 $res = $this->storageBackend->removeRow(
667 $relationTableName,
668 $relationMatchFields,
669 FALSE);
670 return $res;
671 }
672
673 /**
674 * Delete an mm-relation from a relation table
675 *
676 * @param Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject The related object
677 * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
678 * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
679 * @return bool
680 */
681 protected function deleteRelationFromRelationtable(Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $parentPropertyName) {
682 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
683 $columnMap = $dataMap->getColumnMap($parentPropertyName);
684 $relationTableName = $columnMap->getRelationTableName();
685 $res = $this->storageBackend->removeRow(
686 $relationTableName,
687 array(
688 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
689 $columnMap->getChildKeyFieldName() => (int)$relatedObject->getUid(),
690 ),
691 FALSE);
692 return $res;
693 }
694
695 /**
696 * Updates a given object in the storage
697 *
698 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be updated
699 * @param array $row Row to be stored
700 * @return bool
701 */
702 protected function updateObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, array $row) {
703 $dataMap = $this->dataMapper->getDataMap(get_class($object));
704 $this->addCommonFieldsToRow($object, $row);
705 $row['uid'] = $object->getUid();
706 if($dataMap->getLanguageIdColumnName() !== NULL) {
707 $row[$dataMap->getLanguageIdColumnName()] = $object->_getProperty('_languageUid');
708 if ($object->_getProperty('_localizedUid') !== NULL) {
709 $row['uid'] = $object->_getProperty('_localizedUid');
710 }
711 }
712 $res = $this->storageBackend->updateRow(
713 $dataMap->getTableName(),
714 $row
715 );
716 $frameworkConfiguration = $this->configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
717 if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
718 $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), $row['uid']);
719 }
720 return $res;
721 }
722
723 /**
724 * Adds common databse fields to a row
725 *
726 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object
727 * @param array $row
728 * @return void
729 */
730 protected function addCommonFieldsToRow(Tx_Extbase_DomainObject_DomainObjectInterface $object, array &$row) {
731 $dataMap = $this->dataMapper->getDataMap(get_class($object));
732 $this->addCommonDateFieldsToRow($object, $row);
733 if ($dataMap->getRecordTypeColumnName() !== NULL && $dataMap->getRecordType() !== NULL) {
734 $row[$dataMap->getRecordTypeColumnName()] = $dataMap->getRecordType();
735 }
736 if ($object->_isNew() && !isset($row['pid'])) {
737 $row['pid'] = $this->determineStoragePageIdForNewRecord($object);
738 }
739 }
740
741 /**
742 * Adjustes the common date fields of the given row to the current time
743 *
744 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object
745 * @param array $row The row to be updated
746 * @return void
747 */
748 protected function addCommonDateFieldsToRow(Tx_Extbase_DomainObject_DomainObjectInterface $object, array &$row) {
749 $dataMap = $this->dataMapper->getDataMap(get_class($object));
750 if ($object->_isNew() && $dataMap->getCreationDateColumnName() !== NULL) {
751 $row[$dataMap->getCreationDateColumnName()] = $GLOBALS['EXEC_TIME'];
752 }
753 if ($dataMap->getModificationDateColumnName() !== NULL) {
754 $row[$dataMap->getModificationDateColumnName()] = $GLOBALS['EXEC_TIME'];
755 }
756 }
757
758 /**
759 * Iterate over deleted aggregate root objects and process them
760 *
761 * @return void
762 */
763 protected function processDeletedObjects() {
764 foreach ($this->deletedObjects as $object) {
765 $this->removeObject($object);
766 $this->identityMap->unregisterObject($object);
767 }
768 $this->deletedObjects = new Tx_Extbase_Persistence_ObjectStorage();
769 }
770
771 /**
772 * Deletes an object
773 *
774 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be removed from the storage
775 * @param bool $markAsDeleted Wether to just flag the row deleted (default) or really delete it
776 * @return void
777 */
778 protected function removeObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $markAsDeleted = TRUE) {
779 $dataMap = $this->dataMapper->getDataMap(get_class($object));
780 $tableName = $dataMap->getTableName();
781 if (($markAsDeleted === TRUE) && ($dataMap->getDeletedFlagColumnName() !== NULL)) {
782 $deletedColumnName = $dataMap->getDeletedFlagColumnName();
783 $row = array(
784 'uid' => $object->getUid(),
785 $deletedColumnName => 1
786 );
787 $this->addCommonDateFieldsToRow($object, $row);
788 $res = $this->storageBackend->updateRow(
789 $tableName,
790 $row
791 );
792 } else {
793 $res = $this->storageBackend->removeRow(
794 $tableName,
795 array('uid' => $object->getUid())
796 );
797 }
798 $this->removeRelatedObjects($object);
799 $frameworkConfiguration = $this->configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
800 if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
801 $this->referenceIndex->updateRefIndexTable($tableName, $object->getUid());
802 }
803 }
804
805 /**
806 * Remove related objects
807 *
808 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to scanned for related objects
809 * @return void
810 */
811 protected function removeRelatedObjects(Tx_Extbase_DomainObject_DomainObjectInterface $object) {
812 $className = get_class($object);
813 $dataMap = $this->dataMapper->getDataMap($className);
814 $classSchema = $this->reflectionService->getClassSchema($className);
815
816 $properties = $object->_getProperties();
817 foreach ($properties as $propertyName => $propertyValue) {
818 $columnMap = $dataMap->getColumnMap($propertyName);
819 $propertyMetaData = $classSchema->getProperty($propertyName);
820 if ($propertyMetaData['cascade'] === 'remove') {
821 if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
822 foreach ($propertyValue as $containedObject) {
823 $this->removeObject($containedObject);
824 }
825 } elseif ($propertyValue instanceof Tx_Extbase_DomainObject_DomainObjectInterface) {
826 $this->removeObject($propertyValue);
827 }
828 }
829 }
830 }
831
832 /**
833 * Determine the storage page ID for a given NEW record
834 *
835 * This does the following:
836 * - If the domain object has an accessible property 'pid' (i.e. through a getPid() method), that is used to store the record.
837 * - If there is a TypoScript configuration "classes.CLASSNAME.newRecordStoragePid", that is used to store new records.
838 * - If there is no such TypoScript configuration, it uses the first value of The "storagePid" taken for reading records.
839 *
840 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object
841 * @return int the storage Page ID where the object should be stored
842 */
843 protected function determineStoragePageIdForNewRecord(Tx_Extbase_DomainObject_DomainObjectInterface $object = NULL) {
844 $frameworkConfiguration = $this->configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
845 if ($object !== NULL) {
846 if (Tx_Extbase_Reflection_ObjectAccess::isPropertyGettable($object, 'pid')) {
847 $pid = Tx_Extbase_Reflection_ObjectAccess::getProperty($object, 'pid');
848 if (isset($pid)) {
849 return (int)$pid;
850 }
851 }
852 $className = get_class($object);
853 if (isset($frameworkConfiguration['persistence']['classes'][$className]) && !empty($frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'])) {
854 return (int)$frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'];
855 }
856 }
857 $storagePidList = t3lib_div::intExplode(',', $frameworkConfiguration['persistence']['storagePid']);
858 return (int) $storagePidList[0];
859 }
860
861 /**
862 * Returns a plain value, i.e. objects are flattened out if possible.
863 *
864 * @param mixed $input
865 * @return mixed
866 */
867 protected function getPlainValue($input) {
868 if ($input instanceof DateTime) {
869 return $input->format('U');
870 } elseif ($input instanceof Tx_Extbase_DomainObject_DomainObjectInterface) {
871 return $input->getUid();
872 } elseif (is_bool($input)) {
873 return $input === TRUE ? 1 : 0;
874 } else {
875 return $input;
876 }
877 }
878
879 }
880
881 ?>