[BUGFIX] Fix persisting of lazy object storages
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Generic / LazyObjectStorage.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Persistence\Generic;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2010-2013 Extbase Team (http://forge.typo3.org/projects/typo3v4-mvc)
8 * Extbase is a backport of TYPO3 Flow. All credits go to the TYPO3 Flow team.
9 * All rights reserved
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 * A copy is found in the textfile GPL.txt and important notices to the license
20 * from the author is found in LICENSE.txt distributed with these scripts.
21 *
22 *
23 * This script is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * This copyright notice MUST APPEAR in all copies of the script!
29 ***************************************************************/
30 /**
31 * A proxy that can replace any object and replaces itself in it's parent on
32 * first access (call, get, set, isset, unset).
33 */
34 class LazyObjectStorage extends \TYPO3\CMS\Extbase\Persistence\ObjectStorage implements \TYPO3\CMS\Extbase\Persistence\Generic\LoadingStrategyInterface {
35
36 /**
37 * This field is only needed to make debugging easier:
38 * If you call current() on a class that implements Iterator, PHP will return the first field of the object
39 * instead of calling the current() method of the interface.
40 * We use this unusual behavior of PHP to return the warning below in this case.
41 *
42 * @var string
43 */
44 private $warning = 'You should never see this warning. If you do, you probably used PHP array functions like current() on the TYPO3\\CMS\\Extbase\\Persistence\\Generic\\LazyObjectStorage. To retrieve the first result, you can use the rewind() and current() methods.';
45
46 /**
47 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper
48 */
49 protected $dataMapper;
50
51 /**
52 * The object this property is contained in.
53 *
54 * @var \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface
55 */
56 protected $parentObject;
57
58 /**
59 * The name of the property represented by this proxy.
60 *
61 * @var string
62 */
63 protected $propertyName;
64
65 /**
66 * The raw field value.
67 *
68 * @var mixed
69 */
70 protected $fieldValue;
71
72 /**
73 * @var bool
74 */
75 protected $isInitialized = FALSE;
76
77 /**
78 * Returns the state of the initialization
79 *
80 * @return boolean
81 */
82 public function isInitialized() {
83 return $this->isInitialized;
84 }
85
86 /**
87 * Constructs this proxy instance.
88 *
89 * @param object $parentObject The object instance this proxy is part of
90 * @param string $propertyName The name of the proxied property in it's parent
91 * @param mixed $fieldValue The raw field value.
92 */
93 public function __construct($parentObject, $propertyName, $fieldValue) {
94 $this->parentObject = $parentObject;
95 $this->propertyName = $propertyName;
96 $this->fieldValue = $fieldValue;
97 reset($this->storage);
98 }
99
100 /**
101 * Injects the DataMapper to map nodes to objects
102 *
103 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper
104 * @return void
105 */
106 public function injectDataMapper(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper) {
107 $this->dataMapper = $dataMapper;
108 }
109
110 /**
111 * This is a function lazy load implementation.
112 *
113 * @return void
114 */
115 protected function initialize() {
116 if (!$this->isInitialized) {
117 $this->isInitialized = TRUE;
118 $objects = $this->dataMapper->fetchRelated($this->parentObject, $this->propertyName, $this->fieldValue, FALSE);
119 foreach ($objects as $object) {
120 parent::attach($object);
121 }
122 $this->_memorizeCleanState();
123 if (!$this->isStorageAlreadyMemorizedInParentCleanState()) {
124 $this->parentObject->_memorizeCleanState($this->propertyName);
125 }
126 }
127 }
128
129 /**
130 * @return bool
131 */
132 protected function isStorageAlreadyMemorizedInParentCleanState() {
133 return $this->parentObject->_getCleanProperty($this->propertyName) === $this;
134 }
135
136 // Delegation to the ObjectStorage methods below
137 /**
138 * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $storage
139 *
140 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::addAll
141 */
142 public function addAll($storage) {
143 $this->initialize();
144 parent::addAll($storage);
145 }
146
147 /**
148 * @param object $object The object to add.
149 * @param mixed $data The data to associate with the object.
150 *
151 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::attach
152 */
153 public function attach($object, $data = NULL) {
154 $this->initialize();
155 parent::attach($object, $data);
156 }
157
158 /**
159 * @param object $object The object to look for.
160 * @return boolean
161 *
162 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::contains
163 */
164 public function contains($object) {
165 $this->initialize();
166 return parent::contains($object);
167 }
168
169 /**
170 * Counts the elements in the storage array
171 *
172 * @throws Exception
173 * @return int The number of elements in the ObjectStorage
174 */
175 public function count() {
176 $columnMap = $this->dataMapper->getDataMap(get_class($this->parentObject))->getColumnMap($this->propertyName);
177 $numberOfElements = NULL;
178 if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
179 $numberOfElements = $this->dataMapper->countRelated($this->parentObject, $this->propertyName, $this->fieldValue);
180 } else {
181 $this->initialize();
182 $numberOfElements = count($this->storage);
183 }
184 if (is_null($numberOfElements)) {
185 throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception('The number of elements could not be determined.', 1252514486);
186 }
187 return $numberOfElements;
188 }
189
190 /**
191 * @return object The object at the current iterator position.
192 *
193 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::current
194 */
195 public function current() {
196 $this->initialize();
197 return parent::current();
198 }
199
200 /**
201 * @param object $object The object to remove.
202 *
203 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::detach
204 */
205 public function detach($object) {
206 $this->initialize();
207 parent::detach($object);
208 }
209
210 /**
211 * @return string The index corresponding to the position of the iterator.
212 *
213 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::key
214 */
215 public function key() {
216 $this->initialize();
217 return parent::key();
218 }
219
220 /**
221 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::next
222 */
223 public function next() {
224 $this->initialize();
225 parent::next();
226 }
227
228 /**
229 * @param object $object The object to look for.
230 * @return boolean
231 *
232 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::offsetExists
233 */
234 public function offsetExists($object) {
235 $this->initialize();
236 return parent::offsetExists($object);
237 }
238
239 /**
240 * @param object $object The object to look for.
241 * @return mixed
242 *
243 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::offsetGet
244 */
245 public function offsetGet($object) {
246 $this->initialize();
247 return parent::offsetGet($object);
248 }
249
250 /**
251 * @param object $object The object to add.
252 * @param mixed $info The data to associate with the object.
253 *
254 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::offsetSet
255 */
256 public function offsetSet($object, $info) {
257 $this->initialize();
258 parent::offsetSet($object, $info);
259 }
260
261 /**
262 * @param object $object The object to remove.
263 * @return void
264 *
265 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::offsetUnset
266 */
267 public function offsetUnset($object) {
268 $this->initialize();
269 parent::offsetUnset($object);
270 }
271
272 /**
273 * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $storage The storage containing the elements to remove.
274 * @return void
275 *
276 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::removeAll
277 */
278 public function removeAll($storage) {
279 $this->initialize();
280 parent::removeAll($storage);
281 }
282
283 /**
284 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::rewind
285 */
286 public function rewind() {
287 $this->initialize();
288 parent::rewind();
289 }
290
291 /**
292 * @return boolean
293 *
294 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::valid
295 */
296 public function valid() {
297 $this->initialize();
298 return parent::valid();
299 }
300
301 /**
302 * @return array
303 *
304 * @see \TYPO3\CMS\Extbase\Persistence\ObjectStorage::toArray
305 */
306 public function toArray() {
307 $this->initialize();
308 return parent::toArray();
309 }
310
311 /**
312 * @param mixed $object
313 * @return integer|NULL
314 */
315 public function getPosition($object) {
316 $this->initialize();
317 return parent::getPosition($object);
318 }
319 }
320
321 ?>