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