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