[+BUGFIX] Extbase (Persistence): Fixed a problem where Objects are deleted from the...
[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: Backend.php 2183 2009-04-24 14:28:37Z k-fish $
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_IdentityMap
50 **/
51 protected $identityMap;
52
53 /**
54 * @var Tx_Extbase_Persistence_QueryFactoryInterface
55 */
56 protected $queryFactory;
57
58 /**
59 * @var Tx_Extbase_Persistence_QOM_QueryObjectModelFactoryInterface
60 */
61 protected $QOMFactory;
62
63 /**
64 * @var Tx_Extbase_Persistence_ValueFactoryInterface
65 */
66 protected $valueFactory;
67
68 /**
69 * @var Tx_Extbase_Persistence_Storage_BackendInterface
70 */
71 protected $storageBackend;
72
73 /**
74 * @var Tx_Extbase_Persistence_DataMapper
75 */
76 protected $dataMapper;
77
78 /**
79 * The TYPO3 reference index object
80 *
81 * @var t3lib_refindex
82 **/
83 protected $referenceIndex;
84
85 /**
86 * Constructs the backend
87 *
88 * @param Tx_Extbase_Persistence_Session $session The persistence session used to persist data
89 */
90 public function __construct(Tx_Extbase_Persistence_Session $session, Tx_Extbase_Persistence_Storage_BackendInterface $storageBackend) {
91 $this->session = $session;
92 $this->storageBackend = $storageBackend;
93 $this->referenceIndex = t3lib_div::makeInstance('t3lib_refindex');
94 $this->aggregateRootObjects = new Tx_Extbase_Persistence_ObjectStorage();
95 }
96
97 /**
98 * Injects the DataMapper to map nodes to objects
99 *
100 * @param Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper
101 * @return void
102 */
103 public function injectDataMapper(Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper) {
104 $this->dataMapper = $dataMapper;
105 }
106
107 /**
108 * Injects the identity map
109 *
110 * @param Tx_Extbase_Persistence_IdentityMap $identityMap
111 * @return void
112 */
113 public function injectIdentityMap(Tx_Extbase_Persistence_IdentityMap $identityMap) {
114 $this->identityMap = $identityMap;
115 }
116
117 /**
118 * Injects the QueryFactory
119 *
120 * @param Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory
121 * @return void
122 */
123 public function injectQueryFactory(Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory) {
124 $this->queryFactory = $queryFactory;
125 }
126
127 /**
128 * Injects the QueryObjectModelFactory
129 *
130 * @param Tx_Extbase_Persistence_QOM_QueryObjectModelFactoryInterface $dataMapper
131 * @return void
132 */
133 public function injectQOMFactory(Tx_Extbase_Persistence_QOM_QueryObjectModelFactoryInterface $QOMFactory) {
134 $this->QOMFactory = $QOMFactory;
135 }
136
137 /**
138 * Injects the ValueFactory
139 *
140 * @param Tx_Extbase_Persistence_ValueFactoryInterface $valueFactory
141 * @return void
142 */
143 public function injectValueFactory(Tx_Extbase_Persistence_ValueFactoryInterface $valueFactory) {
144 $this->valueFactory = $valueFactory;
145 }
146
147 /**
148 * Returns the repository session
149 *
150 * @return Tx_Extbase_Persistence_Session
151 */
152 public function getSession() {
153 return $this->session;
154 }
155
156 /**
157 * Returns the Data Mapper
158 *
159 * @return Tx_Extbase_Persistence_Mapper_DataMapper
160 */
161 public function getDataMapper() {
162 return $this->dataMapper;
163 }
164
165 /**
166 * Returns the current QOM factory
167 *
168 * @return Tx_Extbase_Persistence_QOM_QueryObjectModelFactoryInterface
169 */
170 public function getQOMFactory() {
171 return $this->QOMFactory;
172 }
173
174 /**
175 * Returns the current value factory
176 *
177 * @return Tx_Extbase_Persistence_ValueFactoryInterface
178 */
179 public function getValueFactory() {
180 return $this->valueFactory;
181 }
182
183 /**
184 * Returns the current identityMap
185 *
186 * @return Tx_Extbase_Persistence_IdentityMap
187 */
188 public function getIdentityMap() {
189 return $this->identityMap;
190 }
191
192 /**
193 * Returns the (internal) identifier for the object, if it is known to the
194 * backend. Otherwise NULL is returned.
195 *
196 * @param object $object
197 * @return string The identifier for the object if it is known, or NULL
198 */
199 public function getIdentifierByObject($object) {
200 if ($this->identityMap->hasObject($object)) {
201 return $this->identityMap->getIdentifierByObject($object);
202 } else {
203 return NULL;
204 }
205 }
206
207 /**
208 * Returns the object with the (internal) identifier, if it is known to the
209 * backend. Otherwise NULL is returned.
210 *
211 * @param string $identifier
212 * @param string $className
213 * @return object The object for the identifier if it is known, or NULL
214 */
215 public function getObjectByIdentifier($identifier, $className) {
216 if ($this->identityMap->hasIdentifier($identifier, $className)) {
217 return $this->identityMap->getObjectByIdentifier($identifier, $className);
218 } else {
219 $query = $this->queryFactory->create($className);
220 $result = $query->matching($query->withUid($identifier))->execute();
221 $object = NULL;
222 if (count($result) > 0) {
223 $object = current($result);
224 }
225 return $object;
226 }
227 }
228
229 /**
230 * Checks if the given object has ever been persisted.
231 *
232 * @param object $object The object to check
233 * @return boolean TRUE if the object is new, FALSE if the object exists in the repository
234 */
235 public function isNewObject($object) {
236 return ($this->getIdentifierByObject($object) === NULL);
237 }
238
239 /**
240 * Replaces the given object by the second object.
241 *
242 * This method will unregister the existing object at the identity map and
243 * register the new object instead. The existing object must therefore
244 * already be registered at the identity map which is the case for all
245 * reconstituted objects.
246 *
247 * The new object will be identified by the uid which formerly belonged
248 * to the existing object. The existing object looses its uid.
249 *
250 * @param object $existingObject The existing object
251 * @param object $newObject The new object
252 * @return void
253 */
254 public function replaceObject($existingObject, $newObject) {
255 $existingUid = $this->getIdentifierByObject($existingObject);
256 if ($existingUid === NULL) throw new Tx_Extbase_Persistence_Exception_UnknownObject('The given object is unknown to this persistence backend.', 1238070163);
257
258 $this->identityMap->unregisterObject($existingObject);
259 $this->identityMap->registerObject($newObject, $existingUid);
260 }
261
262 /**
263 * Sets the aggregate root objects
264 *
265 * @param Tx_Extbase_Persistence_ObjectStorage $objects
266 * @return void
267 */
268 public function setAggregateRootObjects(Tx_Extbase_Persistence_ObjectStorage $objects) {
269 $this->aggregateRootObjects = $objects;
270 }
271
272 /**
273 * Sets the deleted objects
274 *
275 * @param Tx_Extbase_Persistence_ObjectStorage $objects
276 * @return void
277 */
278 public function setDeletedObjects(Tx_Extbase_Persistence_ObjectStorage $objects) {
279 $this->deletedObjects = $objects;
280 }
281
282 /**
283 * Commits the current persistence session.
284 *
285 * @return void
286 */
287 public function commit() {
288 $this->persistObjects();
289 $this->processDeletedObjects();
290 }
291
292 /**
293 * Traverse and persist all aggregate roots and their object graph.
294 *
295 * @return void
296 */
297 protected function persistObjects() {
298 foreach ($this->aggregateRootObjects as $object) {
299 $this->persistObject($object);
300 }
301 }
302
303 /**
304 * Persists an object (instert, update) and its related objects (instert, update, delete).
305 *
306 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be inserted
307 * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
308 * @param string $parentPropertyName The name of the property the object is stored in
309 * @return void
310 */
311 protected function persistObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL) {
312 $row = array();
313 $queuedObjects = array();
314 $className = get_class($object);
315 $dataMap = $this->dataMapper->getDataMap($className);
316
317 if ($object instanceof Tx_Extbase_DomainObject_AbstractValueObject) {
318 $this->mapAlreadyPersistedValueObject($object);
319 }
320
321 $properties = $object->_getProperties();
322 // Fill up $row[$columnName] array with changed values which need to be stored
323 foreach ($properties as $propertyName => $propertyValue) {
324 if (!$dataMap->isPersistableProperty($propertyName) || ($propertyValue instanceof Tx_Extbase_Persistence_LazyLoadingProxy)) {
325 continue;
326 }
327
328 $columnMap = $dataMap->getColumnMap($propertyName);
329 if ($object->_isNew() || $object->_isDirty($propertyName)) {
330 if ($columnMap->isRelation()) {
331 $this->persistRelations($object, $propertyName, $propertyValue, $columnMap, $queuedObjects, $row);
332 } else {
333 // We have not a relation, this means it is a scalar (like a string or interger value) or an object
334 $row[$columnMap->getColumnName()] = $dataMap->convertPropertyValueToFieldValue($propertyValue);
335 }
336 }
337 } // end property iteration for loop
338
339 // The state of the Object has to be stored in a local variable because $object->_isNew() will return FALSE after
340 // the object was inserted. We need the initial state here.
341 $objectIsNew = $object->_isNew();
342 if ($objectIsNew === TRUE) {
343 $this->insertObject($object, $parentObject, $parentPropertyName, $row);
344 } elseif ($object->_isDirty()) {
345 $this->updateObject($object, $parentObject, $parentPropertyName, $row);
346 }
347
348 $objectHasToBeUpdated = $this->processQueuedChildObjects($object, $queuedObjects, $row);
349 if ($objectHasToBeUpdated === TRUE) {
350 // TODO Check if this can be merged with the first update
351 $this->updateObject($object, $parentObject, $parentPropertyName, $row);
352 }
353
354 // SK: I need to check the code below more thoroughly
355 if ($parentObject instanceof Tx_Extbase_DomainObject_DomainObjectInterface && !empty($parentPropertyName)) {
356 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
357 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
358 if (($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY)) {
359 $this->insertRelationInRelationtable($object, $parentObject, $parentPropertyName);
360 }
361 }
362
363 // TODO: We should only register the object if it was really updated.
364 $this->identityMap->registerObject($object, $object->getUid());
365 $object->_memorizeCleanState();
366 }
367
368 /**
369 * Persists a relation. Objects of a 1:n or m:n relation are queued and processed with the parent object. A 1:1 relation
370 * gets persisted immediately. Objects which were removed from the property were deleted immediately, too.
371 *
372 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object
373 * @param string $propertyName The name of the property the related objects are stored in
374 * @param mixed $propertyValue The property value (an array of Domain Objects, ObjectStorage holding Domain Objects or a Domain Object itself)
375 * @return void
376 */
377 protected function persistRelations(Tx_Extbase_DomainObject_DomainObjectInterface $object, $propertyName, $propertyValue, Tx_Extbase_Persistence_Mapper_ColumnMap $columnMap, &$queuedObjects, &$row) {
378 $columnName = $columnMap->getColumnName();
379 if (($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) || ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY)) {
380 if (is_array($propertyValue) || $propertyValue instanceof ArrayAccess) {
381 foreach ($propertyValue as $relatedObject) {
382 $queuedObjects[$propertyName][] = $relatedObject;
383 }
384 foreach ($this->getRemovedChildObjects($object, $propertyName) as $removedObject) {
385 // TODO The removed object should only be deleted automatically if it is not managed by a Repository
386 if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
387 $this->deleteObject($removedObject, $object, $propertyName);
388 } elseif ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
389 $this->deleteRelationFromRelationtable($removedObject, $object, $propertyName);
390 }
391 }
392 $row[$columnName] = count($propertyValue); // Will be overwritten if the related objects are referenced by a comma separated list
393 }
394 } elseif ($propertyValue instanceof Tx_Extbase_DomainObject_DomainObjectInterface) {
395 // TODO Handle Value Objects different
396 if ($propertyValue->_isNew() || $propertyValue->_isDirty()) {
397 $this->persistObject($propertyValue);
398 }
399 $row[$columnName] = $propertyValue->getUid();
400 }
401 }
402
403 /**
404 * Returns the removed objects determined by a comparison of the clean property value
405 * with the actual property value.
406 *
407 * @param Tx_Extbase_DomainObject_AbstractEntity $object The object
408 * @param string $parentPropertyName The name of the property
409 * @return array An array of removed objects
410 */
411 protected function getRemovedChildObjects(Tx_Extbase_DomainObject_AbstractEntity $object, $propertyName) {
412 $removedObjects = array();
413 if (!$object->_isNew()) {
414 $cleanProperties = $object->_getCleanProperties();
415 $cleanPropertyValue = $cleanProperties[$propertyName];
416 $propertyValue = $object->_getProperty($propertyName);
417 if ($cleanPropertyValue instanceof Tx_Extbase_Persistence_ObjectStorage) {
418 $cleanPropertyValue = $cleanPropertyValue->toArray();
419 }
420 if ($propertyValue instanceof Tx_Extbase_Persistence_ObjectStorage) {
421 $propertyValue = $propertyValue->toArray();
422 }
423 foreach ($cleanPropertyValue as $item) {
424 if (!in_array($item, $propertyValue)) {
425 $removedObjects[] = $item;
426 }
427 }
428 }
429 return $removedObjects;
430 }
431
432 /**
433 * This function processes the queued child objects to be persisted. The queue is build while looping over the
434 * collection of Domain Objects stored in a object property.
435 *
436 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object holding the collection
437 * @param array $queuedObjects The queued child objects
438 * @param array $row The row to be inseted or updated in the database. Passed as reference.
439 * @return boolean TRUE if the object holding the collection has to be updated; otherwise FALSE
440 */
441 protected function processQueuedChildObjects(Tx_Extbase_DomainObject_DomainObjectInterface $object, array $queuedChildObjects, array &$row) {
442 $objectHasToBeUpdated = FALSE;
443 $className = get_class($object);
444 $dataMap = $this->dataMapper->getDataMap($className);
445 foreach ($queuedChildObjects as $propertyName => $childObjects) {
446 $columnMap = $dataMap->getColumnMap($propertyName);
447 $childPidArray = array();
448 foreach($childObjects as $childObject) {
449 $this->persistObject($childObject, $object, $propertyName);
450 if ($childObject instanceof Tx_Extbase_DomainObject_DomainObjectInterface) {
451 $childPidArray[] = (int)$childObject->getUid();
452 }
453 }
454 if ($columnMap->getParentKeyFieldName() === NULL) { // TRUE: We have to generate a comma separated list stored in the field
455 if (count($childPidArray) > 0) {
456 $row[$propertyName] = implode(',', $childPidArray);
457 } else {
458 $row[$propertyName] = NULL;
459 }
460 $objectHasToBeUpdated = TRUE;
461 }
462 }
463 return $objectHasToBeUpdated;
464 }
465
466 /**
467 * Tests, if the given Value Object already exists in the storage backend. If so, it maps the uid
468 * to the given object.
469 *
470 * @param Tx_Extbase_DomainObject_AbstractValueObject $object The object to be tested
471 */
472 protected function mapAlreadyPersistedValueObject(Tx_Extbase_DomainObject_AbstractValueObject $object) {
473 $dataMap = $this->dataMapper->getDataMap(get_class($object));
474 $properties = $object->_getProperties();
475 $result = $this->storageBackend->hasValueObject($properties, $dataMap);
476 if ($result !== FALSE) {
477 $object->_setProperty('uid', $result);
478 }
479 }
480
481 /**
482 * Inserts an object in the storage
483 *
484 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be insterted in the storage
485 * @param Tx_Extbase_DomainObject_AbstractEntity|NULL $parentObject The parent object (if any)
486 * @param string|NULL $parentPropertyName The name of the property
487 * @param array $row The $row
488 */
489 protected function insertObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, Tx_Extbase_DomainObject_AbstractEntity $parentObject = NULL, $parentPropertyName = NULL, array &$row) {
490 $className = get_class($object);
491 $dataMap = $this->dataMapper->getDataMap($className);
492 $tableName = $dataMap->getTableName();
493 $this->addCommonFieldsToRow($object, $parentObject, $parentPropertyName, $row);
494 $uid = $this->storageBackend->addRow(
495 $tableName,
496 $row
497 );
498 $object->_setProperty('uid', $uid);
499 $this->referenceIndex->updateRefIndexTable($tableName, $uid);
500 }
501
502 /**
503 * Inserts mm-relation into a relation table
504 *
505 * @param Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject The related object
506 * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
507 * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
508 * @return void
509 */
510 protected function insertRelationInRelationtable(Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $parentPropertyName) {
511 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
512 $columnMap = $dataMap->getColumnMap($parentPropertyName);
513 $row = array(
514 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
515 $columnMap->getChildKeyFieldName() => (int)$relatedObject->getUid(),
516 'sorting' => 9999 // TODO sorting of mm table items
517 );
518 $relationTableName = $columnMap->getRelationTableName();
519 // FIXME Reenable support for tablenames
520 // $childTableName = $columnMap->getChildTableName();
521 // if (isset($childTableName)) {
522 // $row['tablenames'] = $childTableName;
523 // }
524 $res = $this->storageBackend->addRow(
525 $relationTableName,
526 $row,
527 TRUE);
528 return $res;
529 }
530
531 /**
532 * Delete an mm-relation from a relation table
533 *
534 * @param Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject The related object
535 * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
536 * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
537 * @return void
538 */
539 protected function deleteRelationFromRelationtable(Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $parentPropertyName) {
540 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
541 $columnMap = $dataMap->getColumnMap($parentPropertyName);
542 $relationTableName = $columnMap->getRelationTableName();
543 $res = $this->storageBackend->removeRow(
544 $relationTableName,
545 array(
546 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
547 $columnMap->getChildKeyFieldName() => (int)$relatedObject->getUid(),
548 ),
549 FALSE);
550 return $res;
551 }
552
553 /**
554 * Updates a given object in the storage
555 *
556 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be insterted in the storage
557 * @param Tx_Extbase_DomainObject_AbstractEntity|NULL $parentObject The parent object (if any)
558 * @param string|NULL $parentPropertyName The name of the property
559 * @param array $row The $row
560 */
561 protected function updateObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, array &$row) {
562 $className = get_class($object);
563 $dataMap = $this->dataMapper->getDataMap($className);
564 $tableName = $dataMap->getTableName();
565 $this->addCommonFieldsToRow($object, $parentObject, $parentPropertyName, $row);
566 $uid = $object->getUid();
567 $row['uid'] = $uid;
568 $res = $this->storageBackend->updateRow(
569 $tableName,
570 $row
571 );
572 $this->referenceIndex->updateRefIndexTable($tableName, $uid);
573 return $res;
574 }
575
576 /**
577 * Returns a table row to be inserted or updated in the database
578 *
579 * @param Tx_Extbase_Persistence_Mapper_DataMap $dataMap The appropriate data map representing a database table
580 * @param array $properties The properties of the object
581 * @return array A single row to be inserted in the database
582 */
583 protected function addCommonFieldsToRow(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, array &$row) {
584 $className = get_class($object);
585 $dataMap = $this->dataMapper->getDataMap($className);
586 if ($dataMap->hasCreationDateColumn() && $object->_isNew()) {
587 $row[$dataMap->getCreationDateColumnName()] = $GLOBALS['EXEC_TIME'];
588 }
589 if ($dataMap->hasTimestampColumn()) {
590 $row[$dataMap->getTimestampColumnName()] = $GLOBALS['EXEC_TIME'];
591 }
592
593 if ($object->_isNew() && $dataMap->hasPidColumn() && !isset($row['pid'])) {
594 $row['pid'] = $this->determineStoragePageIdForNewRecord($object);
595 }
596
597 if ($parentObject instanceof Tx_Extbase_DomainObject_DomainObjectInterface && !empty($parentPropertyName)) {
598 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
599 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
600 // FIXME This is a hacky solution
601 if ($parentColumnMap->getTypeOfRelation() !== Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
602 $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
603 if ($parentKeyFieldName !== NULL) {
604 $row[$parentKeyFieldName] = $parentObject->getUid();
605 }
606 $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
607 if ($parentTableFieldName !== NULL) {
608 $row[$parentTableFieldName] = $parentDataMap->getTableName();
609 }
610 }
611 }
612 }
613
614 /**
615 * Determine the storage page ID for a given NEW record
616 *
617 * This does the following:
618 * - If there is a TypoScript configuration "classes.CLASSNAME.newRecordStoragePid", that is used to store new records.
619 * - If there is no such TypoScript configuration, it uses the first value of The "storagePid" taken for reading records.
620 *
621 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object
622 * @return int the storage Page ID where the object should be stored
623 */
624 protected function determineStoragePageIdForNewRecord(Tx_Extbase_DomainObject_DomainObjectInterface $object) {
625 $className = get_class($object);
626 $extbaseSettings = Tx_Extbase_Dispatcher::getExtbaseFrameworkConfiguration();
627
628 if (isset($extbaseSettings['persistence']['classes'][$className]) && !empty($extbaseSettings['persistence']['classes'][$className]['newRecordStoragePid'])) {
629 return (int)$extbaseSettings['persistence']['classes'][$className]['newRecordStoragePid'];
630 } else {
631 $storagePidList = t3lib_div::intExplode(',', $extbaseSettings['persistence']['storagePid']);
632 return (int) $storagePidList[0];
633 }
634 }
635
636 /**
637 * Iterate over deleted aggregate root objects and process them
638 *
639 * @return void
640 */
641 protected function processDeletedObjects() {
642 foreach ($this->deletedObjects as $object) {
643 $this->deleteObject($object);
644 $this->identityMap->unregisterObject($object);
645 }
646 $this->deletedObjects = new Tx_Extbase_Persistence_ObjectStorage();
647 }
648
649 /**
650 * Deletes an object, it's 1:n related objects, and the m:n relations in relation tables (but not the m:n related objects!)
651 *
652 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be insterted in the storage
653 * @param Tx_Extbase_DomainObject_AbstractEntity|NULL $parentObject The parent object (if any)
654 * @param string|NULL $parentPropertyName The name of the property
655 * @param bool $markAsDeleted Shold we only mark the row as deleted instead of deleting (TRUE by default)?
656 * @return void
657 */
658 protected function deleteObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, $markAsDeleted = TRUE) {
659 // TODO Implement recursive deletions
660 $dataMap = $this->dataMapper->getDataMap(get_class($object));
661 $tableName = $dataMap->getTableName();
662 if (($markAsDeleted === TRUE) && $dataMap->hasDeletedColumn()) {
663 $deletedColumnName = $dataMap->getDeletedColumnName();
664 $res = $this->storageBackend->updateRow(
665 $tableName,
666 array(
667 'uid' => $object->getUid(),
668 $deletedColumnName => 1
669 )
670 );
671 } else {
672 $res = $this->storageBackend->removeRow(
673 $tableName,
674 array('uid' => $object->getUid())
675 );
676 }
677 $this->referenceIndex->updateRefIndexTable($tableName, $uid);
678 }
679
680 /**
681 * Delegates the call to the Data Map.
682 * Returns TRUE if the property is persistable (configured in $TCA)
683 *
684 * @param string $className The property name
685 * @param string $propertyName The property name
686 * @return boolean TRUE if the property is persistable (configured in $TCA)
687 */
688 public function isPersistableProperty($className, $propertyName) {
689 $dataMap = $this->dataMapper->getDataMap($className);
690 return $dataMap->isPersistableProperty($propertyName);
691 }
692
693 }
694
695 ?>