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