[BUGFIX] Consider property clean if lazy loaded proxy is untouched
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Generic / LazyLoadingProxy.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Persistence\Generic;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
18
19 /**
20 * A proxy that can replace any object and replaces itself in it's parent on
21 * first access (call, get, set, isset, unset).
22 */
23 class LazyLoadingProxy implements \Iterator, \TYPO3\CMS\Extbase\Persistence\Generic\LoadingStrategyInterface
24 {
25 /**
26 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper
27 */
28 protected $dataMapper;
29
30 /**
31 * The object this property is contained in.
32 *
33 * @var DomainObjectInterface
34 */
35 private $parentObject;
36
37 /**
38 * The name of the property represented by this proxy.
39 *
40 * @var string
41 */
42 private $propertyName;
43
44 /**
45 * The raw field value.
46 *
47 * @var mixed
48 */
49 private $fieldValue;
50
51 /**
52 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper
53 */
54 public function injectDataMapper(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper $dataMapper)
55 {
56 $this->dataMapper = $dataMapper;
57 }
58
59 /**
60 * Constructs this proxy instance.
61 *
62 * @param DomainObjectInterface $parentObject The object instance this proxy is part of
63 * @param string $propertyName The name of the proxied property in it's parent
64 * @param mixed $fieldValue The raw field value.
65 */
66 public function __construct($parentObject, $propertyName, $fieldValue)
67 {
68 $this->parentObject = $parentObject;
69 $this->propertyName = $propertyName;
70 $this->fieldValue = $fieldValue;
71 }
72
73 /**
74 * Populate this proxy by asking the $population closure.
75 *
76 * @return object The instance (hopefully) returned
77 */
78 public function _loadRealInstance()
79 {
80 // this check safeguards against a proxy being activated multiple times
81 // usually that does not happen, but if the proxy is held from outside
82 // its parent ... the result would be weird.
83 if ($this->parentObject->_getProperty($this->propertyName) instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
84 $objects = $this->dataMapper->fetchRelated($this->parentObject, $this->propertyName, $this->fieldValue, false, false);
85 $propertyValue = $this->dataMapper->mapResultToPropertyValue($this->parentObject, $this->propertyName, $objects);
86 $this->parentObject->_setProperty($this->propertyName, $propertyValue);
87 $this->parentObject->_memorizeCleanState($this->propertyName);
88 return $propertyValue;
89 }
90 return $this->parentObject->_getProperty($this->propertyName);
91 }
92
93 /**
94 * @return string
95 */
96 public function _getTypeAndUidString()
97 {
98 $type = $this->dataMapper->getType(get_class($this->parentObject), $this->propertyName);
99 return $type . ':' . $this->fieldValue;
100 }
101
102 /**
103 * Magic method call implementation.
104 *
105 * @param string $methodName The name of the property to get
106 * @param array $arguments The arguments given to the call
107 * @return mixed
108 */
109 public function __call($methodName, $arguments)
110 {
111 $realInstance = $this->_loadRealInstance();
112 if (!is_object($realInstance)) {
113 return null;
114 }
115 return call_user_func_array([$realInstance, $methodName], $arguments);
116 }
117
118 /**
119 * Magic get call implementation.
120 *
121 * @param string $propertyName The name of the property to get
122 * @return mixed
123 */
124 public function __get($propertyName)
125 {
126 $realInstance = $this->_loadRealInstance();
127 return $realInstance->{$propertyName};
128 }
129
130 /**
131 * Magic set call implementation.
132 *
133 * @param string $propertyName The name of the property to set
134 * @param mixed $value The value for the property to set
135 */
136 public function __set($propertyName, $value)
137 {
138 $realInstance = $this->_loadRealInstance();
139 $realInstance->{$propertyName} = $value;
140 }
141
142 /**
143 * Magic isset call implementation.
144 *
145 * @param string $propertyName The name of the property to check
146 * @return bool
147 */
148 public function __isset($propertyName)
149 {
150 $realInstance = $this->_loadRealInstance();
151 return isset($realInstance->{$propertyName});
152 }
153
154 /**
155 * Magic unset call implementation.
156 *
157 * @param string $propertyName The name of the property to unset
158 */
159 public function __unset($propertyName)
160 {
161 $realInstance = $this->_loadRealInstance();
162 unset($realInstance->{$propertyName});
163 }
164
165 /**
166 * Magic toString call implementation.
167 *
168 * @return string
169 */
170 public function __toString()
171 {
172 $realInstance = $this->_loadRealInstance();
173 return $realInstance->__toString();
174 }
175
176 /**
177 * Returns the current value of the storage array
178 *
179 * @return mixed
180 */
181 public function current()
182 {
183 $realInstance = $this->_loadRealInstance();
184 return current($realInstance);
185 }
186
187 /**
188 * Returns the current key storage array
189 *
190 * @return int
191 */
192 public function key()
193 {
194 $realInstance = $this->_loadRealInstance();
195 return key($realInstance);
196 }
197
198 /**
199 * Returns the next position of the storage array
200 */
201 public function next()
202 {
203 $realInstance = $this->_loadRealInstance();
204 next($realInstance);
205 }
206
207 /**
208 * Resets the array pointer of the storage
209 */
210 public function rewind()
211 {
212 $realInstance = $this->_loadRealInstance();
213 reset($realInstance);
214 }
215
216 /**
217 * Checks if the array pointer of the storage points to a valid position
218 *
219 * @return bool
220 */
221 public function valid()
222 {
223 return $this->current() !== false;
224 }
225 }