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