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