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