TYPO3  7.6
Backend.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Extbase\Persistence\Generic;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
20 
26 {
30  protected $session;
31 
36 
41 
45  protected $deletedEntities;
46 
50  protected $changedEntities;
51 
56 
60  protected $reflectionService;
61 
65  protected $qomFactory;
66 
70  protected $storageBackend;
71 
75  protected $dataMapper;
76 
82  protected $referenceIndex;
83 
88 
93 
97  public function injectSession(\TYPO3\CMS\Extbase\Persistence\Generic\Session $session)
98  {
99  $this->session = $session;
100  }
101 
105  public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
106  {
107  $this->reflectionService = $reflectionService;
108  }
109 
113  public function injectQomFactory(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory $qomFactory)
114  {
115  $this->qomFactory = $qomFactory;
116  }
117 
121  public function injectStorageBackend(\TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface $storageBackend)
122  {
123  $this->storageBackend = $storageBackend;
124  }
125 
129  public function injectDataMapper(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper)
130  {
131  $this->dataMapper = $dataMapper;
132  }
133 
137  public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
138  {
139  $this->signalSlotDispatcher = $signalSlotDispatcher;
140  }
141 
147  public function __construct(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager)
148  {
149  $this->configurationManager = $configurationManager;
150  $this->referenceIndex = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ReferenceIndex::class);
151  $this->aggregateRootObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
152  $this->deletedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
153  $this->changedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
154  }
155 
159  public function setPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager)
160  {
161  $this->persistenceManager = $persistenceManager;
162  }
163 
169  public function getSession()
170  {
171  return $this->session;
172  }
173 
179  public function getDataMapper()
180  {
181  return $this->dataMapper;
182  }
183 
189  public function getQomFactory()
190  {
191  return $this->qomFactory;
192  }
193 
199  public function getReflectionService()
200  {
202  }
203 
211  public function getObjectCountByQuery(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query)
212  {
213  return $this->storageBackend->getObjectCountByQuery($query);
214  }
215 
223  public function getObjectDataByQuery(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query)
224  {
225  $query = $this->emitBeforeGettingObjectDataSignal($query);
226  $result = $this->storageBackend->getObjectDataByQuery($query);
227  $result = $this->emitafterGettingObjectDataSignal($query, $result);
228  return $result;
229  }
230 
237  protected function emitBeforeGettingObjectDataSignal(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query)
238  {
239  $signalArguments = $this->signalSlotDispatcher->dispatch(__CLASS__, 'beforeGettingObjectData', array($query));
240  return $signalArguments[0];
241  }
242 
250  protected function emitAfterGettingObjectDataSignal(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query, array $result)
251  {
252  $signalArguments = $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterGettingObjectData', array($query, $result));
253  return $signalArguments[1];
254  }
255 
263  public function getIdentifierByObject($object)
264  {
265  if ($object instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
266  $object = $object->_loadRealInstance();
267  if (!is_object($object)) {
268  return null;
269  }
270  }
271  return $this->session->getIdentifierByObject($object);
272  }
273 
282  public function getObjectByIdentifier($identifier, $className)
283  {
284  if ($this->session->hasIdentifier($identifier, $className)) {
285  return $this->session->getObjectByIdentifier($identifier, $className);
286  } else {
287  $query = $this->persistenceManager->createQueryForType($className);
288  $query->getQuerySettings()->setRespectStoragePage(false);
289  $query->getQuerySettings()->setRespectSysLanguage(false);
290  return $query->matching($query->equals('uid', $identifier))->execute()->getFirst();
291  }
292  }
293 
300  public function isNewObject($object)
301  {
302  return $this->getIdentifierByObject($object) === null;
303  }
304 
311  public function setAggregateRootObjects(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects)
312  {
313  $this->aggregateRootObjects = $objects;
314  }
315 
322  public function setChangedEntities(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities)
323  {
324  $this->changedEntities = $entities;
325  }
326 
333  public function setDeletedEntities(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities)
334  {
335  $this->deletedEntities = $entities;
336  }
337 
343  public function commit()
344  {
345  $this->persistObjects();
346  $this->processDeletedObjects();
347  }
348 
354  protected function persistObjects()
355  {
356  $this->visitedDuringPersistence = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
357  foreach ($this->aggregateRootObjects as $object) {
359  if ($object->_isNew()) {
360  $this->insertObject($object);
361  }
362  $this->persistObject($object, null);
363  }
364  foreach ($this->changedEntities as $object) {
365  $this->persistObject($object, null);
366  }
367  }
368 
375  protected function persistObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object)
376  {
377  if (isset($this->visitedDuringPersistence[$object])) {
378  return;
379  }
380  $row = array();
381  $queue = array();
382  $dataMap = $this->dataMapper->getDataMap(get_class($object));
383  $properties = $object->_getProperties();
384  foreach ($properties as $propertyName => $propertyValue) {
385  if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) {
386  continue;
387  }
388  $columnMap = $dataMap->getColumnMap($propertyName);
389  if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage) {
390  $cleanProperty = $object->_getCleanProperty($propertyName);
391  // objectstorage needs to be persisted if the object is new, the objectstorge is dirty, meaning it has
392  // been changed after initial build, or an empty objectstorge is present and the cleanstate objectstorage
393  // has childelements, meaning all elements should been removed from the objectstorage
394  if ($object->_isNew() || $propertyValue->_isDirty() || ($propertyValue->count() === 0 && $cleanProperty && $cleanProperty->count() > 0)) {
395  $this->persistObjectStorage($propertyValue, $object, $propertyName, $row);
396  $propertyValue->_memorizeCleanState();
397  }
398  foreach ($propertyValue as $containedObject) {
399  if ($containedObject instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) {
400  $queue[] = $containedObject;
401  }
402  }
403  } elseif ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface
404  && $object instanceof ObjectMonitoringInterface) {
405  if ($object->_isDirty($propertyName)) {
406  if ($propertyValue->_isNew()) {
407  $this->insertObject($propertyValue, $object, $propertyName);
408  }
409  // Check explicitly for NULL, as getPlainValue would convert this to 'NULL'
410  $row[$columnMap->getColumnName()] = $propertyValue !== null
411  ? $this->dataMapper->getPlainValue($propertyValue)
412  : null;
413  }
414  $queue[] = $propertyValue;
415  } elseif ($object->_isNew() || $object->_isDirty($propertyName)) {
416  $row[$columnMap->getColumnName()] = $this->dataMapper->getPlainValue($propertyValue, $columnMap);
417  }
418  }
419  if (!empty($row)) {
420  $this->updateObject($object, $row);
421  $object->_memorizeCleanState();
422  }
423  $this->visitedDuringPersistence[$object] = $object->getUid();
424  foreach ($queue as $queuedObject) {
425  $this->persistObject($queuedObject);
426  }
427  $this->emitAfterPersistObjectSignal($object);
428  }
429 
436  protected function propertyValueIsLazyLoaded($propertyValue)
437  {
438  if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
439  return true;
440  }
441  if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage) {
442  if ($propertyValue->isInitialized() === false) {
443  return true;
444  }
445  }
446  return false;
447  }
448 
460  protected function persistObjectStorage(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $objectStorage, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $propertyName, array &$row)
461  {
462  $className = get_class($parentObject);
463  $columnMap = $this->dataMapper->getDataMap($className)->getColumnMap($propertyName);
464  $propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
465  foreach ($this->getRemovedChildObjects($parentObject, $propertyName) as $removedObject) {
466  $this->detachObjectFromParentObject($removedObject, $parentObject, $propertyName);
467  if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY && $propertyMetaData['cascade'] === 'remove') {
468  $this->removeEntity($removedObject);
469  }
470  }
471 
472  $currentUids = array();
473  $sortingPosition = 1;
474  $updateSortingOfFollowing = false;
475 
476  foreach ($objectStorage as $object) {
478  if (empty($currentUids)) {
479  $sortingPosition = 1;
480  } else {
481  $sortingPosition++;
482  }
483  $cleanProperty = $parentObject->_getCleanProperty($propertyName);
484  if ($object->_isNew()) {
485  $this->insertObject($object);
486  $this->attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
487  // if a new object is inserted, all objects after this need to have their sorting updated
488  $updateSortingOfFollowing = true;
489  } elseif ($cleanProperty === null || $cleanProperty->getPosition($object) === null) {
490  // if parent object is new then it doesn't have cleanProperty yet; before attaching object it's clean position is null
491  $this->attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
492  // 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
493  $updateSortingOfFollowing = true;
494  } elseif ($objectStorage->isRelationDirty($object) || $cleanProperty->getPosition($object) !== $objectStorage->getPosition($object)) {
495  $this->updateRelationOfObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
496  $updateSortingOfFollowing = true;
497  } elseif ($updateSortingOfFollowing) {
498  if ($sortingPosition > $objectStorage->getPosition($object)) {
499  $this->updateRelationOfObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
500  } else {
501  $sortingPosition = $objectStorage->getPosition($object);
502  }
503  }
504  $currentUids[] = $object->getUid();
505  }
506 
507  if ($columnMap->getParentKeyFieldName() === null) {
508  $row[$columnMap->getColumnName()] = implode(',', $currentUids);
509  } else {
510  $row[$columnMap->getColumnName()] = $this->dataMapper->countRelated($parentObject, $propertyName);
511  }
512  }
513 
522  protected function getRemovedChildObjects(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, $propertyName)
523  {
524  $removedObjects = array();
525  $cleanPropertyValue = $object->_getCleanProperty($propertyName);
526  if (is_array($cleanPropertyValue) || $cleanPropertyValue instanceof \Iterator) {
527  $propertyValue = $object->_getProperty($propertyName);
528  foreach ($cleanPropertyValue as $containedObject) {
529  if (!$propertyValue->contains($containedObject)) {
530  $removedObjects[] = $containedObject;
531  }
532  }
533  }
534  return $removedObjects;
535  }
536 
546  protected function attachObjectToParentObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition = 0)
547  {
548  $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
549  $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
550  if ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
551  $this->attachObjectToParentObjectRelationHasMany($object, $parentObject, $parentPropertyName, $sortingPosition);
552  } elseif ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
553  $this->insertRelationInRelationtable($object, $parentObject, $parentPropertyName, $sortingPosition);
554  }
555  }
556 
566  protected function updateRelationOfObjectToParentObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\AbstractEntity $parentObject, $parentPropertyName, $sortingPosition = 0)
567  {
568  $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
569  $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
570  if ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
571  $this->attachObjectToParentObjectRelationHasMany($object, $parentObject, $parentPropertyName, $sortingPosition);
572  } elseif ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
573  $this->updateRelationInRelationTable($object, $parentObject, $parentPropertyName, $sortingPosition);
574  }
575  }
576 
587  protected function attachObjectToParentObjectRelationHasMany(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\AbstractEntity $parentObject, $parentPropertyName, $sortingPosition = 0)
588  {
589  $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
590  $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
591  if ($parentColumnMap->getTypeOfRelation() !== \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
592  throw new \TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException(
593  'Parent column relation type is ' . $parentColumnMap->getTypeOfRelation() .
594  ' but should be ' . \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY,
595  1345368105
596  );
597  }
598  $row = array();
599  $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
600  if ($parentKeyFieldName !== null) {
601  $row[$parentKeyFieldName] = $parentObject->getUid();
602  $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
603  if ($parentTableFieldName !== null) {
604  $row[$parentTableFieldName] = $parentDataMap->getTableName();
605  }
606  $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
607  if (is_array($relationTableMatchFields)) {
608  $row = array_merge($relationTableMatchFields, $row);
609  }
610  }
611  $childSortByFieldName = $parentColumnMap->getChildSortByFieldName();
612  if (!empty($childSortByFieldName)) {
613  $row[$childSortByFieldName] = $sortingPosition;
614  }
615  if (!empty($row)) {
616  $this->updateObject($object, $row);
617  }
618  }
619 
628  protected function detachObjectFromParentObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName)
629  {
630  $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
631  $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
632  if ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
633  $row = array();
634  $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
635  if ($parentKeyFieldName !== null) {
636  $row[$parentKeyFieldName] = 0;
637  $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
638  if ($parentTableFieldName !== null) {
639  $row[$parentTableFieldName] = '';
640  }
641  $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
642  if (is_array($relationTableMatchFields) && !empty($relationTableMatchFields)) {
643  $row = array_merge(array_fill_keys(array_keys($relationTableMatchFields), ''), $row);
644  }
645  }
646  $childSortByFieldName = $parentColumnMap->getChildSortByFieldName();
647  if (!empty($childSortByFieldName)) {
648  $row[$childSortByFieldName] = 0;
649  }
650  if (!empty($row)) {
651  $this->updateObject($object, $row);
652  }
653  } elseif ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
654  $this->deleteRelationFromRelationtable($object, $parentObject, $parentPropertyName);
655  }
656  }
657 
666  protected function insertObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject = null, $parentPropertyName = '')
667  {
668  if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject) {
669  $result = $this->getUidOfAlreadyPersistedValueObject($object);
670  if ($result !== false) {
671  $object->_setProperty('uid', (int)$result);
672  return;
673  }
674  }
675  $dataMap = $this->dataMapper->getDataMap(get_class($object));
676  $row = array();
677  $properties = $object->_getProperties();
678  foreach ($properties as $propertyName => $propertyValue) {
679  if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) {
680  continue;
681  }
682  $columnMap = $dataMap->getColumnMap($propertyName);
683  if ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_ONE) {
684  $row[$columnMap->getColumnName()] = 0;
685  } elseif ($columnMap->getTypeOfRelation() !== ColumnMap::RELATION_NONE) {
686  if ($columnMap->getParentKeyFieldName() === null) {
687  // CSV type relation
688  $row[$columnMap->getColumnName()] = '';
689  } else {
690  // MM type relation
691  $row[$columnMap->getColumnName()] = 0;
692  }
693  } elseif ($propertyValue !== null) {
694  $row[$columnMap->getColumnName()] = $this->dataMapper->getPlainValue($propertyValue);
695  }
696  }
697  $this->addCommonFieldsToRow($object, $row);
698  if ($dataMap->getLanguageIdColumnName() !== null && $object->_getProperty('_languageUid') === null) {
699  $row[$dataMap->getLanguageIdColumnName()] = 0;
700  $object->_setProperty('_languageUid', 0);
701  }
702  if ($dataMap->getTranslationOriginColumnName() !== null) {
703  $row[$dataMap->getTranslationOriginColumnName()] = 0;
704  }
705  if ($dataMap->getTranslationOriginDiffSourceName() !== null) {
706  $row[$dataMap->getTranslationOriginDiffSourceName()] = '';
707  }
708  if ($parentObject !== null && $parentPropertyName) {
709  $parentColumnDataMap = $this->dataMapper->getDataMap(get_class($parentObject))->getColumnMap($parentPropertyName);
710  $relationTableMatchFields = $parentColumnDataMap->getRelationTableMatchFields();
711  if (is_array($relationTableMatchFields)) {
712  $row = array_merge($relationTableMatchFields, $row);
713  }
714  if ($parentColumnDataMap->getParentKeyFieldName() !== null) {
715  $row[$parentColumnDataMap->getParentKeyFieldName()] = (int)$parentObject->getUid();
716  }
717  }
718  $uid = $this->storageBackend->addRow($dataMap->getTableName(), $row);
719  $object->_setProperty('uid', (int)$uid);
720  $object->setPid((int)$row['pid']);
721  if ((int)$uid >= 1) {
722  $this->emitAfterInsertObjectSignal($object);
723  }
724  $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
725  if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
726  $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), $uid);
727  }
728  $this->session->registerObject($object, $uid);
729  if ((int)$uid >= 1) {
730  $this->emitEndInsertObjectSignal($object);
731  }
732  }
733 
740  {
741  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterInsertObject', array($object));
742  }
743 
751  {
752  $this->signalSlotDispatcher->dispatch(__CLASS__, 'endInsertObject', array($object));
753  }
754 
761  protected function getUidOfAlreadyPersistedValueObject(\TYPO3\CMS\Extbase\DomainObject\AbstractValueObject $object)
762  {
763  return $this->storageBackend->getUidOfAlreadyPersistedValueObject($object);
764  }
765 
775  protected function insertRelationInRelationtable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $propertyName, $sortingPosition = null)
776  {
777  $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
778  $columnMap = $dataMap->getColumnMap($propertyName);
779  $row = array(
780  $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
781  $columnMap->getChildKeyFieldName() => (int)$object->getUid(),
782  $columnMap->getChildSortByFieldName() => !is_null($sortingPosition) ? (int)$sortingPosition : 0
783  );
784  $relationTableName = $columnMap->getRelationTableName();
785  if ($columnMap->getRelationTablePageIdColumnName() !== null) {
786  $row[$columnMap->getRelationTablePageIdColumnName()] = $this->determineStoragePageIdForNewRecord();
787  }
788  $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
789  if (is_array($relationTableMatchFields)) {
790  $row = array_merge($relationTableMatchFields, $row);
791  }
792  $relationTableInsertFields = $columnMap->getRelationTableInsertFields();
793  if (is_array($relationTableInsertFields)) {
794  $row = array_merge($relationTableInsertFields, $row);
795  }
796  $res = $this->storageBackend->addRow($relationTableName, $row, true);
797  return $res;
798  }
799 
809  protected function updateRelationInRelationTable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $propertyName, $sortingPosition = 0)
810  {
811  $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
812  $columnMap = $dataMap->getColumnMap($propertyName);
813  $row = array(
814  $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
815  $columnMap->getChildKeyFieldName() => (int)$object->getUid(),
816  $columnMap->getChildSortByFieldName() => (int)$sortingPosition
817  );
818  $relationTableName = $columnMap->getRelationTableName();
819  $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
820  if (is_array($relationTableMatchFields)) {
821  $row = array_merge($relationTableMatchFields, $row);
822  }
823  $res = $this->storageBackend->updateRelationTableRow(
824  $relationTableName,
825  $row);
826  return $res;
827  }
828 
836  protected function deleteAllRelationsFromRelationtable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName)
837  {
838  $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
839  $columnMap = $dataMap->getColumnMap($parentPropertyName);
840  $relationTableName = $columnMap->getRelationTableName();
841  $relationMatchFields = array(
842  $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid()
843  );
844  $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
845  if (is_array($relationTableMatchFields)) {
846  $relationMatchFields = array_merge($relationTableMatchFields, $relationMatchFields);
847  }
848  $res = $this->storageBackend->removeRow($relationTableName, $relationMatchFields, false);
849  return $res;
850  }
851 
860  protected function deleteRelationFromRelationtable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $relatedObject, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName)
861  {
862  $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
863  $columnMap = $dataMap->getColumnMap($parentPropertyName);
864  $relationTableName = $columnMap->getRelationTableName();
865  $relationMatchFields = array(
866  $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
867  $columnMap->getChildKeyFieldName() => (int)$relatedObject->getUid()
868  );
869  $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
870  if (is_array($relationTableMatchFields)) {
871  $relationMatchFields = array_merge($relationTableMatchFields, $relationMatchFields);
872  }
873  $res = $this->storageBackend->removeRow($relationTableName, $relationMatchFields, false);
874  return $res;
875  }
876 
885  protected function fetchMaxSortingFromParentTable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName)
886  {
887  $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
888  $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
889  if ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
890  $tableName = $parentColumnMap->getChildTableName();
891  $sortByFieldName = $parentColumnMap->getChildSortByFieldName();
892 
893  if (empty($sortByFieldName)) {
894  return false;
895  }
896  $matchFields = array();
897  $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
898  if ($parentKeyFieldName !== null) {
899  $matchFields[$parentKeyFieldName] = $parentObject->getUid();
900  $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
901  if ($parentTableFieldName !== null) {
902  $matchFields[$parentTableFieldName] = $parentDataMap->getTableName();
903  }
904  }
905 
906  if (empty($matchFields)) {
907  return false;
908  }
909  } elseif ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
910  $tableName = $parentColumnMap->getRelationTableName();
911  $sortByFieldName = $parentColumnMap->getChildSortByFieldName();
912 
913  $matchFields = array(
914  $parentColumnMap->getParentKeyFieldName() => (int)$parentObject->getUid()
915  );
916 
917  $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
918  if (is_array($relationTableMatchFields)) {
919  $matchFields = array_merge($relationTableMatchFields, $matchFields);
920  }
921  } else {
922  throw new \TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException('Unexpected parent column relation type:' . $parentColumnMap->getTypeOfRelation(), 1345368106);
923  }
924 
925  $result = $this->storageBackend->getMaxValueFromTable(
926  $tableName,
927  $matchFields,
928  $sortByFieldName);
929  return $result;
930  }
931 
939  protected function updateObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, array $row)
940  {
941  $dataMap = $this->dataMapper->getDataMap(get_class($object));
942  $this->addCommonFieldsToRow($object, $row);
943  $row['uid'] = $object->getUid();
944  if ($dataMap->getLanguageIdColumnName() !== null) {
945  $row[$dataMap->getLanguageIdColumnName()] = (int)$object->_getProperty('_languageUid');
946  if ($object->_getProperty('_localizedUid') !== null) {
947  $row['uid'] = $object->_getProperty('_localizedUid');
948  }
949  }
950  $res = $this->storageBackend->updateRow($dataMap->getTableName(), $row);
951  if ($res === true) {
952  $this->emitAfterUpdateObjectSignal($object);
953  }
954  $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
955  if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
956  $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), $row['uid']);
957  }
958  return $res;
959  }
960 
967  {
968  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterUpdateObject', array($object));
969  }
970 
977  {
978  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterPersistObject', array($object));
979  }
980 
988  protected function addCommonFieldsToRow(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, array &$row)
989  {
990  $dataMap = $this->dataMapper->getDataMap(get_class($object));
991  $this->addCommonDateFieldsToRow($object, $row);
992  if ($dataMap->getRecordTypeColumnName() !== null && $dataMap->getRecordType() !== null) {
993  $row[$dataMap->getRecordTypeColumnName()] = $dataMap->getRecordType();
994  }
995  if ($object->_isNew() && !isset($row['pid'])) {
996  $row['pid'] = $this->determineStoragePageIdForNewRecord($object);
997  }
998  }
999 
1007  protected function addCommonDateFieldsToRow(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, array &$row)
1008  {
1009  $dataMap = $this->dataMapper->getDataMap(get_class($object));
1010  if ($object->_isNew() && $dataMap->getCreationDateColumnName() !== null) {
1011  $row[$dataMap->getCreationDateColumnName()] = $GLOBALS['EXEC_TIME'];
1012  }
1013  if ($dataMap->getModificationDateColumnName() !== null) {
1014  $row[$dataMap->getModificationDateColumnName()] = $GLOBALS['EXEC_TIME'];
1015  }
1016  }
1017 
1023  protected function processDeletedObjects()
1024  {
1025  foreach ($this->deletedEntities as $entity) {
1026  if ($this->session->hasObject($entity)) {
1027  $this->removeEntity($entity);
1028  $this->session->unregisterReconstitutedEntity($entity);
1029  $this->session->unregisterObject($entity);
1030  }
1031  }
1032  $this->deletedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
1033  }
1034 
1042  protected function removeEntity(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, $markAsDeleted = true)
1043  {
1044  $dataMap = $this->dataMapper->getDataMap(get_class($object));
1045  $tableName = $dataMap->getTableName();
1046  if ($markAsDeleted === true && $dataMap->getDeletedFlagColumnName() !== null) {
1047  $deletedColumnName = $dataMap->getDeletedFlagColumnName();
1048  $row = array(
1049  'uid' => $object->getUid(),
1050  $deletedColumnName => 1
1051  );
1052  $this->addCommonDateFieldsToRow($object, $row);
1053  $res = $this->storageBackend->updateRow($tableName, $row);
1054  } else {
1055  $res = $this->storageBackend->removeRow($tableName, array('uid' => $object->getUid()));
1056  }
1057  if ($res === true) {
1058  $this->emitAfterRemoveObjectSignal($object);
1059  }
1060  $this->removeRelatedObjects($object);
1061  $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
1062  if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
1063  $this->referenceIndex->updateRefIndexTable($tableName, $object->getUid());
1064  }
1065  }
1066 
1073  {
1074  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterRemoveObject', array($object));
1075  }
1076 
1083  protected function removeRelatedObjects(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object)
1084  {
1085  $className = get_class($object);
1086  $dataMap = $this->dataMapper->getDataMap($className);
1087  $classSchema = $this->reflectionService->getClassSchema($className);
1088  $properties = $object->_getProperties();
1089  foreach ($properties as $propertyName => $propertyValue) {
1090  $columnMap = $dataMap->getColumnMap($propertyName);
1091  $propertyMetaData = $classSchema->getProperty($propertyName);
1092  if ($propertyMetaData['cascade'] === 'remove') {
1093  if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
1094  foreach ($propertyValue as $containedObject) {
1095  $this->removeEntity($containedObject);
1096  }
1097  } elseif ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) {
1098  $this->removeEntity($propertyValue);
1099  }
1100  }
1101  }
1102  }
1103 
1115  protected function determineStoragePageIdForNewRecord(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object = null)
1116  {
1117  $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
1118  if ($object !== null) {
1119  if (\TYPO3\CMS\Extbase\Reflection\ObjectAccess::isPropertyGettable($object, 'pid')) {
1120  $pid = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getProperty($object, 'pid');
1121  if (isset($pid)) {
1122  return (int)$pid;
1123  }
1124  }
1125  $className = get_class($object);
1126  if (isset($frameworkConfiguration['persistence']['classes'][$className]) && !empty($frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'])) {
1127  return (int)$frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'];
1128  }
1129  }
1130  $storagePidList = \TYPO3\CMS\Core\Utility\GeneralUtility::intExplode(',', $frameworkConfiguration['persistence']['storagePid']);
1131  return (int)$storagePidList[0];
1132  }
1133 }