408f8b9b77998322c626e3f4080e8551c974af26
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Generic / Backend.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Persistence\Generic;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
18 use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap;
19 use TYPO3\CMS\Extbase\Persistence\ObjectMonitoringInterface;
20
21 /**
22 * A persistence backend. This backend maps objects to the relational model of the storage backend.
23 * It persists all added, removed and changed objects.
24 */
25 class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface, \TYPO3\CMS\Core\SingletonInterface {
26
27 /**
28 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Session
29 */
30 protected $session;
31
32 /**
33 * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
34 */
35 protected $persistenceManager;
36
37 /**
38 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
39 */
40 protected $aggregateRootObjects;
41
42 /**
43 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
44 */
45 protected $deletedEntities;
46
47 /**
48 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
49 */
50 protected $changedEntities;
51
52 /**
53 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
54 */
55 protected $visitedDuringPersistence;
56
57 /**
58 * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
59 */
60 protected $reflectionService;
61
62 /**
63 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory
64 */
65 protected $qomFactory;
66
67 /**
68 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface
69 */
70 protected $storageBackend;
71
72 /**
73 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper
74 */
75 protected $dataMapper;
76
77 /**
78 * The TYPO3 reference index object
79 *
80 * @var \TYPO3\CMS\Core\Database\ReferenceIndex
81 */
82 protected $referenceIndex;
83
84 /**
85 * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
86 */
87 protected $configurationManager;
88
89 /**
90 * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
91 */
92 protected $signalSlotDispatcher;
93
94 /**
95 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Session $session
96 */
97 public function injectSession(\TYPO3\CMS\Extbase\Persistence\Generic\Session $session) {
98 $this->session = $session;
99 }
100
101 /**
102 * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
103 */
104 public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService) {
105 $this->reflectionService = $reflectionService;
106 }
107
108 /**
109 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory $qomFactory
110 */
111 public function injectQomFactory(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory $qomFactory) {
112 $this->qomFactory = $qomFactory;
113 }
114
115 /**
116 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface $storageBackend
117 */
118 public function injectStorageBackend(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface $storageBackend) {
119 $this->storageBackend = $storageBackend;
120 }
121
122 /**
123 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper
124 */
125 public function injectDataMapper(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper) {
126 $this->dataMapper = $dataMapper;
127 }
128
129 /**
130 * @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher
131 */
132 public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher) {
133 $this->signalSlotDispatcher = $signalSlotDispatcher;
134 }
135
136 /**
137 * Constructs the backend
138 *
139 * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
140 */
141 public function __construct(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager) {
142 $this->configurationManager = $configurationManager;
143 $this->referenceIndex = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ReferenceIndex::class);
144 $this->aggregateRootObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
145 $this->deletedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
146 $this->changedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
147 }
148
149 /**
150 * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
151 */
152 public function setPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager) {
153 $this->persistenceManager = $persistenceManager;
154 }
155
156 /**
157 * Returns the repository session
158 *
159 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Session
160 */
161 public function getSession() {
162 return $this->session;
163 }
164
165 /**
166 * Returns the Data Mapper
167 *
168 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper
169 */
170 public function getDataMapper() {
171 return $this->dataMapper;
172 }
173
174 /**
175 * Returns the current QOM factory
176 *
177 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory
178 */
179 public function getQomFactory() {
180 return $this->qomFactory;
181 }
182
183 /**
184 * Returns the reflection service
185 *
186 * @return \TYPO3\CMS\Extbase\Reflection\ReflectionService
187 */
188 public function getReflectionService() {
189 return $this->reflectionService;
190 }
191
192 /**
193 * Returns the number of records matching the query.
194 *
195 * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
196 * @return int
197 * @api
198 */
199 public function getObjectCountByQuery(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query) {
200 return $this->storageBackend->getObjectCountByQuery($query);
201 }
202
203 /**
204 * Returns the object data matching the $query.
205 *
206 * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
207 * @return array
208 * @api
209 */
210 public function getObjectDataByQuery(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query) {
211 $query = $this->emitBeforeGettingObjectDataSignal($query);
212 $result = $this->storageBackend->getObjectDataByQuery($query);
213 $result = $this->emitafterGettingObjectDataSignal($query, $result);
214 return $result;
215 }
216
217 /**
218 * Emits a signal before object data is fetched
219 *
220 * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
221 * @return \TYPO3\CMS\Extbase\Persistence\QueryInterface Modified query
222 */
223 protected function emitBeforeGettingObjectDataSignal(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query) {
224 $signalArguments = $this->signalSlotDispatcher->dispatch(__CLASS__, 'beforeGettingObjectData', array($query));
225 return $signalArguments[0];
226 }
227
228 /**
229 * Emits a signal after object data is fetched
230 *
231 * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
232 * @param array $result
233 * @return array Modified result
234 */
235 protected function emitAfterGettingObjectDataSignal(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query, array $result) {
236 $signalArguments = $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterGettingObjectData', array($query, $result));
237 return $signalArguments[1];
238 }
239
240 /**
241 * Returns the (internal) identifier for the object, if it is known to the
242 * backend. Otherwise NULL is returned.
243 *
244 * @param object $object
245 * @return string|NULL The identifier for the object if it is known, or NULL
246 */
247 public function getIdentifierByObject($object) {
248 if ($object instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
249 $object = $object->_loadRealInstance();
250 if (!is_object($object)) {
251 return NULL;
252 }
253 }
254 return $this->session->getIdentifierByObject($object);
255 }
256
257 /**
258 * Returns the object with the (internal) identifier, if it is known to the
259 * backend. Otherwise NULL is returned.
260 *
261 * @param string $identifier
262 * @param string $className
263 * @return object|NULL The object for the identifier if it is known, or NULL
264 */
265 public function getObjectByIdentifier($identifier, $className) {
266 if ($this->session->hasIdentifier($identifier, $className)) {
267 return $this->session->getObjectByIdentifier($identifier, $className);
268 } else {
269 $query = $this->persistenceManager->createQueryForType($className);
270 $query->getQuerySettings()->setRespectStoragePage(FALSE);
271 $query->getQuerySettings()->setRespectSysLanguage(FALSE);
272 return $query->matching($query->equals('uid', $identifier))->execute()->getFirst();
273 }
274 }
275
276 /**
277 * Checks if the given object has ever been persisted.
278 *
279 * @param object $object The object to check
280 * @return bool TRUE if the object is new, FALSE if the object exists in the repository
281 */
282 public function isNewObject($object) {
283 return $this->getIdentifierByObject($object) === NULL;
284 }
285
286 /**
287 * Sets the aggregate root objects
288 *
289 * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects
290 * @return void
291 */
292 public function setAggregateRootObjects(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects) {
293 $this->aggregateRootObjects = $objects;
294 }
295
296 /**
297 * Sets the changed objects
298 *
299 * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities
300 * @return void
301 */
302 public function setChangedEntities(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities) {
303 $this->changedEntities = $entities;
304 }
305
306 /**
307 * Sets the deleted objects
308 *
309 * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities
310 * @return void
311 */
312 public function setDeletedEntities(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities) {
313 $this->deletedEntities = $entities;
314 }
315
316 /**
317 * Commits the current persistence session.
318 *
319 * @return void
320 */
321 public function commit() {
322 $this->persistObjects();
323 $this->processDeletedObjects();
324 }
325
326 /**
327 * Traverse and persist all aggregate roots and their object graph.
328 *
329 * @return void
330 */
331 protected function persistObjects() {
332 $this->visitedDuringPersistence = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
333 foreach ($this->aggregateRootObjects as $object) {
334 /** @var DomainObjectInterface $object */
335 if ($object->_isNew()) {
336 $this->insertObject($object);
337 }
338 $this->persistObject($object, NULL);
339 }
340 foreach ($this->changedEntities as $object) {
341 $this->persistObject($object, NULL);
342 }
343 }
344
345 /**
346 * Persists the given object.
347 *
348 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be inserted
349 * @return void
350 */
351 protected function persistObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object) {
352 if (isset($this->visitedDuringPersistence[$object])) {
353 return;
354 }
355 $row = array();
356 $queue = array();
357 $dataMap = $this->dataMapper->getDataMap(get_class($object));
358 $properties = $object->_getProperties();
359 foreach ($properties as $propertyName => $propertyValue) {
360 if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) {
361 continue;
362 }
363 $columnMap = $dataMap->getColumnMap($propertyName);
364 if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage) {
365 $cleanProperty = $object->_getCleanProperty($propertyName);
366 // objectstorage needs to be persisted if the object is new, the objectstorge is dirty, meaning it has
367 // been changed after initial build, or an empty objectstorge is present and the cleanstate objectstorage
368 // has childelements, meaning all elements should been removed from the objectstorage
369 if ($object->_isNew() || $propertyValue->_isDirty() || ($propertyValue->count() === 0 && $cleanProperty && $cleanProperty->count() > 0)) {
370 $this->persistObjectStorage($propertyValue, $object, $propertyName, $row);
371 $propertyValue->_memorizeCleanState();
372 }
373 foreach ($propertyValue as $containedObject) {
374 if ($containedObject instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) {
375 $queue[] = $containedObject;
376 }
377 }
378 } elseif ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface
379 && $object instanceof ObjectMonitoringInterface) {
380 if ($object->_isDirty($propertyName)) {
381 if ($propertyValue->_isNew()) {
382 $this->insertObject($propertyValue, $object, $propertyName);
383 }
384 // Check explicitly for NULL, as getPlainValue would convert this to 'NULL'
385 $row[$columnMap->getColumnName()] = $propertyValue !== NULL
386 ? $this->dataMapper->getPlainValue($propertyValue)
387 : NULL;
388 }
389 $queue[] = $propertyValue;
390 } elseif ($object->_isNew() || $object->_isDirty($propertyName)) {
391 $row[$columnMap->getColumnName()] = $this->dataMapper->getPlainValue($propertyValue, $columnMap);
392 }
393 }
394 if (!empty($row)) {
395 $this->updateObject($object, $row);
396 $object->_memorizeCleanState();
397 }
398 $this->visitedDuringPersistence[$object] = $object->getUid();
399 foreach ($queue as $queuedObject) {
400 $this->persistObject($queuedObject);
401 }
402 $this->emitAfterPersistObjectSignal($object);
403 }
404
405 /**
406 * Checks, if the property value is lazy loaded and was not initialized
407 *
408 * @param mixed $propertyValue The property value
409 * @return bool
410 */
411 protected function propertyValueIsLazyLoaded($propertyValue) {
412 if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
413 return TRUE;
414 }
415 if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage) {
416 if ($propertyValue->isInitialized() === FALSE) {
417 return TRUE;
418 }
419 }
420 return FALSE;
421 }
422
423 /**
424 * Persists an object storage. Objects of a 1:n or m:n relation are queued and processed with the parent object. A 1:1 relation
425 * gets persisted immediately. Objects which were removed from the property were detached from the parent object. They will not be
426 * deleted by default. You have to annotate the property with "@cascade remove" if you want them to be deleted as well.
427 *
428 * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $objectStorage The object storage to be persisted.
429 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object. One of the properties holds the object storage.
430 * @param string $propertyName The name of the property holding the object storage.
431 * @param array &$row The row array of the parent object to be persisted. It's passed by reference and gets filled with either a comma separated list of uids (csv) or the number of contained objects.
432 * @return void
433 */
434 protected function persistObjectStorage(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $objectStorage, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $propertyName, array &$row) {
435 $className = get_class($parentObject);
436 $columnMap = $this->dataMapper->getDataMap($className)->getColumnMap($propertyName);
437 $propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
438 foreach ($this->getRemovedChildObjects($parentObject, $propertyName) as $removedObject) {
439 $this->detachObjectFromParentObject($removedObject, $parentObject, $propertyName);
440 if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY && $propertyMetaData['cascade'] === 'remove') {
441 $this->removeEntity($removedObject);
442 }
443 }
444
445 $currentUids = array();
446 $sortingPosition = 1;
447 $updateSortingOfFollowing = FALSE;
448
449 foreach ($objectStorage as $object) {
450 /** @var DomainObjectInterface $object */
451 if (empty($currentUids)) {
452 $sortingPosition = 1;
453 } else {
454 $sortingPosition++;
455 }
456 $cleanProperty = $parentObject->_getCleanProperty($propertyName);
457 if ($object->_isNew()) {
458 $this->insertObject($object);
459 $this->attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
460 // if a new object is inserted, all objects after this need to have their sorting updated
461 $updateSortingOfFollowing = TRUE;
462 } elseif ($cleanProperty === NULL || $cleanProperty->getPosition($object) === NULL) {
463 // if parent object is new then it doesn't have cleanProperty yet; before attaching object it's clean position is null
464 $this->attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
465 // if a relation is dirty (speaking the same object is removed and added again at a different position), all objects after this needs to be updated the sorting
466 $updateSortingOfFollowing = TRUE;
467 } elseif ($objectStorage->isRelationDirty($object) || $cleanProperty->getPosition($object) !== $objectStorage->getPosition($object)) {
468 $this->updateRelationOfObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
469 $updateSortingOfFollowing = TRUE;
470 } elseif ($updateSortingOfFollowing) {
471 if ($sortingPosition > $objectStorage->getPosition($object)) {
472 $this->updateRelationOfObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
473 } else {
474 $sortingPosition = $objectStorage->getPosition($object);
475 }
476 }
477 $currentUids[] = $object->getUid();
478 }
479
480 if ($columnMap->getParentKeyFieldName() === NULL) {
481 $row[$columnMap->getColumnName()] = implode(',', $currentUids);
482 } else {
483 $row[$columnMap->getColumnName()] = $this->dataMapper->countRelated($parentObject, $propertyName);
484 }
485 }
486
487 /**
488 * Returns the removed objects determined by a comparison of the clean property value
489 * with the actual property value.
490 *
491 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object
492 * @param string $propertyName
493 * @return array An array of removed objects
494 */
495 protected function getRemovedChildObjects(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, $propertyName) {
496 $removedObjects = array();
497 $cleanPropertyValue = $object->_getCleanProperty($propertyName);
498 if (is_array($cleanPropertyValue) || $cleanPropertyValue instanceof \Iterator) {
499 $propertyValue = $object->_getProperty($propertyName);
500 foreach ($cleanPropertyValue as $containedObject) {
501 if (!$propertyValue->contains($containedObject)) {
502 $removedObjects[] = $containedObject;
503 }
504 }
505 }
506 return $removedObjects;
507 }
508
509 /**
510 * Updates the fields defining the relation between the object and the parent object.
511 *
512 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
513 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject
514 * @param string $parentPropertyName
515 * @param int $sortingPosition
516 * @return void
517 */
518 protected function attachObjectToParentObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition = 0) {
519 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
520 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
521 if ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
522 $this->attachObjectToParentObjectRelationHasMany($object, $parentObject, $parentPropertyName, $sortingPosition);
523 } elseif ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
524 $this->insertRelationInRelationtable($object, $parentObject, $parentPropertyName, $sortingPosition);
525 }
526 }
527
528 /**
529 * Updates the fields defining the relation between the object and the parent object.
530 *
531 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
532 * @param \TYPO3\CMS\Extbase\DomainObject\AbstractEntity $parentObject
533 * @param string $parentPropertyName
534 * @param int $sortingPosition
535 * @return void
536 */
537 protected function updateRelationOfObjectToParentObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\AbstractEntity $parentObject, $parentPropertyName, $sortingPosition = 0) {
538 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
539 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
540 if ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
541 $this->attachObjectToParentObjectRelationHasMany($object, $parentObject, $parentPropertyName, $sortingPosition);
542 } elseif ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
543 $this->updateRelationInRelationTable($object, $parentObject, $parentPropertyName, $sortingPosition);
544 }
545 }
546
547 /**
548 * Updates fields defining the relation between the object and the parent object in relation has-many.
549 *
550 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
551 * @param \TYPO3\CMS\Extbase\DomainObject\AbstractEntity $parentObject
552 * @param string $parentPropertyName
553 * @param int $sortingPosition
554 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException
555 * @return void
556 */
557 protected function attachObjectToParentObjectRelationHasMany(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\AbstractEntity $parentObject, $parentPropertyName, $sortingPosition = 0) {
558 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
559 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
560 if ($parentColumnMap->getTypeOfRelation() !== \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
561 throw new \TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException(
562 'Parent column relation type is ' . $parentColumnMap->getTypeOfRelation() .
563 ' but should be ' . \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY,
564 1345368105
565 );
566 }
567 $row = array();
568 $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
569 if ($parentKeyFieldName !== NULL) {
570 $row[$parentKeyFieldName] = $parentObject->getUid();
571 $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
572 if ($parentTableFieldName !== NULL) {
573 $row[$parentTableFieldName] = $parentDataMap->getTableName();
574 }
575 $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
576 if (is_array($relationTableMatchFields)) {
577 $row = array_merge($relationTableMatchFields, $row);
578 }
579 }
580 $childSortByFieldName = $parentColumnMap->getChildSortByFieldName();
581 if (!empty($childSortByFieldName)) {
582 $row[$childSortByFieldName] = $sortingPosition;
583 }
584 if (!empty($row)) {
585 $this->updateObject($object, $row);
586 }
587 }
588
589 /**
590 * Updates the fields defining the relation between the object and the parent object.
591 *
592 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
593 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject
594 * @param string $parentPropertyName
595 * @return void
596 */
597 protected function detachObjectFromParentObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName) {
598 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
599 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
600 if ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
601 $row = array();
602 $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
603 if ($parentKeyFieldName !== NULL) {
604 $row[$parentKeyFieldName] = 0;
605 $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
606 if ($parentTableFieldName !== NULL) {
607 $row[$parentTableFieldName] = '';
608 }
609 $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
610 if (is_array($relationTableMatchFields) && !empty($relationTableMatchFields)) {
611 $row = array_merge(array_fill_keys(array_keys($relationTableMatchFields), ''), $row);
612 }
613 }
614 $childSortByFieldName = $parentColumnMap->getChildSortByFieldName();
615 if (!empty($childSortByFieldName)) {
616 $row[$childSortByFieldName] = 0;
617 }
618 if (!empty($row)) {
619 $this->updateObject($object, $row);
620 }
621 } elseif ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
622 $this->deleteRelationFromRelationtable($object, $parentObject, $parentPropertyName);
623 }
624 }
625
626 /**
627 * Inserts an object in the storage backend
628 *
629 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be insterted in the storage
630 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parentobject.
631 * @param string $parentPropertyName
632 * @return void
633 */
634 protected function insertObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject = NULL, $parentPropertyName = '') {
635 if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject) {
636 $result = $this->getUidOfAlreadyPersistedValueObject($object);
637 if ($result !== FALSE) {
638 $object->_setProperty('uid', (int)$result);
639 return;
640 }
641 }
642 $dataMap = $this->dataMapper->getDataMap(get_class($object));
643 $row = array();
644 $properties = $object->_getProperties();
645 foreach ($properties as $propertyName => $propertyValue) {
646 if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) {
647 continue;
648 }
649 $columnMap = $dataMap->getColumnMap($propertyName);
650 if ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_ONE) {
651 $row[$columnMap->getColumnName()] = 0;
652 } elseif ($columnMap->getTypeOfRelation() !== ColumnMap::RELATION_NONE) {
653 if ($columnMap->getParentKeyFieldName() === NULL) {
654 // CSV type relation
655 $row[$columnMap->getColumnName()] = '';
656 } else {
657 // MM type relation
658 $row[$columnMap->getColumnName()] = 0;
659 }
660 } else {
661 // Check explicitly for NULL, as getPlainValue would convert this to 'NULL'
662 $row[$columnMap->getColumnName()] = $propertyValue !== NULL
663 ? $this->dataMapper->getPlainValue($propertyValue)
664 : NULL;
665 }
666 }
667 $this->addCommonFieldsToRow($object, $row);
668 if ($dataMap->getLanguageIdColumnName() !== NULL) {
669 $row[$dataMap->getLanguageIdColumnName()] = -1;
670 }
671 if ($dataMap->getTranslationOriginColumnName() !== NULL) {
672 $row[$dataMap->getTranslationOriginColumnName()] = 0;
673 }
674 if ($dataMap->getTranslationOriginDiffSourceName() !== NULL) {
675 $row[$dataMap->getTranslationOriginDiffSourceName()] = '';
676 }
677 if ($parentObject !== NULL && $parentPropertyName) {
678 $parentColumnDataMap = $this->dataMapper->getDataMap(get_class($parentObject))->getColumnMap($parentPropertyName);
679 $relationTableMatchFields = $parentColumnDataMap->getRelationTableMatchFields();
680 if (is_array($relationTableMatchFields)) {
681 $row = array_merge($relationTableMatchFields, $row);
682 }
683 if ($parentColumnDataMap->getParentKeyFieldName() !== NULL) {
684 $row[$parentColumnDataMap->getParentKeyFieldName()] = (int)$parentObject->getUid();
685 }
686 }
687 $uid = $this->storageBackend->addRow($dataMap->getTableName(), $row);
688 $object->_setProperty('uid', (int)$uid);
689 $object->setPid((int)$row['pid']);
690 if ((int)$uid >= 1) {
691 $this->emitAfterInsertObjectSignal($object);
692 }
693 $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
694 if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
695 $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), $uid);
696 }
697 $this->session->registerObject($object, $uid);
698 if ((int)$uid >= 1) {
699 $this->emitEndInsertObjectSignal($object);
700 }
701 }
702
703 /**
704 * Emits a signal after an object was added to the storage
705 *
706 * @param DomainObjectInterface $object
707 */
708 protected function emitAfterInsertObjectSignal(DomainObjectInterface $object) {
709 $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterInsertObject', array($object));
710 }
711
712 /**
713 * Emits a signal after an object was registered in persistence session
714 * This signal replaces the afterInsertObject signal which is now deprecated
715 *
716 * @param DomainObjectInterface $object
717 */
718 protected function emitEndInsertObjectSignal(DomainObjectInterface $object) {
719 $this->signalSlotDispatcher->dispatch(__CLASS__, 'endInsertObject', array($object));
720 }
721
722 /**
723 * Tests, if the given Value Object already exists in the storage backend and if so, it returns the uid.
724 *
725 * @param \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject $object The object to be tested
726 * @return mixed The matching uid if an object was found, else FALSE
727 */
728 protected function getUidOfAlreadyPersistedValueObject(\TYPO3\CMS\Extbase\DomainObject\AbstractValueObject $object) {
729 return $this->storageBackend->getUidOfAlreadyPersistedValueObject($object);
730 }
731
732 /**
733 * Inserts mm-relation into a relation table
734 *
735 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The related object
736 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
737 * @param string $propertyName The name of the parent object's property where the related objects are stored in
738 * @param int $sortingPosition Defaults to NULL
739 * @return int The uid of the inserted row
740 */
741 protected function insertRelationInRelationtable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $propertyName, $sortingPosition = NULL) {
742 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
743 $columnMap = $dataMap->getColumnMap($propertyName);
744 $row = array(
745 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
746 $columnMap->getChildKeyFieldName() => (int)$object->getUid(),
747 $columnMap->getChildSortByFieldName() => !is_null($sortingPosition) ? (int)$sortingPosition : 0
748 );
749 $relationTableName = $columnMap->getRelationTableName();
750 if ($columnMap->getRelationTablePageIdColumnName() !== NULL) {
751 $row[$columnMap->getRelationTablePageIdColumnName()] = $this->determineStoragePageIdForNewRecord();
752 }
753 $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
754 if (is_array($relationTableMatchFields)) {
755 $row = array_merge($relationTableMatchFields, $row);
756 }
757 $relationTableInsertFields = $columnMap->getRelationTableInsertFields();
758 if (is_array($relationTableInsertFields)) {
759 $row = array_merge($relationTableInsertFields, $row);
760 }
761 $res = $this->storageBackend->addRow($relationTableName, $row, TRUE);
762 return $res;
763 }
764
765 /**
766 * Updates mm-relation in a relation table
767 *
768 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The related object
769 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
770 * @param string $propertyName The name of the parent object's property where the related objects are stored in
771 * @param int $sortingPosition Defaults to NULL
772 * @return bool TRUE if update was successfully
773 */
774 protected function updateRelationInRelationTable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $propertyName, $sortingPosition = 0) {
775 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
776 $columnMap = $dataMap->getColumnMap($propertyName);
777 $row = array(
778 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
779 $columnMap->getChildKeyFieldName() => (int)$object->getUid(),
780 $columnMap->getChildSortByFieldName() => (int)$sortingPosition
781 );
782 $relationTableName = $columnMap->getRelationTableName();
783 $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
784 if (is_array($relationTableMatchFields)) {
785 $row = array_merge($relationTableMatchFields, $row);
786 }
787 $res = $this->storageBackend->updateRelationTableRow(
788 $relationTableName,
789 $row);
790 return $res;
791 }
792
793 /**
794 * Delete all mm-relations of a parent from a relation table
795 *
796 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
797 * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
798 * @return bool TRUE if delete was successfully
799 */
800 protected function deleteAllRelationsFromRelationtable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName) {
801 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
802 $columnMap = $dataMap->getColumnMap($parentPropertyName);
803 $relationTableName = $columnMap->getRelationTableName();
804 $relationMatchFields = array(
805 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid()
806 );
807 $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
808 if (is_array($relationTableMatchFields)) {
809 $relationMatchFields = array_merge($relationTableMatchFields, $relationMatchFields);
810 }
811 $res = $this->storageBackend->removeRow($relationTableName, $relationMatchFields, FALSE);
812 return $res;
813 }
814
815 /**
816 * Delete an mm-relation from a relation table
817 *
818 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $relatedObject The related object
819 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
820 * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
821 * @return bool
822 */
823 protected function deleteRelationFromRelationtable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $relatedObject, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName) {
824 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
825 $columnMap = $dataMap->getColumnMap($parentPropertyName);
826 $relationTableName = $columnMap->getRelationTableName();
827 $relationMatchFields = array(
828 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
829 $columnMap->getChildKeyFieldName() => (int)$relatedObject->getUid()
830 );
831 $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
832 if (is_array($relationTableMatchFields)) {
833 $relationMatchFields = array_merge($relationTableMatchFields, $relationMatchFields);
834 }
835 $res = $this->storageBackend->removeRow($relationTableName, $relationMatchFields, FALSE);
836 return $res;
837 }
838
839 /**
840 * Fetches maximal value currently used for sorting field in parent table
841 *
842 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
843 * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
844 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException
845 * @return mixed the max value
846 */
847 protected function fetchMaxSortingFromParentTable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName) {
848 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
849 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
850 if ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
851 $tableName = $parentColumnMap->getChildTableName();
852 $sortByFieldName = $parentColumnMap->getChildSortByFieldName();
853
854 if (empty($sortByFieldName)) {
855 return FALSE;
856 }
857 $matchFields = array();
858 $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
859 if ($parentKeyFieldName !== NULL) {
860 $matchFields[$parentKeyFieldName] = $parentObject->getUid();
861 $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
862 if ($parentTableFieldName !== NULL) {
863 $matchFields[$parentTableFieldName] = $parentDataMap->getTableName();
864 }
865 }
866
867 if (empty($matchFields)) {
868 return FALSE;
869 }
870 } elseif ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
871 $tableName = $parentColumnMap->getRelationTableName();
872 $sortByFieldName = $parentColumnMap->getChildSortByFieldName();
873
874 $matchFields = array(
875 $parentColumnMap->getParentKeyFieldName() => (int)$parentObject->getUid()
876 );
877
878 $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
879 if (is_array($relationTableMatchFields)) {
880 $matchFields = array_merge($relationTableMatchFields, $matchFields);
881 }
882 } else {
883 throw new \TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException('Unexpected parent column relation type:' . $parentColumnMap->getTypeOfRelation(), 1345368106);
884 }
885
886 $result = $this->storageBackend->getMaxValueFromTable(
887 $tableName,
888 $matchFields,
889 $sortByFieldName);
890 return $result;
891 }
892
893 /**
894 * Updates a given object in the storage
895 *
896 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be updated
897 * @param array $row Row to be stored
898 * @return bool
899 */
900 protected function updateObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, array $row) {
901 $dataMap = $this->dataMapper->getDataMap(get_class($object));
902 $this->addCommonFieldsToRow($object, $row);
903 $row['uid'] = $object->getUid();
904 if ($dataMap->getLanguageIdColumnName() !== NULL) {
905 $row[$dataMap->getLanguageIdColumnName()] = (int)$object->_getProperty('_languageUid');
906 if ($object->_getProperty('_localizedUid') !== NULL) {
907 $row['uid'] = $object->_getProperty('_localizedUid');
908 }
909 }
910 $res = $this->storageBackend->updateRow($dataMap->getTableName(), $row);
911 if ($res === TRUE) {
912 $this->emitAfterUpdateObjectSignal($object);
913 }
914 $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
915 if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
916 $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), $row['uid']);
917 }
918 return $res;
919 }
920
921 /**
922 * Emits a signal after an object was updated in storage
923 *
924 * @param DomainObjectInterface $object
925 */
926 protected function emitAfterUpdateObjectSignal(DomainObjectInterface $object) {
927 $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterUpdateObject', array($object));
928 }
929
930 /**
931 * Emits a signal after an object was persisted
932 *
933 * @param DomainObjectInterface $object
934 */
935 protected function emitAfterPersistObjectSignal(DomainObjectInterface $object) {
936 $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterPersistObject', array($object));
937 }
938
939 /**
940 * Adds common databse fields to a row
941 *
942 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
943 * @param array &$row
944 * @return void
945 */
946 protected function addCommonFieldsToRow(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, array &$row) {
947 $dataMap = $this->dataMapper->getDataMap(get_class($object));
948 $this->addCommonDateFieldsToRow($object, $row);
949 if ($dataMap->getRecordTypeColumnName() !== NULL && $dataMap->getRecordType() !== NULL) {
950 $row[$dataMap->getRecordTypeColumnName()] = $dataMap->getRecordType();
951 }
952 if ($object->_isNew() && !isset($row['pid'])) {
953 $row['pid'] = $this->determineStoragePageIdForNewRecord($object);
954 }
955 }
956
957 /**
958 * Adjustes the common date fields of the given row to the current time
959 *
960 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
961 * @param array &$row The row to be updated
962 * @return void
963 */
964 protected function addCommonDateFieldsToRow(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, array &$row) {
965 $dataMap = $this->dataMapper->getDataMap(get_class($object));
966 if ($object->_isNew() && $dataMap->getCreationDateColumnName() !== NULL) {
967 $row[$dataMap->getCreationDateColumnName()] = $GLOBALS['EXEC_TIME'];
968 }
969 if ($dataMap->getModificationDateColumnName() !== NULL) {
970 $row[$dataMap->getModificationDateColumnName()] = $GLOBALS['EXEC_TIME'];
971 }
972 }
973
974 /**
975 * Iterate over deleted aggregate root objects and process them
976 *
977 * @return void
978 */
979 protected function processDeletedObjects() {
980 foreach ($this->deletedEntities as $entity) {
981 if ($this->session->hasObject($entity)) {
982 $this->removeEntity($entity);
983 $this->session->unregisterReconstitutedEntity($entity);
984 $this->session->unregisterObject($entity);
985 }
986 }
987 $this->deletedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
988 }
989
990 /**
991 * Deletes an object
992 *
993 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be removed from the storage
994 * @param bool $markAsDeleted Whether to just flag the row deleted (default) or really delete it
995 * @return void
996 */
997 protected function removeEntity(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, $markAsDeleted = TRUE) {
998 $dataMap = $this->dataMapper->getDataMap(get_class($object));
999 $tableName = $dataMap->getTableName();
1000 if ($markAsDeleted === TRUE && $dataMap->getDeletedFlagColumnName() !== NULL) {
1001 $deletedColumnName = $dataMap->getDeletedFlagColumnName();
1002 $row = array(
1003 'uid' => $object->getUid(),
1004 $deletedColumnName => 1
1005 );
1006 $this->addCommonDateFieldsToRow($object, $row);
1007 $res = $this->storageBackend->updateRow($tableName, $row);
1008 } else {
1009 $res = $this->storageBackend->removeRow($tableName, array('uid' => $object->getUid()));
1010 }
1011 if ($res === TRUE) {
1012 $this->emitAfterRemoveObjectSignal($object);
1013 }
1014 $this->removeRelatedObjects($object);
1015 $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
1016 if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
1017 $this->referenceIndex->updateRefIndexTable($tableName, $object->getUid());
1018 }
1019 }
1020
1021 /**
1022 * Emits a signal after an object was removed from storage
1023 *
1024 * @param DomainObjectInterface $object
1025 */
1026 protected function emitAfterRemoveObjectSignal(DomainObjectInterface $object) {
1027 $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterRemoveObject', array($object));
1028 }
1029
1030 /**
1031 * Remove related objects
1032 *
1033 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to scanned for related objects
1034 * @return void
1035 */
1036 protected function removeRelatedObjects(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object) {
1037 $className = get_class($object);
1038 $dataMap = $this->dataMapper->getDataMap($className);
1039 $classSchema = $this->reflectionService->getClassSchema($className);
1040 $properties = $object->_getProperties();
1041 foreach ($properties as $propertyName => $propertyValue) {
1042 $columnMap = $dataMap->getColumnMap($propertyName);
1043 $propertyMetaData = $classSchema->getProperty($propertyName);
1044 if ($propertyMetaData['cascade'] === 'remove') {
1045 if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
1046 foreach ($propertyValue as $containedObject) {
1047 $this->removeEntity($containedObject);
1048 }
1049 } elseif ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) {
1050 $this->removeEntity($propertyValue);
1051 }
1052 }
1053 }
1054 }
1055
1056 /**
1057 * Determine the storage page ID for a given NEW record
1058 *
1059 * This does the following:
1060 * - If the domain object has an accessible property 'pid' (i.e. through a getPid() method), that is used to store the record.
1061 * - If there is a TypoScript configuration "classes.CLASSNAME.newRecordStoragePid", that is used to store new records.
1062 * - If there is no such TypoScript configuration, it uses the first value of The "storagePid" taken for reading records.
1063 *
1064 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
1065 * @return int the storage Page ID where the object should be stored
1066 */
1067 protected function determineStoragePageIdForNewRecord(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object = NULL) {
1068 $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
1069 if ($object !== NULL) {
1070 if (\TYPO3\CMS\Extbase\Reflection\ObjectAccess::isPropertyGettable($object, 'pid')) {
1071 $pid = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getProperty($object, 'pid');
1072 if (isset($pid)) {
1073 return (int)$pid;
1074 }
1075 }
1076 $className = get_class($object);
1077 if (isset($frameworkConfiguration['persistence']['classes'][$className]) && !empty($frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'])) {
1078 return (int)$frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'];
1079 }
1080 }
1081 $storagePidList = \TYPO3\CMS\Core\Utility\GeneralUtility::intExplode(',', $frameworkConfiguration['persistence']['storagePid']);
1082 return (int)$storagePidList[0];
1083 }
1084
1085 }