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