‪TYPO3CMS  ‪main
Backend.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\EventDispatcher\EventDispatcherInterface;
46 
53 {
57  protected ‪$session;
58 
62  protected ‪$persistenceManager;
63 
67  protected ‪$aggregateRootObjects;
68 
73 
77  protected ‪$changedEntities;
78 
83 
87  protected ‪$reflectionService;
88 
92  protected ‪$storageBackend;
93 
97  protected ‪$dataMapFactory;
98 
104  protected ‪$referenceIndex;
105 
109  protected ‪$configurationManager;
110 
114  protected ‪$eventDispatcher;
115 
119  public function ‪__construct(
123  \‪TYPO3\CMS\‪Extbase\Persistence\Generic\Storage\‪BackendInterface ‪$storageBackend,
125  EventDispatcherInterface ‪$eventDispatcher
126  ) {
127  $this->configurationManager = ‪$configurationManager;
128  $this->session = ‪$session;
129  $this->reflectionService = ‪$reflectionService;
130  $this->storageBackend = ‪$storageBackend;
131  $this->dataMapFactory = ‪$dataMapFactory;
132  $this->eventDispatcher = ‪$eventDispatcher;
133 
134  $this->referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class);
135  $this->aggregateRootObjects = new ‪ObjectStorage();
136  $this->deletedEntities = new ‪ObjectStorage();
137  $this->changedEntities = new ‪ObjectStorage();
138  }
139 
141  {
142  $this->persistenceManager = ‪$persistenceManager;
143  }
144 
150  public function ‪getObjectCountByQuery(‪QueryInterface $query)
151  {
152  return $this->storageBackend->getObjectCountByQuery($query);
153  }
154 
160  public function ‪getObjectDataByQuery(QueryInterface $query)
161  {
162  $event = new ModifyQueryBeforeFetchingObjectDataEvent($query);
163  $this->eventDispatcher->dispatch($event);
164  $query = $event->getQuery();
165  $result = $this->storageBackend->getObjectDataByQuery($query);
166  $event = new ‪ModifyResultAfterFetchingObjectDataEvent($query, $result);
167  $this->eventDispatcher->dispatch($event);
168  return $event->getResult();
169  }
170 
178  public function ‪getIdentifierByObject($object)
179  {
180  if ($object instanceof ‪LazyLoadingProxy) {
181  $object = $object->_loadRealInstance();
182  }
183 
184  return is_object($object) ? $this->session->getIdentifierByObject($object) : null;
185  }
186 
195  public function ‪getObjectByIdentifier(‪$identifier, $className)
196  {
197  if ($this->session->hasIdentifier(‪$identifier, $className)) {
198  return $this->session->getObjectByIdentifier(‪$identifier, $className);
199  }
200  $query = $this->persistenceManager->createQueryForType($className);
201  $query->getQuerySettings()->setRespectStoragePage(false);
202  $query->getQuerySettings()->setRespectSysLanguage(false);
203  // This allows to fetch IDs for languages for default language AND language IDs
204  // This is especially important when using the PropertyMapper of the Extbase MVC part to get
205  // an object of the translated version of the incoming ID of a record.
206  $languageAspect = $query->getQuerySettings()->getLanguageAspect();
207  $languageAspect = new LanguageAspect(
208  $languageAspect->getId(),
209  $languageAspect->getContentId(),
210  $languageAspect->getOverlayType() === ‪LanguageAspect::OVERLAYS_OFF ? ‪LanguageAspect::OVERLAYS_ON_WITH_FLOATING : $languageAspect->getOverlayType()
211  );
212  $query->getQuerySettings()->setLanguageAspect($languageAspect);
213  return $query->matching($query->equals('uid', ‪$identifier))->execute()->getFirst();
214  }
215 
222  public function ‪isNewObject($object)
223  {
224  return $this->‪getIdentifierByObject($object) === null;
225  }
226 
230  public function ‪setAggregateRootObjects(‪ObjectStorage $objects)
231  {
232  $this->aggregateRootObjects = $objects;
233  }
234 
238  public function ‪setChangedEntities(‪ObjectStorage $entities)
239  {
240  $this->changedEntities = $entities;
241  }
242 
246  public function ‪setDeletedEntities(‪ObjectStorage $entities)
247  {
248  $this->deletedEntities = $entities;
249  }
250 
254  public function ‪commit()
255  {
256  $this->‪persistObjects();
257  $this->‪processDeletedObjects();
258  }
259 
263  protected function ‪persistObjects()
264  {
265  $this->visitedDuringPersistence = new ‪ObjectStorage();
266  foreach ($this->aggregateRootObjects as $object) {
268  if ($object->_isNew()) {
269  $this->‪insertObject($object);
270  }
271  $this->‪persistObject($object);
272  }
273  foreach ($this->changedEntities as $object) {
274  $this->‪persistObject($object);
275  }
276  }
277 
283  protected function ‪persistObject(DomainObjectInterface $object)
284  {
285  if (isset($this->visitedDuringPersistence[$object])) {
286  return;
287  }
288  $row = [];
289  $queue = [];
290  $className = get_class($object);
291  $dataMap = $this->dataMapFactory->buildDataMap($className);
292  $classSchema = $this->reflectionService->getClassSchema($className);
293  foreach ($classSchema->getDomainObjectProperties() as $property) {
294  $propertyName = $property->getName();
295  if (!$dataMap->isPersistableProperty($propertyName)) {
296  continue;
297  }
298  $propertyValue = $object->_getProperty($propertyName);
299  if ($this->‪propertyValueIsLazyLoaded($propertyValue)) {
300  continue;
301  }
302  $columnMap = $dataMap->getColumnMap($propertyName);
303  if ($propertyValue instanceof ObjectStorage) {
304  $cleanProperty = $object->_getCleanProperty($propertyName);
305  // objectstorage needs to be persisted if the object is new, the objectstorage is dirty, meaning it has
306  // been changed after initial build, or an empty objectstorage is present and the cleanstate objectstorage
307  // has childelements, meaning all elements should been removed from the objectstorage
308  if ($object->_isNew() || $propertyValue->_isDirty() || ($propertyValue->count() === 0 && $cleanProperty && $cleanProperty->count() > 0)) {
309  $this->‪persistObjectStorage($propertyValue, $object, $propertyName, $row);
310  $propertyValue->_memorizeCleanState();
311  }
312  foreach ($propertyValue as $containedObject) {
313  if ($containedObject instanceof DomainObjectInterface) {
314  $queue[] = $containedObject;
315  }
316  }
317  } elseif ($propertyValue instanceof DomainObjectInterface) {
318  if ($object->_isDirty($propertyName)) {
319  if ($propertyValue->_isNew()) {
320  $this->‪insertObject($propertyValue, $object, $propertyName);
321  }
322  $row[$columnMap->getColumnName()] = $this->‪getPlainValue($propertyValue);
323  }
324  $queue[] = $propertyValue;
325  } elseif ($object->_isNew() || $object->_isDirty($propertyName)) {
326  $row[$columnMap->getColumnName()] = $this->‪getPlainValue($propertyValue, $columnMap);
327  }
328  }
329  if (!empty($row)) {
330  $this->‪updateObject($object, $row);
331  $object->_memorizeCleanState();
332  }
333  $this->visitedDuringPersistence[$object] = $object->getUid();
334  foreach ($queue as $queuedObject) {
335  $this->‪persistObject($queuedObject);
336  }
337  $this->eventDispatcher->dispatch(new ‪EntityPersistedEvent($object));
338  }
339 
346  protected function ‪propertyValueIsLazyLoaded($propertyValue)
347  {
348  if ($propertyValue instanceof LazyLoadingProxy) {
349  return true;
350  }
351  if ($propertyValue instanceof LazyObjectStorage) {
352  if ($propertyValue->isInitialized() === false) {
353  return true;
354  }
355  }
356  return false;
357  }
358 
370  protected function ‪persistObjectStorage(‪ObjectStorage $objectStorage, ‪DomainObjectInterface $parentObject, $propertyName, array &$row)
371  {
372  $className = get_class($parentObject);
373  $dataMapper = GeneralUtility::makeInstance(DataMapper::class);
374  $columnMap = $this->dataMapFactory->buildDataMap($className)->getColumnMap($propertyName);
375  $property = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
376  foreach ($this->‪getRemovedChildObjects($parentObject, $propertyName) as $removedObject) {
377  $this->‪detachObjectFromParentObject($removedObject, $parentObject, $propertyName);
378  if ($columnMap->getTypeOfRelation() === Relation::HAS_MANY && $property->getCascadeValue() === 'remove') {
379  $this->‪removeEntity($removedObject);
380  }
381  }
382 
383  $currentUids = [];
384  $sortingPosition = 1;
385  $updateSortingOfFollowing = false;
386 
387  foreach ($objectStorage as $object) {
389  if (empty($currentUids)) {
390  $sortingPosition = 1;
391  } else {
392  $sortingPosition++;
393  }
394  $cleanProperty = $parentObject->_getCleanProperty($propertyName);
395  if ($object->_isNew()) {
396  $this->‪insertObject($object);
397  $this->‪attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
398  // if a new object is inserted, all objects after this need to have their sorting updated
399  $updateSortingOfFollowing = true;
400  } elseif ($cleanProperty === null || $cleanProperty->getPosition($object) === null) {
401  // if parent object is new then it doesn't have cleanProperty yet; before attaching object it's clean position is null
402  $this->‪attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
403  // 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
404  $updateSortingOfFollowing = true;
405  } elseif ($objectStorage->‪isRelationDirty($object) || $cleanProperty->getPosition($object) !== $objectStorage->‪getPosition($object)) {
406  $this->‪updateRelationOfObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
407  $updateSortingOfFollowing = true;
408  } elseif ($updateSortingOfFollowing) {
409  if ($sortingPosition > $objectStorage->‪getPosition($object)) {
410  $this->‪updateRelationOfObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
411  } else {
412  $sortingPosition = $objectStorage->‪getPosition($object);
413  }
414  }
415  $currentUids[] = $object->getUid();
416  }
417 
418  if ($columnMap->getParentKeyFieldName() === null) {
419  $row[$columnMap->getColumnName()] = implode(',', $currentUids);
420  } else {
421  $row[$columnMap->getColumnName()] = $dataMapper->countRelated($parentObject, $propertyName);
422  }
423  }
424 
433  protected function ‪getRemovedChildObjects(‪DomainObjectInterface $object, $propertyName)
434  {
435  $removedObjects = [];
436  $cleanPropertyValue = $object->_getCleanProperty($propertyName);
437  if (is_array($cleanPropertyValue) || $cleanPropertyValue instanceof \Iterator) {
438  $propertyValue = $object->‪_getProperty($propertyName);
439  foreach ($cleanPropertyValue as $containedObject) {
440  if (!$propertyValue->contains($containedObject)) {
441  $removedObjects[] = $containedObject;
442  }
443  }
444  }
445  return $removedObjects;
446  }
447 
454  protected function ‪attachObjectToParentObject(DomainObjectInterface $object, DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition = 0)
455  {
456  $parentDataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
457 
458  $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
459  if ($parentColumnMap->getTypeOfRelation() === Relation::HAS_MANY) {
460  $this->‪attachObjectToParentObjectRelationHasMany($object, $parentObject, $parentPropertyName, $sortingPosition);
461  } elseif ($parentColumnMap->getTypeOfRelation() === Relation::HAS_AND_BELONGS_TO_MANY) {
462  $this->‪insertRelationInRelationtable($object, $parentObject, $parentPropertyName, $sortingPosition);
463  }
464  }
465 
472  protected function ‪updateRelationOfObjectToParentObject(DomainObjectInterface $object, DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition = 0)
473  {
474  $parentDataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
475  $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
476  if ($parentColumnMap->getTypeOfRelation() === Relation::HAS_MANY) {
477  $this->‪attachObjectToParentObjectRelationHasMany($object, $parentObject, $parentPropertyName, $sortingPosition);
478  } elseif ($parentColumnMap->getTypeOfRelation() === Relation::HAS_AND_BELONGS_TO_MANY) {
479  $this->‪updateRelationInRelationTable($object, $parentObject, $parentPropertyName, $sortingPosition);
480  }
481  }
482 
490  protected function ‪attachObjectToParentObjectRelationHasMany(DomainObjectInterface $object, DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition = 0)
491  {
492  $parentDataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
493  $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
494  if ($parentColumnMap->getTypeOfRelation() !== Relation::HAS_MANY) {
495  throw new IllegalRelationTypeException(
496  'Parent column relation type is ' . Relation::class . '::' . $parentColumnMap->getTypeOfRelation()->name .
497  ' but should be ' . Relation::class . '::' . Relation::HAS_MANY->name,
498  1345368105
499  );
500  }
501  $row = [];
502  $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
503  if ($parentKeyFieldName !== null) {
504  $row[$parentKeyFieldName] = $parentObject->_getProperty(‪AbstractDomainObject::PROPERTY_LOCALIZED_UID) ?: $parentObject->getUid();
505  $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
506  if ($parentTableFieldName !== null) {
507  $row[$parentTableFieldName] = $parentDataMap->getTableName();
508  }
509  $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
510  if (is_array($relationTableMatchFields)) {
511  $row = array_merge($relationTableMatchFields, $row);
512  }
513  }
514  $childSortByFieldName = $parentColumnMap->getChildSortByFieldName();
515  if (!empty($childSortByFieldName)) {
516  $row[$childSortByFieldName] = $sortingPosition;
517  }
518  if (!empty($row)) {
519  $this->‪updateObject($object, $row);
520  }
521  }
522 
528  protected function ‪detachObjectFromParentObject(DomainObjectInterface $object, DomainObjectInterface $parentObject, $parentPropertyName)
529  {
530  $parentDataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
531  $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
532  if ($parentColumnMap->getTypeOfRelation() === Relation::HAS_MANY) {
533  $row = [];
534  $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
535  if ($parentKeyFieldName !== null) {
536  $row[$parentKeyFieldName] = 0;
537  $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
538  if ($parentTableFieldName !== null) {
539  $row[$parentTableFieldName] = '';
540  }
541  $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
542  if (is_array($relationTableMatchFields) && !empty($relationTableMatchFields)) {
543  $row = array_merge(array_fill_keys(array_keys($relationTableMatchFields), ''), $row);
544  }
545  }
546  $childSortByFieldName = $parentColumnMap->getChildSortByFieldName();
547  if (!empty($childSortByFieldName)) {
548  $row[$childSortByFieldName] = 0;
549  }
550  if (!empty($row)) {
551  $this->‪updateObject($object, $row);
552  }
553  } elseif ($parentColumnMap->getTypeOfRelation() === Relation::HAS_AND_BELONGS_TO_MANY) {
554  $this->‪deleteRelationFromRelationtable($object, $parentObject, $parentPropertyName);
555  }
556  }
557 
565  protected function ‪insertObject(DomainObjectInterface $object, DomainObjectInterface $parentObject = null, $parentPropertyName = '')
566  {
567  if ($object instanceof AbstractValueObject) {
568  $result = $this->‪getUidOfAlreadyPersistedValueObject($object);
569  if ($result !== null) {
570  $object->_setProperty(‪AbstractDomainObject::PROPERTY_UID, $result);
571  return;
572  }
573  }
574  $className = get_class($object);
575  $dataMap = $this->dataMapFactory->buildDataMap($className);
576  $row = [];
577  $classSchema = $this->reflectionService->getClassSchema($className);
578  foreach ($classSchema->getDomainObjectProperties() as $property) {
579  $propertyName = $property->getName();
580  if (!$dataMap->isPersistableProperty($propertyName)) {
581  continue;
582  }
583  $propertyValue = $object->_getProperty($propertyName);
584  if ($this->‪propertyValueIsLazyLoaded($propertyValue)) {
585  continue;
586  }
587  $columnMap = $dataMap->getColumnMap($propertyName);
588  if ($columnMap->getTypeOfRelation() === Relation::HAS_ONE) {
589  $row[$columnMap->getColumnName()] = 0;
590  } elseif ($columnMap->getTypeOfRelation() !== Relation::NONE) {
591  if ($columnMap->getParentKeyFieldName() === null) {
592  // CSV type relation
593  $row[$columnMap->getColumnName()] = '';
594  } else {
595  // MM type relation
596  $row[$columnMap->getColumnName()] = 0;
597  }
598  } elseif ($propertyValue !== null) {
599  $row[$columnMap->getColumnName()] = $this->‪getPlainValue($propertyValue, $columnMap);
600  }
601  }
602  $this->‪addCommonFieldsToRow($object, $row);
603  if ($dataMap->getLanguageIdColumnName() !== null && $object->_getProperty(‪AbstractDomainObject::PROPERTY_LANGUAGE_UID) === null) {
604  $row[$dataMap->getLanguageIdColumnName()] = 0;
605  $object->_setProperty(‪AbstractDomainObject::PROPERTY_LANGUAGE_UID, 0);
606  }
607  if ($dataMap->getTranslationOriginColumnName() !== null) {
608  $row[$dataMap->getTranslationOriginColumnName()] = 0;
609  }
610  if ($dataMap->getTranslationOriginDiffSourceName() !== null) {
611  $row[$dataMap->getTranslationOriginDiffSourceName()] = '';
612  }
613  if ($parentObject !== null && $parentPropertyName) {
614  $parentColumnDataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject))->getColumnMap($parentPropertyName);
615  $relationTableMatchFields = $parentColumnDataMap->getRelationTableMatchFields();
616  if (is_array($relationTableMatchFields)) {
617  $row = array_merge($relationTableMatchFields, $row);
618  }
619  if ($parentColumnDataMap->getParentKeyFieldName() !== null) {
620  $row[$parentColumnDataMap->getParentKeyFieldName()] = (int)$parentObject->getUid();
621  }
622  }
623  ‪$uid = $this->storageBackend->addRow($dataMap->getTableName(), $row);
624  $localizedUid = $object->_getProperty(‪AbstractDomainObject::PROPERTY_LOCALIZED_UID);
625  ‪$identifier = ‪$uid . ($localizedUid ? '_' . $localizedUid : '');
626  $object->_setProperty(‪AbstractDomainObject::PROPERTY_UID, ‪$uid);
627  $object->setPid((int)$row['pid']);
628  if (‪$uid >= 1) {
629  $this->eventDispatcher->dispatch(new EntityAddedToPersistenceEvent($object));
630  }
631  $frameworkConfiguration = $this->configurationManager->getConfiguration(‪ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
632  if (($frameworkConfiguration['persistence']['updateReferenceIndex'] ?? '') === '1') {
633  $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), ‪$uid);
634  }
635  $this->session->registerObject($object, ‪$identifier);
636  if (‪$uid >= 1) {
637  $this->eventDispatcher->dispatch(new ‪EntityFinalizedAfterPersistenceEvent($object));
638  }
639  }
640 
648  {
649  return $this->storageBackend->getUidOfAlreadyPersistedValueObject($object);
650  }
651 
661  protected function ‪insertRelationInRelationtable(‪DomainObjectInterface $object, ‪DomainObjectInterface $parentObject, $propertyName, $sortingPosition = null)
662  {
663  $dataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
664  $columnMap = $dataMap->getColumnMap($propertyName);
665  $parentUid = $parentObject->‪getUid();
668  }
669  $row = [
670  $columnMap->getParentKeyFieldName() => (int)$parentUid,
671  $columnMap->getChildKeyFieldName() => (int)$object->‪getUid(),
672  $columnMap->getChildSortByFieldName() => $sortingPosition !== null ? (int)$sortingPosition : 0,
673  ];
674  $relationTableName = $columnMap->getRelationTableName();
675  if (isset(‪$GLOBALS['TCA'][$relationTableName])) {
677  }
678  $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
679  if (is_array($relationTableMatchFields)) {
680  $row = array_merge($relationTableMatchFields, $row);
681  }
682  $res = $this->storageBackend->addRow($relationTableName, $row, true);
683  return $res;
684  }
685 
695  protected function ‪updateRelationInRelationTable(‪DomainObjectInterface $object, ‪DomainObjectInterface $parentObject, $propertyName, $sortingPosition = 0)
696  {
697  $dataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
698  $columnMap = $dataMap->getColumnMap($propertyName);
699  $row = [
700  $columnMap->getParentKeyFieldName() => (int)$parentObject->‪getUid(),
701  $columnMap->getChildKeyFieldName() => (int)$object->‪getUid(),
702  $columnMap->getChildSortByFieldName() => (int)$sortingPosition,
703  ];
704  $relationTableName = $columnMap->getRelationTableName();
705  $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
706  if (is_array($relationTableMatchFields)) {
707  $row = array_merge($relationTableMatchFields, $row);
708  }
709  $this->storageBackend->updateRelationTableRow(
710  $relationTableName,
711  $row
712  );
713  return true;
714  }
715 
723  protected function ‪deleteAllRelationsFromRelationtable(‪DomainObjectInterface $parentObject, $parentPropertyName)
724  {
725  $dataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
726  $columnMap = $dataMap->getColumnMap($parentPropertyName);
727  $relationTableName = $columnMap->getRelationTableName();
728  $relationMatchFields = [
729  $columnMap->getParentKeyFieldName() => (int)$parentObject->‪getUid(),
730  ];
731  $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
732  if (is_array($relationTableMatchFields)) {
733  $relationMatchFields = array_merge($relationTableMatchFields, $relationMatchFields);
734  }
735  $this->storageBackend->removeRow($relationTableName, $relationMatchFields, false);
736  return true;
737  }
738 
747  protected function ‪deleteRelationFromRelationtable(‪DomainObjectInterface $relatedObject, ‪DomainObjectInterface $parentObject, $parentPropertyName)
748  {
749  $dataMap = $this->dataMapFactory->buildDataMap(get_class($parentObject));
750  $columnMap = $dataMap->getColumnMap($parentPropertyName);
751  $relationTableName = $columnMap->getRelationTableName();
752  $relationMatchFields = [
753  $columnMap->getParentKeyFieldName() => (int)$parentObject->‪getUid(),
754  $columnMap->getChildKeyFieldName() => (int)$relatedObject->‪getUid(),
755  ];
756  $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
757  if (is_array($relationTableMatchFields)) {
758  $relationMatchFields = array_merge($relationTableMatchFields, $relationMatchFields);
759  }
760  $this->storageBackend->removeRow($relationTableName, $relationMatchFields, false);
761  return true;
762  }
763 
771  protected function ‪updateObject(‪DomainObjectInterface $object, array $row)
772  {
773  $dataMap = $this->dataMapFactory->buildDataMap(get_class($object));
774  $this->‪addCommonFieldsToRow($object, $row);
775  $row['uid'] = $object->‪getUid();
776  if ($dataMap->getLanguageIdColumnName() !== null) {
777  $row[$dataMap->getLanguageIdColumnName()] = (int)$object->‪_getProperty(‪AbstractDomainObject::PROPERTY_LANGUAGE_UID);
780  }
781  }
782  $this->storageBackend->updateRow($dataMap->getTableName(), $row);
783  $this->eventDispatcher->dispatch(new ‪EntityUpdatedInPersistenceEvent($object));
784 
785  $frameworkConfiguration = $this->configurationManager->getConfiguration(‪ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
786  if (($frameworkConfiguration['persistence']['updateReferenceIndex'] ?? '') === '1') {
787  $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), (int)$row['uid']);
788  }
789  return true;
790  }
791 
795  protected function ‪addCommonFieldsToRow(‪DomainObjectInterface $object, array &$row)
796  {
797  $dataMap = $this->dataMapFactory->buildDataMap(get_class($object));
798  $this->‪addCommonDateFieldsToRow($object, $row);
799  if ($dataMap->getRecordTypeColumnName() !== null && $dataMap->getRecordType() !== null) {
800  $row[$dataMap->getRecordTypeColumnName()] = $dataMap->getRecordType();
801  }
802  if ($object->‪_isNew() && !isset($row['pid'])) {
803  $row['pid'] = $this->‪determineStoragePageIdForNewRecord($object);
804  }
805  }
806 
812  protected function ‪addCommonDateFieldsToRow(DomainObjectInterface $object, array &$row)
813  {
814  $dataMap = $this->dataMapFactory->buildDataMap(get_class($object));
815  if ($object->_isNew() && $dataMap->getCreationDateColumnName() !== null) {
816  $row[$dataMap->getCreationDateColumnName()] = ‪$GLOBALS['EXEC_TIME'];
817  }
818  if ($dataMap->getModificationDateColumnName() !== null) {
819  $row[$dataMap->getModificationDateColumnName()] = ‪$GLOBALS['EXEC_TIME'];
820  }
821  }
822 
826  protected function ‪processDeletedObjects()
827  {
828  foreach ($this->deletedEntities as $entity) {
829  if ($this->session->hasObject($entity)) {
830  $this->‪removeEntity($entity);
831  $this->session->unregisterReconstitutedEntity($entity);
832  $this->session->unregisterObject($entity);
833  }
834  }
835  $this->deletedEntities = new ‪ObjectStorage();
836  }
837 
844  protected function ‪removeEntity(DomainObjectInterface $object, $markAsDeleted = true)
845  {
846  $dataMap = $this->dataMapFactory->buildDataMap(get_class($object));
847  $tableName = $dataMap->getTableName();
848  if ($markAsDeleted === true && $dataMap->getDeletedFlagColumnName() !== null) {
849  $deletedColumnName = $dataMap->getDeletedFlagColumnName();
850  $row = [
851  'uid' => $object->getUid(),
852  $deletedColumnName => 1,
853  ];
854  $this->‪addCommonDateFieldsToRow($object, $row);
855  $this->storageBackend->updateRow($tableName, $row);
856  } else {
857  $this->storageBackend->removeRow($tableName, ['uid' => $object->getUid()]);
858  }
859  $this->eventDispatcher->dispatch(new EntityRemovedFromPersistenceEvent($object));
860 
861  $this->‪removeRelatedObjects($object);
862  $frameworkConfiguration = $this->configurationManager->getConfiguration(‪ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
863  if (($frameworkConfiguration['persistence']['updateReferenceIndex'] ?? '') === '1') {
864  $this->referenceIndex->updateRefIndexTable($tableName, $object->getUid());
865  }
866  }
867 
873  protected function ‪removeRelatedObjects(‪DomainObjectInterface $object)
874  {
875  $className = get_class($object);
876  $dataMap = $this->dataMapFactory->buildDataMap($className);
877  $classSchema = $this->reflectionService->getClassSchema($className);
878  foreach ($classSchema->getDomainObjectProperties() as $property) {
879  $propertyName = $property->getName();
880  $columnMap = $dataMap->getColumnMap($propertyName);
881  if ($columnMap === null) {
882  continue;
883  }
884  $propertyValue = $object->‪_getProperty($propertyName);
885  if ($property->getCascadeValue() === 'remove') {
886  if ($columnMap->getTypeOfRelation() === Relation::HAS_MANY) {
887  foreach ($propertyValue as $containedObject) {
888  $this->‪removeEntity($containedObject);
889  }
890  } elseif ($propertyValue instanceof DomainObjectInterface) {
891  $this->‪removeEntity($propertyValue);
892  }
893  } elseif ($dataMap->getDeletedFlagColumnName() === null
894  && $columnMap->getTypeOfRelation() === Relation::HAS_AND_BELONGS_TO_MANY
895  ) {
896  $this->‪deleteAllRelationsFromRelationtable($object, $propertyName);
897  }
898  }
899  }
900 
912  protected function ‪determineStoragePageIdForNewRecord(‪DomainObjectInterface $object = null)
913  {
914  $frameworkConfiguration = $this->configurationManager->getConfiguration(‪ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
915  if ($object !== null) {
918  if (isset($pid)) {
919  return (int)$pid;
920  }
921  }
922  $className = get_class($object);
923  // todo: decide what to do with this option.
924  if (isset($frameworkConfiguration['persistence']['classes'][$className]) && !empty($frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'])) {
925  return (int)$frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'];
926  }
927  }
928  $storagePidList = ‪GeneralUtility::intExplode(',', (string)($frameworkConfiguration['persistence']['storagePid'] ?? ''));
929  return $storagePidList[0];
930  }
931 
942  protected function ‪getPlainValue($input, ‪ColumnMap $columnMap = null)
943  {
944  return $input !== null
945  ? GeneralUtility::makeInstance(DataMapper::class)->getPlainValue($input, $columnMap)
946  : null;
947  }
948 }
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend
Definition: Backend.php:53
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\$deletedEntities
‪TYPO3 CMS Extbase Persistence ObjectStorage $deletedEntities
Definition: Backend.php:68
‪TYPO3\CMS\Extbase\Event\Persistence\EntityFinalizedAfterPersistenceEvent
Definition: EntityFinalizedAfterPersistenceEvent.php:27
‪TYPO3\CMS\Extbase\Reflection\ObjectAccess\getProperty
‪static mixed getProperty(object|array $subject, string $propertyName)
Definition: ObjectAccess.php:59
‪TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
Definition: PersistenceManagerInterface.php:24
‪TYPO3\CMS\Extbase\Annotation
Definition: IgnoreValidation.php:18
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\insertObject
‪insertObject(DomainObjectInterface $object, DomainObjectInterface $parentObject=null, $parentPropertyName='')
Definition: Backend.php:553
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\$reflectionService
‪TYPO3 CMS Extbase Reflection ReflectionService $reflectionService
Definition: Backend.php:80
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\getUidOfAlreadyPersistedValueObject
‪int null getUidOfAlreadyPersistedValueObject(AbstractValueObject $object)
Definition: Backend.php:635
‪TYPO3\CMS\Extbase\Persistence\QueryInterface
Definition: QueryInterface.php:30
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\$storageBackend
‪TYPO3 CMS Extbase Persistence Generic Storage BackendInterface $storageBackend
Definition: Backend.php:84
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\$session
‪TYPO3 CMS Extbase Persistence Generic Session $session
Definition: Backend.php:56
‪TYPO3\CMS\Extbase\Event\Persistence\EntityAddedToPersistenceEvent
Definition: EntityAddedToPersistenceEvent.php:27
‪TYPO3\CMS\Core\Database\ReferenceIndex
Definition: ReferenceIndex.php:40
‪TYPO3
‪TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap
Definition: ColumnMap.php:27
‪TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject\PROPERTY_PID
‪const PROPERTY_PID
Definition: AbstractDomainObject.php:33
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\addCommonFieldsToRow
‪addCommonFieldsToRow(DomainObjectInterface $object, array &$row)
Definition: Backend.php:783
‪TYPO3\CMS\Extbase\Event\Persistence\EntityRemovedFromPersistenceEvent
Definition: EntityRemovedFromPersistenceEvent.php:26
‪TYPO3\CMS\Extbase\Persistence\ObjectStorage\getPosition
‪getPosition(object $object)
Definition: ObjectStorage.php:343
‪TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
Definition: ConfigurationManagerInterface.php:28
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\attachObjectToParentObject
‪attachObjectToParentObject(DomainObjectInterface $object, DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition=0)
Definition: Backend.php:442
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\persistObjectStorage
‪persistObjectStorage(ObjectStorage $objectStorage, DomainObjectInterface $parentObject, $propertyName, array &$row)
Definition: Backend.php:358
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\processDeletedObjects
‪processDeletedObjects()
Definition: Backend.php:814
‪TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper
Definition: DataMapper.php:58
‪TYPO3\CMS\Extbase\Persistence\ObjectStorage\isRelationDirty
‪isRelationDirty(object $object)
Definition: ObjectStorage.php:336
‪TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface\_getProperty
‪_getProperty(string $propertyName)
‪TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject\PROPERTY_LOCALIZED_UID
‪const PROPERTY_LOCALIZED_UID
Definition: AbstractDomainObject.php:34
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\persistObject
‪persistObject(DomainObjectInterface $object)
Definition: Backend.php:271
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\commit
‪commit()
Definition: Backend.php:242
‪TYPO3\CMS\Extbase\Event\Persistence\ModifyResultAfterFetchingObjectDataEvent
Definition: ModifyResultAfterFetchingObjectDataEvent.php:26
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\$configurationManager
‪TYPO3 CMS Extbase Configuration ConfigurationManagerInterface $configurationManager
Definition: Backend.php:98
‪TYPO3\CMS\Extbase\Reflection\ObjectAccess
Definition: ObjectAccess.php:39
‪TYPO3\CMS\Extbase\Persistence\ObjectStorage
Definition: ObjectStorage.php:34
‪TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException
Definition: IllegalRelationTypeException.php:25
‪TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface\CONFIGURATION_TYPE_FRAMEWORK
‪const CONFIGURATION_TYPE_FRAMEWORK
Definition: ConfigurationManagerInterface.php:29
‪TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory
Definition: DataMapFactory.php:34
‪TYPO3\CMS\Extbase\Reflection\ReflectionService
Definition: ReflectionService.php:28
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\setPersistenceManager
‪setPersistenceManager(PersistenceManagerInterface $persistenceManager)
Definition: Backend.php:128
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\removeRelatedObjects
‪removeRelatedObjects(DomainObjectInterface $object)
Definition: Backend.php:861
‪TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface\_isNew
‪_isNew()
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\$eventDispatcher
‪EventDispatcherInterface $eventDispatcher
Definition: Backend.php:102
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\getPlainValue
‪int string null getPlainValue($input, ColumnMap $columnMap=null)
Definition: Backend.php:930
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\$aggregateRootObjects
‪TYPO3 CMS Extbase Persistence ObjectStorage $aggregateRootObjects
Definition: Backend.php:64
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\propertyValueIsLazyLoaded
‪bool propertyValueIsLazyLoaded($propertyValue)
Definition: Backend.php:334
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\determineStoragePageIdForNewRecord
‪int determineStoragePageIdForNewRecord(DomainObjectInterface $object=null)
Definition: Backend.php:900
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\getObjectCountByQuery
‪int getObjectCountByQuery(QueryInterface $query)
Definition: Backend.php:138
‪TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
Definition: BackendInterface.php:26
‪TYPO3\CMS\Extbase\DomainObject\AbstractValueObject
Definition: AbstractValueObject.php:26
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\$visitedDuringPersistence
‪TYPO3 CMS Extbase Persistence ObjectStorage $visitedDuringPersistence
Definition: Backend.php:76
‪TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface
Definition: DomainObjectInterface.php:33
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\getObjectDataByQuery
‪array getObjectDataByQuery(QueryInterface $query)
Definition: Backend.php:148
‪TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject
Definition: AbstractDomainObject.php:31
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\$referenceIndex
‪TYPO3 CMS Core Database ReferenceIndex $referenceIndex
Definition: Backend.php:94
‪TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface\getUid
‪getUid()
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\setAggregateRootObjects
‪setAggregateRootObjects(ObjectStorage $objects)
Definition: Backend.php:218
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\removeEntity
‪removeEntity(DomainObjectInterface $object, $markAsDeleted=true)
Definition: Backend.php:832
‪TYPO3\CMS\Extbase\Persistence\Generic\Session
Definition: Session.php:27
‪TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject\PROPERTY_LANGUAGE_UID
‪const PROPERTY_LANGUAGE_UID
Definition: AbstractDomainObject.php:35
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\setChangedEntities
‪setChangedEntities(ObjectStorage $entities)
Definition: Backend.php:226
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\__construct
‪__construct(ConfigurationManagerInterface $configurationManager, Session $session, ReflectionService $reflectionService, \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface $storageBackend, DataMapFactory $dataMapFactory, EventDispatcherInterface $eventDispatcher)
Definition: Backend.php:107
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\deleteAllRelationsFromRelationtable
‪bool deleteAllRelationsFromRelationtable(DomainObjectInterface $parentObject, $parentPropertyName)
Definition: Backend.php:711
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\insertRelationInRelationtable
‪int insertRelationInRelationtable(DomainObjectInterface $object, DomainObjectInterface $parentObject, $propertyName, $sortingPosition=null)
Definition: Backend.php:649
‪TYPO3\CMS\Core\Context\LanguageAspect
Definition: LanguageAspect.php:57
‪TYPO3\CMS\Extbase\Event\Persistence\EntityUpdatedInPersistenceEvent
Definition: EntityUpdatedInPersistenceEvent.php:26
‪TYPO3\CMS\Extbase\Persistence\Generic
Definition: Backend.php:18
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\updateRelationInRelationTable
‪bool updateRelationInRelationTable(DomainObjectInterface $object, DomainObjectInterface $parentObject, $propertyName, $sortingPosition=0)
Definition: Backend.php:683
‪TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface\_setProperty
‪_setProperty(string $propertyName, mixed $value)
‪TYPO3\CMS\Extbase\Event\Persistence\ModifyQueryBeforeFetchingObjectDataEvent
Definition: ModifyQueryBeforeFetchingObjectDataEvent.php:26
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\$dataMapFactory
‪TYPO3 CMS Extbase Persistence Generic Mapper DataMapFactory $dataMapFactory
Definition: Backend.php:88
‪TYPO3\CMS\Extbase\Reflection\ObjectAccess\isPropertyGettable
‪static isPropertyGettable(object|array $object, string $propertyName)
Definition: ObjectAccess.php:335
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy
Definition: LazyLoadingProxy.php:30
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\getRemovedChildObjects
‪array getRemovedChildObjects(DomainObjectInterface $object, $propertyName)
Definition: Backend.php:421
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\isNewObject
‪bool isNewObject($object)
Definition: Backend.php:210
‪TYPO3\CMS\Extbase\Event\Persistence\EntityPersistedEvent
Definition: EntityPersistedEvent.php:26
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\getObjectByIdentifier
‪object null getObjectByIdentifier($identifier, $className)
Definition: Backend.php:183
‪TYPO3\CMS\Core\Context\LanguageAspect\OVERLAYS_OFF
‪const OVERLAYS_OFF
Definition: LanguageAspect.php:74
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\updateRelationOfObjectToParentObject
‪updateRelationOfObjectToParentObject(DomainObjectInterface $object, DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition=0)
Definition: Backend.php:460
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\persistObjects
‪persistObjects()
Definition: Backend.php:251
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\$changedEntities
‪TYPO3 CMS Extbase Persistence ObjectStorage $changedEntities
Definition: Backend.php:72
‪TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject\PROPERTY_UID
‪const PROPERTY_UID
Definition: AbstractDomainObject.php:32
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Context\LanguageAspect\OVERLAYS_ON_WITH_FLOATING
‪const OVERLAYS_ON_WITH_FLOATING
Definition: LanguageAspect.php:77
‪TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage
Definition: LazyObjectStorage.php:36
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\attachObjectToParentObjectRelationHasMany
‪attachObjectToParentObjectRelationHasMany(DomainObjectInterface $object, DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition=0)
Definition: Backend.php:478
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static list< int > intExplode(string $delimiter, string $string, bool $removeEmptyValues=false)
Definition: GeneralUtility.php:756
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\setDeletedEntities
‪setDeletedEntities(ObjectStorage $entities)
Definition: Backend.php:234
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\updateObject
‪bool updateObject(DomainObjectInterface $object, array $row)
Definition: Backend.php:759
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\detachObjectFromParentObject
‪detachObjectFromParentObject(DomainObjectInterface $object, DomainObjectInterface $parentObject, $parentPropertyName)
Definition: Backend.php:516
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\addCommonDateFieldsToRow
‪addCommonDateFieldsToRow(DomainObjectInterface $object, array &$row)
Definition: Backend.php:800
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap
Definition: Relation.php:18
‪TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap\Relation
‪Relation
Definition: Relation.php:24
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\deleteRelationFromRelationtable
‪bool deleteRelationFromRelationtable(DomainObjectInterface $relatedObject, DomainObjectInterface $parentObject, $parentPropertyName)
Definition: Backend.php:735
‪TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface\setPid
‪setPid(int $pid)
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\$persistenceManager
‪TYPO3 CMS Extbase Persistence PersistenceManagerInterface $persistenceManager
Definition: Backend.php:60
‪TYPO3\CMS\Extbase\Persistence\Generic\Backend\getIdentifierByObject
‪string null getIdentifierByObject($object)
Definition: Backend.php:166