Passed
Push — master ( e47dd8...3ef27a )
by
unknown
13:30
created

Backend::getReflectionService()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Extbase\Persistence\Generic;
17
18
use Psr\EventDispatcher\EventDispatcherInterface;
19
use TYPO3\CMS\Core\Database\ReferenceIndex;
20
use TYPO3\CMS\Core\SingletonInterface;
21
use TYPO3\CMS\Core\Utility\GeneralUtility;
22
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
23
use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
24
use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
25
use TYPO3\CMS\Extbase\Event\Persistence\EntityAddedToPersistenceEvent;
26
use TYPO3\CMS\Extbase\Event\Persistence\EntityFinalizedAfterPersistenceEvent;
27
use TYPO3\CMS\Extbase\Event\Persistence\EntityPersistedEvent;
28
use TYPO3\CMS\Extbase\Event\Persistence\EntityRemovedFromPersistenceEvent;
29
use TYPO3\CMS\Extbase\Event\Persistence\EntityUpdatedInPersistenceEvent;
30
use TYPO3\CMS\Extbase\Event\Persistence\ModifyQueryBeforeFetchingObjectDataEvent;
31
use TYPO3\CMS\Extbase\Event\Persistence\ModifyResultAfterFetchingObjectDataEvent;
32
use TYPO3\CMS\Extbase\Object\ObjectManager;
33
use TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException;
34
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap;
35
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory;
36
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
37
use TYPO3\CMS\Extbase\Persistence\ObjectMonitoringInterface;
38
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
39
use TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface;
40
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
41
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
42
use TYPO3\CMS\Extbase\Reflection\ReflectionService;
43
44
/**
45
 * A persistence backend. This backend maps objects to the relational model of the storage backend.
46
 * It persists all added, removed and changed objects.
47
 * @internal only to be used within Extbase, not part of TYPO3 Core API.
48
 */
49
class Backend implements BackendInterface, SingletonInterface
50
{
51
    /**
52
     * @var \TYPO3\CMS\Extbase\Persistence\Generic\Session
53
     */
54
    protected $session;
55
56
    /**
57
     * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
58
     */
59
    protected $persistenceManager;
60
61
    /**
62
     * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
63
     */
64
    protected $aggregateRootObjects;
65
66
    /**
67
     * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
68
     */
69
    protected $deletedEntities;
70
71
    /**
72
     * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
73
     */
74
    protected $changedEntities;
75
76
    /**
77
     * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
78
     */
79
    protected $visitedDuringPersistence;
80
81
    /**
82
     * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
83
     */
84
    protected $reflectionService;
85
86
    /**
87
     * @var \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface
88
     */
89
    protected $storageBackend;
90
91
    /**
92
     * @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory
93
     */
94
    protected $dataMapFactory;
95
96
    /**
97
     * The TYPO3 reference index object
98
     *
99
     * @var \TYPO3\CMS\Core\Database\ReferenceIndex
100
     */
101
    protected $referenceIndex;
102
103
    /**
104
     * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
105
     */
106
    protected $configurationManager;
107
108
    /**
109
     * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
110
     */
111
    protected $signalSlotDispatcher;
112
113
    /**
114
     * @var EventDispatcherInterface
115
     */
116
    protected $eventDispatcher;
117
118
    /**
119
     * Constructs the backend
120
     *
121
     * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
122
     * @param Session $session
123
     * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
124
     * @param \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface $storageBackend
125
     * @param \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory $dataMapFactory
126
     * @param EventDispatcherInterface $eventDispatcher
127
     */
128
    public function __construct(
129
        ConfigurationManagerInterface $configurationManager,
130
        Session $session,
131
        ReflectionService $reflectionService,
132
        \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface $storageBackend,
133
        DataMapFactory $dataMapFactory,
134
        EventDispatcherInterface $eventDispatcher
135
    ) {
136
        $this->configurationManager = $configurationManager;
137
        $this->session = $session;
138
        $this->reflectionService = $reflectionService;
139
        $this->storageBackend = $storageBackend;
140
        $this->dataMapFactory = $dataMapFactory;
141
        $this->eventDispatcher = $eventDispatcher;
142
143
        $this->referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class);
144
        $this->aggregateRootObjects = new ObjectStorage();
145
        $this->deletedEntities = new ObjectStorage();
146
        $this->changedEntities = new ObjectStorage();
147
    }
148
149
    /**
150
     * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
151
     */
152
    public function setPersistenceManager(PersistenceManagerInterface $persistenceManager)
153
    {
154
        $this->persistenceManager = $persistenceManager;
155
    }
156
157
    /**
158
     * Returns the number of records matching the query.
159
     *
160
     * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
161
     * @return int
162
     */
163
    public function getObjectCountByQuery(QueryInterface $query)
164
    {
165
        return $this->storageBackend->getObjectCountByQuery($query);
166
    }
167
168
    /**
169
     * Returns the object data matching the $query.
170
     *
171
     * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
172
     * @return array
173
     */
174
    public function getObjectDataByQuery(QueryInterface $query)
175
    {
176
        $event = new ModifyQueryBeforeFetchingObjectDataEvent($query);
177
        $this->eventDispatcher->dispatch($event);
178
        $query = $event->getQuery();
179
        $result = $this->storageBackend->getObjectDataByQuery($query);
180
        $event = new ModifyResultAfterFetchingObjectDataEvent($query, $result);
181
        $this->eventDispatcher->dispatch($event);
182
        return $event->getResult();
183
    }
184
185
    /**
186
     * Returns the (internal) identifier for the object, if it is known to the
187
     * backend. Otherwise NULL is returned.
188
     *
189
     * @param object $object
190
     * @return string|null The identifier for the object if it is known, or NULL
191
     */
192
    public function getIdentifierByObject($object)
193
    {
194
        if ($object instanceof LazyLoadingProxy) {
195
            $object = $object->_loadRealInstance();
196
            if (!is_object($object)) {
197
                return null;
198
            }
199
        }
200
        return $this->session->getIdentifierByObject($object);
201
    }
202
203
    /**
204
     * Returns the object with the (internal) identifier, if it is known to the
205
     * backend. Otherwise NULL is returned.
206
     *
207
     * @param string $identifier
208
     * @param string $className
209
     * @return object|null The object for the identifier if it is known, or NULL
210
     */
211
    public function getObjectByIdentifier($identifier, $className)
212
    {
213
        if ($this->session->hasIdentifier($identifier, $className)) {
214
            return $this->session->getObjectByIdentifier($identifier, $className);
215
        }
216
        $query = $this->persistenceManager->createQueryForType($className);
217
        $query->getQuerySettings()->setRespectStoragePage(false);
218
        $query->getQuerySettings()->setRespectSysLanguage(false);
219
        $query->getQuerySettings()->setLanguageOverlayMode(true);
220
        return $query->matching($query->equals('uid', $identifier))->execute()->getFirst();
221
    }
222
223
    /**
224
     * Checks if the given object has ever been persisted.
225
     *
226
     * @param object $object The object to check
227
     * @return bool TRUE if the object is new, FALSE if the object exists in the repository
228
     */
229
    public function isNewObject($object)
230
    {
231
        return $this->getIdentifierByObject($object) === null;
232
    }
233
234
    /**
235
     * Sets the aggregate root objects
236
     *
237
     * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects
238
     */
239
    public function setAggregateRootObjects(ObjectStorage $objects)
240
    {
241
        $this->aggregateRootObjects = $objects;
242
    }
243
244
    /**
245
     * Sets the changed objects
246
     *
247
     * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities
248
     */
249
    public function setChangedEntities(ObjectStorage $entities)
250
    {
251
        $this->changedEntities = $entities;
252
    }
253
254
    /**
255
     * Sets the deleted objects
256
     *
257
     * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities
258
     */
259
    public function setDeletedEntities(ObjectStorage $entities)
260
    {
261
        $this->deletedEntities = $entities;
262
    }
263
264
    /**
265
     * Commits the current persistence session.
266
     */
267
    public function commit()
268
    {
269
        $this->persistObjects();
270
        $this->processDeletedObjects();
271
    }
272
273
    /**
274
     * Traverse and persist all aggregate roots and their object graph.
275
     */
276
    protected function persistObjects()
277
    {
278
        $this->visitedDuringPersistence = new ObjectStorage();
279
        foreach ($this->aggregateRootObjects as $object) {
280
            /** @var DomainObjectInterface $object */
281
            if ($object->_isNew()) {
282
                $this->insertObject($object);
283
            }
284
            $this->persistObject($object);
285
        }
286
        foreach ($this->changedEntities as $object) {
287
            $this->persistObject($object);
288
        }
289
    }
290
291
    /**
292
     * Persists the given object.
293
     *
294
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be inserted
295
     */
296
    protected function persistObject(DomainObjectInterface $object)
297
    {
298
        if (isset($this->visitedDuringPersistence[$object])) {
299
            return;
300
        }
301
        $row = [];
302
        $queue = [];
303
        $dataMap = $this->dataMapFactory->buildDataMap(get_class($object));
304
        $properties = $object->_getProperties();
305
        foreach ($properties as $propertyName => $propertyValue) {
306
            if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) {
307
                continue;
308
            }
309
            $columnMap = $dataMap->getColumnMap($propertyName);
310
            if ($propertyValue instanceof ObjectStorage) {
311
                $cleanProperty = $object->_getCleanProperty($propertyName);
312
                // objectstorage needs to be persisted if the object is new, the objectstorage is dirty, meaning it has
313
                // been changed after initial build, or an empty objectstorage is present and the cleanstate objectstorage
314
                // has childelements, meaning all elements should been removed from the objectstorage
315
                if ($object->_isNew() || $propertyValue->_isDirty() || ($propertyValue->count() === 0 && $cleanProperty && $cleanProperty->count() > 0)) {
316
                    $this->persistObjectStorage($propertyValue, $object, $propertyName, $row);
317
                    $propertyValue->_memorizeCleanState();
318
                }
319
                foreach ($propertyValue as $containedObject) {
320
                    if ($containedObject instanceof DomainObjectInterface) {
321
                        $queue[] = $containedObject;
322
                    }
323
                }
324
            } elseif ($propertyValue instanceof DomainObjectInterface
325
                && $object instanceof ObjectMonitoringInterface) {
326
                if ($object->_isDirty($propertyName)) {
0 ignored issues
show
Unused Code introduced by
The call to TYPO3\CMS\Extbase\Persis...ngInterface::_isDirty() has too many arguments starting with $propertyName. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

326
                if ($object->/** @scrutinizer ignore-call */ _isDirty($propertyName)) {

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
327
                    if ($propertyValue->_isNew()) {
328
                        $this->insertObject($propertyValue, $object, $propertyName);
329
                    }
330
                    $row[$columnMap->getColumnName()] = $this->getPlainValue($propertyValue);
331
                }
332
                $queue[] = $propertyValue;
333
            } elseif ($object->_isNew() || $object->_isDirty($propertyName)) {
0 ignored issues
show
Bug introduced by
The method _isDirty() does not exist on TYPO3\CMS\Extbase\Domain...t\DomainObjectInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to TYPO3\CMS\Extbase\Domain...t\DomainObjectInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

333
            } elseif ($object->_isNew() || $object->/** @scrutinizer ignore-call */ _isDirty($propertyName)) {
Loading history...
334
                $row[$columnMap->getColumnName()] = $this->getPlainValue($propertyValue, $columnMap);
335
            }
336
        }
337
        if (!empty($row)) {
338
            $this->updateObject($object, $row);
339
            $object->_memorizeCleanState();
0 ignored issues
show
Bug introduced by
The method _memorizeCleanState() does not exist on TYPO3\CMS\Extbase\Domain...t\DomainObjectInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to TYPO3\CMS\Extbase\Domain...t\DomainObjectInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

339
            $object->/** @scrutinizer ignore-call */ 
340
                     _memorizeCleanState();
Loading history...
340
        }
341
        $this->visitedDuringPersistence[$object] = $object->getUid();
342
        foreach ($queue as $queuedObject) {
343
            $this->persistObject($queuedObject);
344
        }
345
        $this->eventDispatcher->dispatch(new EntityPersistedEvent($object));
346
    }
347
348
    /**
349
     * Checks, if the property value is lazy loaded and was not initialized
350
     *
351
     * @param mixed $propertyValue The property value
352
     * @return bool
353
     */
354
    protected function propertyValueIsLazyLoaded($propertyValue)
355
    {
356
        if ($propertyValue instanceof LazyLoadingProxy) {
357
            return true;
358
        }
359
        if ($propertyValue instanceof LazyObjectStorage) {
360
            if ($propertyValue->isInitialized() === false) {
361
                return true;
362
            }
363
        }
364
        return false;
365
    }
366
367
    /**
368
     * Persists an object storage. Objects of a 1:n or m:n relation are queued and processed with the parent object.
369
     * A 1:1 relation gets persisted immediately. Objects which were removed from the property were detached from
370
     * the parent object. They will not be deleted by default. You have to annotate the property
371
     * with '@TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove")' if you want them to be deleted as well.
372
     *
373
     * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $objectStorage The object storage to be persisted.
374
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object. One of the properties holds the object storage.
375
     * @param string $propertyName The name of the property holding the object storage.
376
     * @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.
377
     */
378
    protected function persistObjectStorage(ObjectStorage $objectStorage, DomainObjectInterface $parentObject, $propertyName, array &$row)
379
    {
380
        $className = get_class($parentObject);
381
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
382
        $dataMapper = $objectManager->get(DataMapper::class);
383
        $columnMap = $this->dataMapFactory->buildDataMap($className)->getColumnMap($propertyName);
384
        $property = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
385
        foreach ($this->getRemovedChildObjects($parentObject, $propertyName) as $removedObject) {
386
            $this->detachObjectFromParentObject($removedObject, $parentObject, $propertyName);
387
            if ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_MANY && $property->getCascadeValue() === 'remove') {
388
                $this->removeEntity($removedObject);
389
            }
390
        }
391
392
        $currentUids = [];
393
        $sortingPosition = 1;
394
        $updateSortingOfFollowing = false;
395
396
        foreach ($objectStorage as $object) {
397
            /** @var DomainObjectInterface $object */
398
            if (empty($currentUids)) {
399
                $sortingPosition = 1;
400
            } else {
401
                $sortingPosition++;
402
            }
403
            $cleanProperty = $parentObject->_getCleanProperty($propertyName);
404
            if ($object->_isNew()) {
405
                $this->insertObject($object);
406
                $this->attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
407
                // if a new object is inserted, all objects after this need to have their sorting updated
408
                $updateSortingOfFollowing = true;
409
            } elseif ($cleanProperty === null || $cleanProperty->getPosition($object) === null) {
410
                // if parent object is new then it doesn't have cleanProperty yet; before attaching object it's clean position is null
411
                $this->attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
412
                // 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
413
                $updateSortingOfFollowing = true;
414
            } elseif ($objectStorage->isRelationDirty($object) || $cleanProperty->getPosition($object) !== $objectStorage->getPosition($object)) {
415
                $this->updateRelationOfObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
416
                $updateSortingOfFollowing = true;
417
            } elseif ($updateSortingOfFollowing) {
418
                if ($sortingPosition > $objectStorage->getPosition($object)) {
419
                    $this->updateRelationOfObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
420
                } else {
421
                    $sortingPosition = $objectStorage->getPosition($object);
422
                }
423
            }
424
            $currentUids[] = $object->getUid();
425
        }
426
427
        if ($columnMap->getParentKeyFieldName() === null) {
428
            $row[$columnMap->getColumnName()] = implode(',', $currentUids);
429
        } else {
430
            $row[$columnMap->getColumnName()] = $dataMapper->countRelated($parentObject, $propertyName);
431
        }
432
    }
433
434
    /**
435
     * Returns the removed objects determined by a comparison of the clean property value
436
     * with the actual property value.
437
     *
438
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object
439
     * @param string $propertyName
440
     * @return array An array of removed objects
441
     */
442
    protected function getRemovedChildObjects(DomainObjectInterface $object, $propertyName)
443
    {
444
        $removedObjects = [];
445
        $cleanPropertyValue = $object->_getCleanProperty($propertyName);
446
        if (is_array($cleanPropertyValue) || $cleanPropertyValue instanceof \Iterator) {
447
            $propertyValue = $object->_getProperty($propertyName);
448
            foreach ($cleanPropertyValue as $containedObject) {
449
                if (!$propertyValue->contains($containedObject)) {
450
                    $removedObjects[] = $containedObject;
451
                }
452
            }
453
        }
454
        return $removedObjects;
455
    }
456
457
    /**
458
     * Updates the fields defining the relation between the object and the parent object.
459
     *
460
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
461
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject
462
     * @param string $parentPropertyName
463
     * @param int $sortingPosition
464
     */
465
    protected function attachObjectToParentObject(DomainObjectInterface $object, DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition = 0)
466
    {
467
        $parentDataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
468
469
        $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
470
        if ($parentColumnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_MANY) {
471
            $this->attachObjectToParentObjectRelationHasMany($object, $parentObject, $parentPropertyName, $sortingPosition);
472
        } elseif ($parentColumnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
473
            $this->insertRelationInRelationtable($object, $parentObject, $parentPropertyName, $sortingPosition);
474
        }
475
    }
476
477
    /**
478
     * Updates the fields defining the relation between the object and the parent object.
479
     *
480
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
481
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject
482
     * @param string $parentPropertyName
483
     * @param int $sortingPosition
484
     */
485
    protected function updateRelationOfObjectToParentObject(DomainObjectInterface $object, DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition = 0)
486
    {
487
        $parentDataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
488
        $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
489
        if ($parentColumnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_MANY) {
490
            $this->attachObjectToParentObjectRelationHasMany($object, $parentObject, $parentPropertyName, $sortingPosition);
491
        } elseif ($parentColumnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
492
            $this->updateRelationInRelationTable($object, $parentObject, $parentPropertyName, $sortingPosition);
493
        }
494
    }
495
496
    /**
497
     * Updates fields defining the relation between the object and the parent object in relation has-many.
498
     *
499
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
500
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject
501
     * @param string $parentPropertyName
502
     * @param int $sortingPosition
503
     * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException
504
     */
505
    protected function attachObjectToParentObjectRelationHasMany(DomainObjectInterface $object, DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition = 0)
506
    {
507
        $parentDataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
508
        $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
509
        if ($parentColumnMap->getTypeOfRelation() !== ColumnMap::RELATION_HAS_MANY) {
510
            throw new IllegalRelationTypeException(
511
                'Parent column relation type is ' . $parentColumnMap->getTypeOfRelation() .
512
                ' but should be ' . ColumnMap::RELATION_HAS_MANY,
513
                1345368105
514
            );
515
        }
516
        $row = [];
517
        $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
518
        if ($parentKeyFieldName !== null) {
519
            $row[$parentKeyFieldName] = $parentObject->_getProperty('_localizedUid') ?: $parentObject->getUid();
520
            $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
521
            if ($parentTableFieldName !== null) {
522
                $row[$parentTableFieldName] = $parentDataMap->getTableName();
523
            }
524
            $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
525
            if (is_array($relationTableMatchFields)) {
526
                $row = array_merge($relationTableMatchFields, $row);
527
            }
528
        }
529
        $childSortByFieldName = $parentColumnMap->getChildSortByFieldName();
530
        if (!empty($childSortByFieldName)) {
531
            $row[$childSortByFieldName] = $sortingPosition;
532
        }
533
        if (!empty($row)) {
534
            $this->updateObject($object, $row);
535
        }
536
    }
537
538
    /**
539
     * Updates the fields defining the relation between the object and the parent object.
540
     *
541
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
542
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject
543
     * @param string $parentPropertyName
544
     */
545
    protected function detachObjectFromParentObject(DomainObjectInterface $object, DomainObjectInterface $parentObject, $parentPropertyName)
546
    {
547
        $parentDataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
548
        $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
549
        if ($parentColumnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_MANY) {
550
            $row = [];
551
            $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
552
            if ($parentKeyFieldName !== null) {
553
                $row[$parentKeyFieldName] = 0;
554
                $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
555
                if ($parentTableFieldName !== null) {
556
                    $row[$parentTableFieldName] = '';
557
                }
558
                $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
559
                if (is_array($relationTableMatchFields) && !empty($relationTableMatchFields)) {
560
                    $row = array_merge(array_fill_keys(array_keys($relationTableMatchFields), ''), $row);
561
                }
562
            }
563
            $childSortByFieldName = $parentColumnMap->getChildSortByFieldName();
564
            if (!empty($childSortByFieldName)) {
565
                $row[$childSortByFieldName] = 0;
566
            }
567
            if (!empty($row)) {
568
                $this->updateObject($object, $row);
569
            }
570
        } elseif ($parentColumnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
571
            $this->deleteRelationFromRelationtable($object, $parentObject, $parentPropertyName);
572
        }
573
    }
574
575
    /**
576
     * Inserts an object in the storage backend
577
     *
578
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be inserted in the storage
579
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parentobject.
580
     * @param string $parentPropertyName
581
     */
582
    protected function insertObject(DomainObjectInterface $object, DomainObjectInterface $parentObject = null, $parentPropertyName = '')
583
    {
584
        if ($object instanceof AbstractValueObject) {
585
            $result = $this->getUidOfAlreadyPersistedValueObject($object);
586
            if ($result !== null) {
587
                $object->_setProperty('uid', $result);
588
                return;
589
            }
590
        }
591
        $dataMap = $this->dataMapFactory->buildDataMap(get_class($object));
592
        $row = [];
593
        $properties = $object->_getProperties();
594
        foreach ($properties as $propertyName => $propertyValue) {
595
            if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) {
596
                continue;
597
            }
598
            $columnMap = $dataMap->getColumnMap($propertyName);
599
            if ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_ONE) {
600
                $row[$columnMap->getColumnName()] = 0;
601
            } elseif ($columnMap->getTypeOfRelation() !== ColumnMap::RELATION_NONE) {
602
                if ($columnMap->getParentKeyFieldName() === null) {
603
                    // CSV type relation
604
                    $row[$columnMap->getColumnName()] = '';
605
                } else {
606
                    // MM type relation
607
                    $row[$columnMap->getColumnName()] = 0;
608
                }
609
            } elseif ($propertyValue !== null) {
610
                $row[$columnMap->getColumnName()] = $this->getPlainValue($propertyValue, $columnMap);
611
            }
612
        }
613
        $this->addCommonFieldsToRow($object, $row);
614
        if ($dataMap->getLanguageIdColumnName() !== null && $object->_getProperty('_languageUid') === null) {
615
            $row[$dataMap->getLanguageIdColumnName()] = 0;
616
            $object->_setProperty('_languageUid', 0);
617
        }
618
        if ($dataMap->getTranslationOriginColumnName() !== null) {
0 ignored issues
show
introduced by
The condition $dataMap->getTranslation...inColumnName() !== null is always true.
Loading history...
619
            $row[$dataMap->getTranslationOriginColumnName()] = 0;
620
        }
621
        if ($dataMap->getTranslationOriginDiffSourceName() !== null) {
0 ignored issues
show
introduced by
The condition $dataMap->getTranslation...ffSourceName() !== null is always true.
Loading history...
622
            $row[$dataMap->getTranslationOriginDiffSourceName()] = '';
623
        }
624
        if ($parentObject !== null && $parentPropertyName) {
625
            $parentColumnDataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject))->getColumnMap($parentPropertyName);
626
            $relationTableMatchFields = $parentColumnDataMap->getRelationTableMatchFields();
627
            if (is_array($relationTableMatchFields)) {
628
                $row = array_merge($relationTableMatchFields, $row);
629
            }
630
            if ($parentColumnDataMap->getParentKeyFieldName() !== null) {
631
                $row[$parentColumnDataMap->getParentKeyFieldName()] = (int)$parentObject->getUid();
632
            }
633
        }
634
        $uid = $this->storageBackend->addRow($dataMap->getTableName(), $row);
635
        $object->_setProperty('uid', (int)$uid);
636
        $object->setPid((int)$row['pid']);
637
        if ((int)$uid >= 1) {
638
            $this->eventDispatcher->dispatch(new EntityAddedToPersistenceEvent($object));
639
        }
640
        $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
641
        if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
642
            $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), $uid);
643
        }
644
        $this->session->registerObject($object, $uid);
645
        if ((int)$uid >= 1) {
646
            $this->eventDispatcher->dispatch(new EntityFinalizedAfterPersistenceEvent($object));
647
        }
648
    }
649
650
    /**
651
     * Tests, if the given Value Object already exists in the storage backend and if so, it returns the uid.
652
     *
653
     * @param \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject $object The object to be tested
654
     * @return int|null The matching uid if an object was found, else null
655
     */
656
    protected function getUidOfAlreadyPersistedValueObject(AbstractValueObject $object)
657
    {
658
        return $this->storageBackend->getUidOfAlreadyPersistedValueObject($object);
659
    }
660
661
    /**
662
     * Inserts mm-relation into a relation table
663
     *
664
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The related object
665
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
666
     * @param string $propertyName The name of the parent object's property where the related objects are stored in
667
     * @param int $sortingPosition Defaults to NULL
668
     * @return int The uid of the inserted row
669
     */
670
    protected function insertRelationInRelationtable(DomainObjectInterface $object, DomainObjectInterface $parentObject, $propertyName, $sortingPosition = null)
671
    {
672
        $dataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
673
        $columnMap = $dataMap->getColumnMap($propertyName);
674
        $parentUid = $parentObject->getUid();
675
        if ($parentObject->_getProperty('_localizedUid') !== null) {
676
            $parentUid = $parentObject->_getProperty('_localizedUid');
677
        }
678
        $row = [
679
            $columnMap->getParentKeyFieldName() => (int)$parentUid,
680
            $columnMap->getChildKeyFieldName() => (int)$object->getUid(),
681
            $columnMap->getChildSortByFieldName() => $sortingPosition !== null ? (int)$sortingPosition : 0
682
        ];
683
        $relationTableName = $columnMap->getRelationTableName();
684
        if ($columnMap->getRelationTablePageIdColumnName() !== null) {
685
            $row[$columnMap->getRelationTablePageIdColumnName()] = $this->determineStoragePageIdForNewRecord();
686
        }
687
        $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
688
        if (is_array($relationTableMatchFields)) {
689
            $row = array_merge($relationTableMatchFields, $row);
690
        }
691
        $relationTableInsertFields = $columnMap->getRelationTableInsertFields();
692
        if (is_array($relationTableInsertFields)) {
693
            $row = array_merge($relationTableInsertFields, $row);
694
        }
695
        $res = $this->storageBackend->addRow($relationTableName, $row, true);
0 ignored issues
show
Bug introduced by
It seems like $relationTableName can also be of type null; however, parameter $tableName of TYPO3\CMS\Extbase\Persis...kendInterface::addRow() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

695
        $res = $this->storageBackend->addRow(/** @scrutinizer ignore-type */ $relationTableName, $row, true);
Loading history...
696
        return $res;
697
    }
698
699
    /**
700
     * Updates mm-relation in a relation table
701
     *
702
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The related object
703
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
704
     * @param string $propertyName The name of the parent object's property where the related objects are stored in
705
     * @param int $sortingPosition Defaults to NULL
706
     * @return bool TRUE if update was successfully
707
     */
708
    protected function updateRelationInRelationTable(DomainObjectInterface $object, DomainObjectInterface $parentObject, $propertyName, $sortingPosition = 0)
709
    {
710
        $dataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
711
        $columnMap = $dataMap->getColumnMap($propertyName);
712
        $row = [
713
            $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
714
            $columnMap->getChildKeyFieldName() => (int)$object->getUid(),
715
            $columnMap->getChildSortByFieldName() => (int)$sortingPosition
716
        ];
717
        $relationTableName = $columnMap->getRelationTableName();
718
        $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
719
        if (is_array($relationTableMatchFields)) {
720
            $row = array_merge($relationTableMatchFields, $row);
721
        }
722
        $this->storageBackend->updateRelationTableRow(
723
            $relationTableName,
0 ignored issues
show
Bug introduced by
It seems like $relationTableName can also be of type null; however, parameter $tableName of TYPO3\CMS\Extbase\Persis...pdateRelationTableRow() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

723
            /** @scrutinizer ignore-type */ $relationTableName,
Loading history...
724
            $row
725
        );
726
        return true;
727
    }
728
729
    /**
730
     * Delete all mm-relations of a parent from a relation table
731
     *
732
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
733
     * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
734
     * @return bool TRUE if delete was successfully
735
     */
736
    protected function deleteAllRelationsFromRelationtable(DomainObjectInterface $parentObject, $parentPropertyName)
737
    {
738
        $dataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
739
        $columnMap = $dataMap->getColumnMap($parentPropertyName);
740
        $relationTableName = $columnMap->getRelationTableName();
741
        $relationMatchFields = [
742
            $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid()
743
        ];
744
        $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
745
        if (is_array($relationTableMatchFields)) {
746
            $relationMatchFields = array_merge($relationTableMatchFields, $relationMatchFields);
747
        }
748
        $this->storageBackend->removeRow($relationTableName, $relationMatchFields, false);
0 ignored issues
show
Bug introduced by
It seems like $relationTableName can also be of type null; however, parameter $tableName of TYPO3\CMS\Extbase\Persis...dInterface::removeRow() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

748
        $this->storageBackend->removeRow(/** @scrutinizer ignore-type */ $relationTableName, $relationMatchFields, false);
Loading history...
749
        return true;
750
    }
751
752
    /**
753
     * Delete an mm-relation from a relation table
754
     *
755
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $relatedObject The related object
756
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
757
     * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
758
     * @return bool
759
     */
760
    protected function deleteRelationFromRelationtable(DomainObjectInterface $relatedObject, DomainObjectInterface $parentObject, $parentPropertyName)
761
    {
762
        $dataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
763
        $columnMap = $dataMap->getColumnMap($parentPropertyName);
764
        $relationTableName = $columnMap->getRelationTableName();
765
        $relationMatchFields = [
766
            $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
767
            $columnMap->getChildKeyFieldName() => (int)$relatedObject->getUid()
768
        ];
769
        $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
770
        if (is_array($relationTableMatchFields)) {
771
            $relationMatchFields = array_merge($relationTableMatchFields, $relationMatchFields);
772
        }
773
        $this->storageBackend->removeRow($relationTableName, $relationMatchFields, false);
0 ignored issues
show
Bug introduced by
It seems like $relationTableName can also be of type null; however, parameter $tableName of TYPO3\CMS\Extbase\Persis...dInterface::removeRow() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

773
        $this->storageBackend->removeRow(/** @scrutinizer ignore-type */ $relationTableName, $relationMatchFields, false);
Loading history...
774
        return true;
775
    }
776
777
    /**
778
     * Updates a given object in the storage
779
     *
780
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be updated
781
     * @param array $row Row to be stored
782
     * @return bool
783
     */
784
    protected function updateObject(DomainObjectInterface $object, array $row)
785
    {
786
        $dataMap = $this->dataMapFactory->buildDataMap(get_class($object));
787
        $this->addCommonFieldsToRow($object, $row);
788
        $row['uid'] = $object->getUid();
789
        if ($dataMap->getLanguageIdColumnName() !== null) {
0 ignored issues
show
introduced by
The condition $dataMap->getLanguageIdColumnName() !== null is always true.
Loading history...
790
            $row[$dataMap->getLanguageIdColumnName()] = (int)$object->_getProperty('_languageUid');
791
            if ($object->_getProperty('_localizedUid') !== null) {
792
                $row['uid'] = $object->_getProperty('_localizedUid');
793
            }
794
        }
795
        $this->storageBackend->updateRow($dataMap->getTableName(), $row);
796
        $this->eventDispatcher->dispatch(new EntityUpdatedInPersistenceEvent($object));
797
798
        $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
799
        if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
800
            $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), $row['uid']);
801
        }
802
        return true;
803
    }
804
805
    /**
806
     * Adds common database fields to a row
807
     *
808
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
809
     * @param array $row
810
     */
811
    protected function addCommonFieldsToRow(DomainObjectInterface $object, array &$row)
812
    {
813
        $dataMap = $this->dataMapFactory->buildDataMap(get_class($object));
814
        $this->addCommonDateFieldsToRow($object, $row);
815
        if ($dataMap->getRecordTypeColumnName() !== null && $dataMap->getRecordType() !== null) {
816
            $row[$dataMap->getRecordTypeColumnName()] = $dataMap->getRecordType();
817
        }
818
        if ($object->_isNew() && !isset($row['pid'])) {
819
            $row['pid'] = $this->determineStoragePageIdForNewRecord($object);
820
        }
821
    }
822
823
    /**
824
     * Adjusts the common date fields of the given row to the current time
825
     *
826
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
827
     * @param array $row The row to be updated
828
     */
829
    protected function addCommonDateFieldsToRow(DomainObjectInterface $object, array &$row)
830
    {
831
        $dataMap = $this->dataMapFactory->buildDataMap(get_class($object));
832
        if ($object->_isNew() && $dataMap->getCreationDateColumnName() !== null) {
833
            $row[$dataMap->getCreationDateColumnName()] = $GLOBALS['EXEC_TIME'];
834
        }
835
        if ($dataMap->getModificationDateColumnName() !== null) {
0 ignored issues
show
introduced by
The condition $dataMap->getModificatio...teColumnName() !== null is always true.
Loading history...
836
            $row[$dataMap->getModificationDateColumnName()] = $GLOBALS['EXEC_TIME'];
837
        }
838
    }
839
840
    /**
841
     * Iterate over deleted aggregate root objects and process them
842
     */
843
    protected function processDeletedObjects()
844
    {
845
        foreach ($this->deletedEntities as $entity) {
846
            if ($this->session->hasObject($entity)) {
847
                $this->removeEntity($entity);
848
                $this->session->unregisterReconstitutedEntity($entity);
849
                $this->session->unregisterObject($entity);
850
            }
851
        }
852
        $this->deletedEntities = new ObjectStorage();
853
    }
854
855
    /**
856
     * Deletes an object
857
     *
858
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be removed from the storage
859
     * @param bool $markAsDeleted Whether to just flag the row deleted (default) or really delete it
860
     */
861
    protected function removeEntity(DomainObjectInterface $object, $markAsDeleted = true)
862
    {
863
        $dataMap = $this->dataMapFactory->buildDataMap(get_class($object));
864
        $tableName = $dataMap->getTableName();
865
        if ($markAsDeleted === true && $dataMap->getDeletedFlagColumnName() !== null) {
866
            $deletedColumnName = $dataMap->getDeletedFlagColumnName();
867
            $row = [
868
                'uid' => $object->getUid(),
869
                $deletedColumnName => 1
870
            ];
871
            $this->addCommonDateFieldsToRow($object, $row);
872
            $this->storageBackend->updateRow($tableName, $row);
873
        } else {
874
            $this->storageBackend->removeRow($tableName, ['uid' => $object->getUid()]);
875
        }
876
        $this->eventDispatcher->dispatch(new EntityRemovedFromPersistenceEvent($object));
877
878
        $this->removeRelatedObjects($object);
879
        $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
880
        if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
881
            $this->referenceIndex->updateRefIndexTable($tableName, $object->getUid());
882
        }
883
    }
884
885
    /**
886
     * Remove related objects
887
     *
888
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to scanned for related objects
889
     */
890
    protected function removeRelatedObjects(DomainObjectInterface $object)
891
    {
892
        $className = get_class($object);
893
        $dataMap = $this->dataMapFactory->buildDataMap($className);
894
        $classSchema = $this->reflectionService->getClassSchema($className);
895
        $properties = $object->_getProperties();
896
        foreach ($properties as $propertyName => $propertyValue) {
897
            $columnMap = $dataMap->getColumnMap($propertyName);
898
            if ($columnMap === null) {
899
                continue;
900
            }
901
            $property = $classSchema->getProperty($propertyName);
902
            if ($property->getCascadeValue() === 'remove') {
903
                if ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_MANY) {
904
                    foreach ($propertyValue as $containedObject) {
905
                        $this->removeEntity($containedObject);
906
                    }
907
                } elseif ($propertyValue instanceof DomainObjectInterface) {
908
                    $this->removeEntity($propertyValue);
909
                }
910
            } elseif ($dataMap->getDeletedFlagColumnName() === null
911
                && $columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY
912
            ) {
913
                $this->deleteAllRelationsFromRelationtable($object, $propertyName);
914
            }
915
        }
916
    }
917
918
    /**
919
     * Determine the storage page ID for a given NEW record
920
     *
921
     * This does the following:
922
     * - If the domain object has an accessible property 'pid' (i.e. through a getPid() method), that is used to store the record.
923
     * - If there is a TypoScript configuration "classes.CLASSNAME.newRecordStoragePid", that is used to store new records.
924
     * - If there is no such TypoScript configuration, it uses the first value of The "storagePid" taken for reading records.
925
     *
926
     * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
927
     * @return int the storage Page ID where the object should be stored
928
     */
929
    protected function determineStoragePageIdForNewRecord(DomainObjectInterface $object = null)
930
    {
931
        $frameworkConfiguration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
932
        if ($object !== null) {
933
            if (ObjectAccess::isPropertyGettable($object, 'pid')) {
934
                $pid = ObjectAccess::getProperty($object, 'pid');
935
                if (isset($pid)) {
936
                    return (int)$pid;
937
                }
938
            }
939
            $className = get_class($object);
940
            // todo: decide what to do with this option.
941
            if (isset($frameworkConfiguration['persistence']['classes'][$className]) && !empty($frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'])) {
942
                return (int)$frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'];
943
            }
944
        }
945
        $storagePidList = GeneralUtility::intExplode(',', $frameworkConfiguration['persistence']['storagePid']);
946
        return (int)$storagePidList[0];
947
    }
948
949
    /**
950
     * Returns a plain value
951
     *
952
     * i.e. objects are flattened out if possible.
953
     * Checks explicitly for null values as DataMapper's getPlainValue would convert this to 'NULL'
954
     *
955
     * @param mixed $input The value that will be converted
956
     * @param ColumnMap $columnMap Optional column map for retrieving the date storage format
957
     * @return int|string|null
958
     */
959
    protected function getPlainValue($input, ColumnMap $columnMap = null)
960
    {
961
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
962
        $dataMapper = $objectManager->get(DataMapper::class);
963
        return $input !== null
964
            ? $dataMapper->getPlainValue($input, $columnMap)
965
            : null;
966
    }
967
}
968