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 16namespace TYPO3\CMS\Extbase\Persistence\Generic; 17 18use TYPO3\CMS\Core\SingletonInterface; 19use TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException; 20use TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException; 21use TYPO3\CMS\Extbase\Persistence\ObjectStorage; 22use TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface; 23use TYPO3\CMS\Extbase\Persistence\QueryInterface; 24 25/** 26 * The Extbase Persistence Manager 27 */ 28class PersistenceManager implements PersistenceManagerInterface, SingletonInterface 29{ 30 /** 31 * @var array 32 */ 33 protected $newObjects = []; 34 35 /** 36 * @var ObjectStorage 37 */ 38 protected $changedObjects; 39 40 /** 41 * @var ObjectStorage 42 */ 43 protected $addedObjects; 44 45 /** 46 * @var ObjectStorage 47 */ 48 protected $removedObjects; 49 50 /** 51 * @var \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface 52 */ 53 protected $queryFactory; 54 55 /** 56 * @var \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface 57 */ 58 protected $backend; 59 60 /** 61 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Session 62 */ 63 protected $persistenceSession; 64 65 /** 66 * Create new instance 67 * @internal only to be used within Extbase, not part of TYPO3 Core API. 68 * @param QueryFactoryInterface $queryFactory 69 * @param BackendInterface $backend 70 * @param Session $persistenceSession 71 */ 72 public function __construct( 73 QueryFactoryInterface $queryFactory, 74 BackendInterface $backend, 75 Session $persistenceSession 76 ) { 77 $this->queryFactory = $queryFactory; 78 $this->backend = $backend; 79 $this->persistenceSession = $persistenceSession; 80 81 $this->addedObjects = new ObjectStorage(); 82 $this->removedObjects = new ObjectStorage(); 83 $this->changedObjects = new ObjectStorage(); 84 } 85 86 /** 87 * Registers a repository 88 * 89 * @param string $className The class name of the repository to be registered 90 * @internal only to be used within Extbase, not part of TYPO3 Core API. 91 */ 92 public function registerRepositoryClassName($className) 93 { 94 } 95 96 /** 97 * Returns the number of records matching the query. 98 * 99 * @param QueryInterface $query 100 * @return int 101 */ 102 public function getObjectCountByQuery(QueryInterface $query) 103 { 104 return $this->backend->getObjectCountByQuery($query); 105 } 106 107 /** 108 * Returns the object data matching the $query. 109 * 110 * @param QueryInterface $query 111 * @return array 112 */ 113 public function getObjectDataByQuery(QueryInterface $query) 114 { 115 return $this->backend->getObjectDataByQuery($query); 116 } 117 118 /** 119 * Returns the (internal) identifier for the object, if it is known to the 120 * backend. Otherwise NULL is returned. 121 * 122 * Note: this returns an identifier even if the object has not been 123 * persisted in case of AOP-managed entities. Use isNewObject() if you need 124 * to distinguish those cases. 125 * 126 * @param object $object 127 * @return mixed The identifier for the object if it is known, or NULL 128 */ 129 public function getIdentifierByObject($object) 130 { 131 return $this->backend->getIdentifierByObject($object); 132 } 133 134 /** 135 * Returns the object with the (internal) identifier, if it is known to the 136 * backend. Otherwise NULL is returned. 137 * 138 * @param mixed $identifier 139 * @param string $objectType 140 * @param bool $useLazyLoading Set to TRUE if you want to use lazy loading for this object 141 * @return object The object for the identifier if it is known, or NULL 142 */ 143 public function getObjectByIdentifier($identifier, $objectType = null, $useLazyLoading = false) 144 { 145 if (isset($this->newObjects[$identifier])) { 146 return $this->newObjects[$identifier]; 147 } 148 if ($this->persistenceSession->hasIdentifier($identifier, $objectType)) { 149 return $this->persistenceSession->getObjectByIdentifier($identifier, $objectType); 150 } 151 return $this->backend->getObjectByIdentifier($identifier, $objectType); 152 } 153 154 /** 155 * Commits new objects and changes to objects in the current persistence 156 * session into the backend. 157 */ 158 public function persistAll() 159 { 160 // hand in only aggregate roots, leaving handling of subobjects to 161 // the underlying storage layer 162 // reconstituted entities must be fetched from the session and checked 163 // for changes by the underlying backend as well! 164 $this->backend->setAggregateRootObjects($this->addedObjects); 165 $this->backend->setChangedEntities($this->changedObjects); 166 $this->backend->setDeletedEntities($this->removedObjects); 167 $this->backend->commit(); 168 169 $this->addedObjects = new ObjectStorage(); 170 $this->removedObjects = new ObjectStorage(); 171 $this->changedObjects = new ObjectStorage(); 172 } 173 174 /** 175 * Return a query object for the given type. 176 * 177 * @param string $type 178 * @return QueryInterface 179 * @internal only to be used within Extbase, not part of TYPO3 Core API. 180 */ 181 public function createQueryForType($type) 182 { 183 return $this->queryFactory->create($type); 184 } 185 186 /** 187 * Adds an object to the persistence. 188 * 189 * @param object $object The object to add 190 */ 191 public function add($object) 192 { 193 $this->addedObjects->attach($object); 194 $this->removedObjects->detach($object); 195 } 196 197 /** 198 * Removes an object to the persistence. 199 * 200 * @param object $object The object to remove 201 */ 202 public function remove($object) 203 { 204 if ($this->addedObjects->contains($object)) { 205 $this->addedObjects->detach($object); 206 } else { 207 $this->removedObjects->attach($object); 208 } 209 } 210 211 /** 212 * Update an object in the persistence. 213 * 214 * @param object $object The modified object 215 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException 216 */ 217 public function update($object) 218 { 219 if ($this->isNewObject($object)) { 220 throw new UnknownObjectException('The object of type "' . get_class($object) . '" given to update must be persisted already, but is new.', 1249479819); 221 } 222 $this->changedObjects->attach($object); 223 } 224 225 /** 226 * Injects the Extbase settings, called by Extbase. 227 * 228 * @param array $settings 229 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException 230 */ 231 public function injectSettings(array $settings) 232 { 233 throw new NotImplementedException(__METHOD__, 1476108078); 234 } 235 236 /** 237 * Initializes the persistence manager, called by Extbase. 238 * @internal only to be used within Extbase, not part of TYPO3 Core API. 239 */ 240 public function initializeObject() 241 { 242 $this->backend->setPersistenceManager($this); 243 } 244 245 /** 246 * Clears the in-memory state of the persistence. 247 * 248 * Managed instances become detached, any fetches will 249 * return data directly from the persistence "backend". 250 * 251 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException 252 * @internal only to be used within Extbase, not part of TYPO3 Core API. 253 */ 254 public function clearState() 255 { 256 $this->newObjects = []; 257 $this->addedObjects = new ObjectStorage(); 258 $this->removedObjects = new ObjectStorage(); 259 $this->changedObjects = new ObjectStorage(); 260 $this->persistenceSession->destroy(); 261 } 262 263 /** 264 * Checks if the given object has ever been persisted. 265 * 266 * @param object $object The object to check 267 * @return bool TRUE if the object is new, FALSE if the object exists in the persistence session 268 */ 269 public function isNewObject($object) 270 { 271 return $this->persistenceSession->hasObject($object) === false; 272 } 273 274 /** 275 * Registers an object which has been created or cloned during this request. 276 * 277 * A "new" object does not necessarily 278 * have to be known by any repository or be persisted in the end. 279 * 280 * Objects registered with this method must be known to the getObjectByIdentifier() 281 * method. 282 * 283 * @param object $object The new object to register 284 * @internal only to be used within Extbase, not part of TYPO3 Core API. 285 */ 286 public function registerNewObject($object) 287 { 288 $identifier = $this->getIdentifierByObject($object); 289 $this->newObjects[$identifier] = $object; 290 } 291 292 /** 293 * Converts the given object into an array containing the identity of the domain object. 294 * 295 * @param object $object The object to be converted 296 * @throws Exception\NotImplementedException 297 */ 298 public function convertObjectToIdentityArray($object) 299 { 300 throw new NotImplementedException(__METHOD__, 1476108103); 301 } 302 303 /** 304 * Recursively iterates through the given array and turns objects 305 * into arrays containing the identity of the domain object. 306 * 307 * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException 308 * @param array $array The array to be iterated over 309 * @see convertObjectToIdentityArray() 310 */ 311 public function convertObjectsToIdentityArrays(array $array) 312 { 313 throw new NotImplementedException(__METHOD__, 1476108111); 314 } 315 316 /** 317 * Tear down the persistence 318 * 319 * This method is called in functional tests to reset the storage between tests. 320 * The implementation is optional and depends on the underlying persistence backend. 321 * @internal only to be used within Extbase, not part of TYPO3 Core API. 322 */ 323 public function tearDown(): void 324 { 325 if (method_exists($this->backend, 'tearDown')) { 326 $this->backend->tearDown(); 327 } 328 } 329} 330