[BUGFIX] Repository replace() does not replace unpersisted objects
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Repository.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
6 * All rights reserved
7 *
8 * This class is a backport of the corresponding class of FLOW3.
9 * All credits go to the v5 team.
10 *
11 * This script is part of the TYPO3 project. The TYPO3 project is
12 * free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * The GNU General Public License can be found at
18 * http://www.gnu.org/copyleft/gpl.html.
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * The base repository - will usually be extended by a more concrete repository.
30 *
31 * @package Extbase
32 * @subpackage Persistence
33 * @api
34 */
35 class Tx_Extbase_Persistence_Repository implements Tx_Extbase_Persistence_RepositoryInterface, t3lib_Singleton {
36
37 /**
38 * @var Tx_Extbase_Persistence_IdentityMap
39 **/
40 protected $identityMap;
41
42 /**
43 * Objects of this repository
44 *
45 * @var Tx_Extbase_Persistence_ObjectStorage
46 */
47 protected $addedObjects;
48
49 /**
50 * Objects removed but not found in $this->addedObjects at removal time
51 *
52 * @var Tx_Extbase_Persistence_ObjectStorage
53 */
54 protected $removedObjects;
55
56 /**
57 * @var Tx_Extbase_Persistence_QueryFactoryInterface
58 */
59 protected $queryFactory;
60
61 /**
62 * @var Tx_Extbase_Persistence_ManagerInterface
63 */
64 protected $persistenceManager;
65
66 /**
67 * @var Tx_Extbase_Object_ObjectManagerInterface
68 */
69 protected $objectManager;
70
71 /**
72 * @var string
73 */
74 protected $objectType;
75
76 /**
77 * @var array
78 */
79 protected $defaultOrderings = array();
80
81 /**
82 * @var Tx_Extbase_Persistence_QuerySettingsInterface
83 */
84 protected $defaultQuerySettings = NULL;
85
86 /**
87 * Constructs a new Repository
88 *
89 * @param Tx_Extbase_Object_ObjectManagerInterface $objectManager
90 */
91 public function __construct(Tx_Extbase_Object_ObjectManagerInterface $objectManager = NULL) {
92 $this->addedObjects = new Tx_Extbase_Persistence_ObjectStorage();
93 $this->removedObjects = new Tx_Extbase_Persistence_ObjectStorage();
94 $this->objectType = preg_replace(array('/_Repository_(?!.*_Repository_)/', '/Repository$/'), array('_Model_', ''), $this->getRepositoryClassName());
95
96 if ($objectManager === NULL) {
97 // Legacy creation, in case the object manager is NOT injected
98 // If ObjectManager IS there, then all properties are automatically injected
99 $this->objectManager = t3lib_div::makeInstance('Tx_Extbase_Object_ObjectManager');
100 $this->injectIdentityMap($this->objectManager->get('Tx_Extbase_Persistence_IdentityMap'));
101 $this->injectQueryFactory($this->objectManager->get('Tx_Extbase_Persistence_QueryFactory'));
102 $this->injectPersistenceManager($this->objectManager->get('Tx_Extbase_Persistence_Manager'));
103 } else {
104 $this->objectManager = $objectManager;
105 }
106 }
107
108 /**
109 * @param Tx_Extbase_Persistence_IdentityMap $identityMap
110 * @return void
111 */
112 public function injectIdentityMap(Tx_Extbase_Persistence_IdentityMap $identityMap) {
113 $this->identityMap = $identityMap;
114 }
115
116 /**
117 * @param Tx_Extbase_Persistence_QueryFactory $queryFactory
118 * @return void
119 */
120 public function injectQueryFactory(Tx_Extbase_Persistence_QueryFactory $queryFactory) {
121 $this->queryFactory = $queryFactory;
122 }
123
124 /**
125 * @param Tx_Extbase_Persistence_ManagerInterface $persistenceManager
126 * @return void
127 */
128 public function injectPersistenceManager(Tx_Extbase_Persistence_ManagerInterface $persistenceManager) {
129 $this->persistenceManager = $persistenceManager;
130 $this->persistenceManager->registerRepositoryClassName($this->getRepositoryClassName());
131 }
132
133 /**
134 * Adds an object to this repository
135 *
136 * @param object $object The object to add
137 * @return void
138 * @api
139 */
140 public function add($object) {
141 if (!($object instanceof $this->objectType)) {
142 throw new Tx_Extbase_Persistence_Exception_IllegalObjectType('The object given to add() was not of the type (' . $this->objectType . ') this repository manages.', 1248363335);
143 }
144
145 $this->addedObjects->attach($object);
146
147 if ($this->removedObjects->contains($object)) {
148 $this->removedObjects->detach($object);
149 }
150 }
151
152 /**
153 * Removes an object from this repository.
154 *
155 * @param object $object The object to remove
156 * @return void
157 * @api
158 */
159 public function remove($object) {
160 if (!($object instanceof $this->objectType)) {
161 throw new Tx_Extbase_Persistence_Exception_IllegalObjectType('The object given to remove() was not of the type (' . $this->objectType . ') this repository manages.', 1248363335);
162 }
163
164 if ($this->addedObjects->contains($object)) {
165 $this->addedObjects->detach($object);
166 }
167
168 if (!$object->_isNew()) {
169 $this->removedObjects->attach($object);
170 }
171 }
172
173 /**
174 * Replaces an object by another.
175 *
176 * @param object $existingObject The existing object
177 * @param object $newObject The new object
178 * @return void
179 * @api
180 */
181 public function replace($existingObject, $newObject) {
182 if (!($existingObject instanceof $this->objectType)) {
183 throw new Tx_Extbase_Persistence_Exception_IllegalObjectType('The existing object given to replace was not of the type (' . $this->objectType . ') this repository manages.', 1248363434);
184 }
185 if (!($newObject instanceof $this->objectType)) {
186 throw new Tx_Extbase_Persistence_Exception_IllegalObjectType('The new object given to replace was not of the type (' . $this->objectType . ') this repository manages.', 1248363439);
187 }
188
189 $backend = $this->persistenceManager->getBackend();
190 $session = $this->persistenceManager->getSession();
191 $uuid = $backend->getIdentifierByObject($existingObject);
192 if ($uuid !== NULL) {
193 $backend->replaceObject($existingObject, $newObject);
194 $session->unregisterReconstitutedObject($existingObject);
195 $session->registerReconstitutedObject($newObject);
196
197 if ($this->removedObjects->contains($existingObject)) {
198 $this->removedObjects->detach($existingObject);
199 $this->removedObjects->attach($newObject);
200 } elseif ($this->addedObjects->contains($existingObject)) {
201 $this->addedObjects->detach($existingObject);
202 $this->addedObjects->attach($newObject);
203 }
204 } elseif ($this->addedObjects->contains($existingObject)) {
205 $this->addedObjects->detach($existingObject);
206 $this->addedObjects->attach($newObject);
207 } else {
208 throw new Tx_Extbase_Persistence_Exception_UnknownObject('The "existing object" is unknown to the persistence backend.', 1238068475);
209 }
210
211 }
212
213 /**
214 * Replaces an existing object with the same identifier by the given object
215 *
216 * @param object $modifiedObject The modified object
217 * @api
218 */
219 public function update($modifiedObject) {
220 if (!($modifiedObject instanceof $this->objectType)) {
221 throw new Tx_Extbase_Persistence_Exception_IllegalObjectType('The modified object given to update() was not of the type (' . $this->objectType . ') this repository manages.', 1249479625);
222 }
223
224 $uid = $modifiedObject->getUid();
225 if ($uid !== NULL) {
226 $existingObject = $this->findByUid($uid);
227 $this->replace($existingObject, $modifiedObject);
228 } else {
229 throw new Tx_Extbase_Persistence_Exception_UnknownObject('The "modified object" is does not have an existing counterpart in this repository.', 1249479819);
230 }
231 }
232
233 /**
234 * Returns all addedObjects that have been added to this repository with add().
235 *
236 * This is a service method for the persistence manager to get all addedObjects
237 * added to the repository. Those are only objects *added*, not objects
238 * fetched from the underlying storage.
239 *
240 * @return Tx_Extbase_Persistence_ObjectStorage the objects
241 */
242 public function getAddedObjects() {
243 return $this->addedObjects;
244 }
245
246 /**
247 * Returns an Tx_Extbase_Persistence_ObjectStorage with objects remove()d from the repository
248 * that had been persisted to the storage layer before.
249 *
250 * @return Tx_Extbase_Persistence_ObjectStorage the objects
251 */
252 public function getRemovedObjects() {
253 return $this->removedObjects;
254 }
255
256 /**
257 * Returns all objects of this repository
258 *
259 * @return array An array of objects, empty if no objects found
260 * @api
261 */
262 public function findAll() {
263 $result = $this->createQuery()->execute();
264 return $result;
265 }
266
267 /**
268 * Returns the total number objects of this repository.
269 *
270 * @return integer The object count
271 * @api
272 */
273 public function countAll() {
274 return $this->createQuery()->execute()->count();
275 }
276
277 /**
278 * Removes all objects of this repository as if remove() was called for
279 * all of them.
280 *
281 * @return void
282 * @api
283 */
284 public function removeAll() {
285 $this->addedObjects = new Tx_Extbase_Persistence_ObjectStorage();
286 foreach ($this->findAll() as $object) {
287 $this->remove($object);
288 }
289 }
290
291 /**
292 * Finds an object matching the given identifier.
293 *
294 * @param int $uid The identifier of the object to find
295 * @return object The matching object if found, otherwise NULL
296 * @api
297 */
298 public function findByUid($uid) {
299 if ($this->identityMap->hasIdentifier($uid, $this->objectType)) {
300 $object = $this->identityMap->getObjectByIdentifier($uid, $this->objectType);
301 } else {
302 $query = $this->createQuery();
303 $query->getQuerySettings()->setRespectSysLanguage(FALSE);
304 $query->getQuerySettings()->setRespectStoragePage(FALSE);
305 $object = $query
306 ->matching(
307 $query->equals('uid', $uid)
308 )
309 ->execute()
310 ->getFirst();
311 }
312 return $object;
313 }
314
315 /**
316 * Sets the property names to order the result by per default.
317 * Expected like this:
318 * array(
319 * 'foo' => Tx_Extbase_Persistence_QueryInterface::ORDER_ASCENDING,
320 * 'bar' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING
321 * )
322 *
323 * @param array $defaultOrderings The property names to order by
324 * @return void
325 * @api
326 */
327 public function setDefaultOrderings(array $defaultOrderings) {
328 $this->defaultOrderings = $defaultOrderings;
329 }
330
331 /**
332 * Sets the default query settings to be used in this repository
333 *
334 * @param Tx_Extbase_Persistence_QuerySettingsInterface $defaultQuerySettings The query settings to be used by default
335 * @return void
336 * @api
337 */
338 public function setDefaultQuerySettings(Tx_Extbase_Persistence_QuerySettingsInterface $defaultQuerySettings) {
339 $this->defaultQuerySettings = $defaultQuerySettings;
340 }
341
342 /**
343 * Returns a query for objects of this repository
344 *
345 * @return Tx_Extbase_Persistence_QueryInterface
346 * @api
347 */
348 public function createQuery() {
349 $query = $this->queryFactory->create($this->objectType);
350 if ($this->defaultOrderings !== array()) {
351 $query->setOrderings($this->defaultOrderings);
352 }
353 if ($this->defaultQuerySettings !== NULL) {
354 $query->setQuerySettings(clone $this->defaultQuerySettings);
355 }
356 return $query;
357 }
358
359 /**
360 * Dispatches magic methods (findBy[Property]())
361 *
362 * @param string $methodName The name of the magic method
363 * @param string $arguments The arguments of the magic method
364 * @throws Tx_Extbase_Persistence_Exception_UnsupportedMethod
365 * @return void
366 * @api
367 */
368 public function __call($methodName, $arguments) {
369 if (substr($methodName, 0, 6) === 'findBy' && strlen($methodName) > 7) {
370 $propertyName = strtolower(substr(substr($methodName, 6), 0, 1) ) . substr(substr($methodName, 6), 1);
371 $query = $this->createQuery();
372 $result = $query->matching($query->equals($propertyName, $arguments[0]))
373 ->execute();
374 return $result;
375 } elseif (substr($methodName, 0, 9) === 'findOneBy' && strlen($methodName) > 10) {
376 $propertyName = strtolower(substr(substr($methodName, 9), 0, 1) ) . substr(substr($methodName, 9), 1);
377 $query = $this->createQuery();
378 $object = $query->matching($query->equals($propertyName, $arguments[0]))
379 ->setLimit(1)
380 ->execute()
381 ->getFirst();
382 return $object;
383 } elseif (substr($methodName, 0, 7) === 'countBy' && strlen($methodName) > 8) {
384 $propertyName = strtolower(substr(substr($methodName, 7), 0, 1) ) . substr(substr($methodName, 7), 1);
385 $query = $this->createQuery();
386 $result = $query->matching($query->equals($propertyName, $arguments[0]))
387 ->execute()
388 ->count();
389 return $result;
390 }
391 throw new Tx_Extbase_Persistence_Exception_UnsupportedMethod('The method "' . $methodName . '" is not supported by the repository.', 1233180480);
392 }
393
394 /**
395 * Returns the class name of this class.
396 *
397 * @return string Class name of the repository.
398 */
399 protected function getRepositoryClassName() {
400 return get_class($this);
401 }
402
403 }
404 ?>