[TASK] Backport Flow property mapper
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Property / PropertyMappingConfiguration.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Property;
3
4 /* *
5 * This script belongs to the Extbase framework *
6 * *
7 * It is free software; you can redistribute it and/or modify it under *
8 * the terms of the GNU Lesser General Public License as published by the *
9 * Free Software Foundation, either version 3 of the License, or (at your *
10 * option) any later version. *
11 * *
12 * This script is distributed in the hope that it will be useful, but *
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- *
14 * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser *
15 * General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU Lesser General Public *
18 * License along with the script. *
19 * If not, see http://www.gnu.org/licenses/lgpl.html *
20 * *
21 * The TYPO3 project - inspiring people to share! *
22 * */
23
24 use TYPO3\CMS\Core\Core\ClassLoader;
25
26 /**
27 * Concrete configuration object for the PropertyMapper.
28 *
29 * @api
30 */
31 class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface {
32
33 /**
34 * Placeholder in property paths for multi-valued types
35 */
36 const PROPERTY_PATH_PLACEHOLDER = '*';
37
38 /**
39 * multi-dimensional array which stores type-converter specific configuration:
40 * 1. Dimension: Fully qualified class name of the type converter
41 * 2. Dimension: Configuration Key
42 * Value: Configuration Value
43 *
44 * @var array
45 */
46 protected $configuration;
47
48 /**
49 * Stores the configuration for specific child properties.
50 *
51 * @var array<\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface>
52 */
53 protected $subConfigurationForProperty = array();
54
55 /**
56 * Keys which should be renamed
57 *
58 * @var array
59 */
60 protected $mapping = array();
61
62 /**
63 * @var \TYPO3\CMS\Extbase\Property\TypeConverterInterface
64 */
65 protected $typeConverter = NULL;
66
67 /**
68 * List of allowed property names to be converted
69 *
70 * @var array
71 */
72 protected $propertiesToBeMapped = array();
73
74 /**
75 * List of property names to be skipped during property mapping
76 *
77 * @var array
78 */
79 protected $propertiesToSkip = array();
80
81 /**
82 * List of disallowed property names which will be ignored while property mapping
83 *
84 * @var array
85 */
86 protected $propertiesNotToBeMapped = array();
87
88 /**
89 * If TRUE, unknown properties will be skipped during property mapping
90 *
91 * @var boolean
92 */
93 protected $skipUnknownProperties = FALSE;
94
95 /**
96 * If TRUE, unknown properties will be mapped.
97 *
98 * @var boolean
99 */
100 protected $mapUnknownProperties = FALSE;
101
102 /**
103 * The behavior is as follows:
104 *
105 * - if a property has been explicitly forbidden using allowAllPropertiesExcept(...), it is directly rejected
106 * - if a property has been allowed using allowProperties(...), it is directly allowed.
107 * - if allowAllProperties* has been called, we allow unknown properties
108 * - else, return FALSE.
109 *
110 * @param string $propertyName
111 * @return boolean TRUE if the given propertyName should be mapped, FALSE otherwise.
112 * @api
113 */
114 public function shouldMap($propertyName) {
115 if (isset($this->propertiesNotToBeMapped[$propertyName])) {
116 return FALSE;
117 }
118
119 if (isset($this->propertiesToBeMapped[$propertyName])) {
120 return TRUE;
121 }
122
123 if (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
124 return TRUE;
125 }
126
127 return $this->mapUnknownProperties;
128 }
129
130 /**
131 * Check if the given $propertyName should be skipped during mapping.
132 *
133 * @param string $propertyName
134 * @return boolean
135 * @api
136 */
137 public function shouldSkip($propertyName) {
138 return isset($this->propertiesToSkip[$propertyName]);
139 }
140
141 /**
142 * Allow all properties in property mapping, even unknown ones.
143 *
144 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
145 * @api
146 */
147 public function allowAllProperties() {
148 $this->mapUnknownProperties = TRUE;
149 return $this;
150 }
151
152 /**
153 * Allow a list of specific properties. All arguments of
154 * allowProperties are used here (varargs).
155 *
156 * Example: allowProperties('title', 'content', 'author')
157 *
158 * @return void
159 * @api
160 */
161 public function allowProperties() {
162 foreach (func_get_args() as $propertyName) {
163 $this->propertiesToBeMapped[$propertyName] = $propertyName;
164 }
165 return $this;
166 }
167
168 /**
169 * Skip a list of specific properties. All arguments of
170 * skipProperties are used here (varargs).
171 *
172 * Example: skipProperties('unused', 'dummy')
173 *
174 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
175 * @api
176 */
177 public function skipProperties() {
178 foreach (func_get_args() as $propertyName) {
179 $this->propertiesToSkip[$propertyName] = $propertyName;
180 }
181 return $this;
182 }
183
184 /**
185 * Allow all properties during property mapping, but reject a few
186 * selected ones (blacklist).
187 *
188 * Example: allowAllPropertiesExcept('password', 'userGroup')
189 *
190 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
191 * @api
192 */
193 public function allowAllPropertiesExcept() {
194 $this->mapUnknownProperties = TRUE;
195
196 foreach (func_get_args() as $propertyName) {
197 $this->propertiesNotToBeMapped[$propertyName] = $propertyName;
198 }
199 return $this;
200 }
201
202 /**
203 * When this is enabled, properties that are disallowed will be skipped
204 * instead of triggering an error during mapping.
205 *
206 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
207 * @api
208 */
209 public function skipUnknownProperties() {
210 $this->skipUnknownProperties = TRUE;
211 return $this;
212 }
213
214 /**
215 * Whether unknown (unconfigured) properties should be skipped during
216 * mapping, instead if causing an error.
217 *
218 * @return boolean
219 * @api
220 */
221 public function shouldSkipUnknownProperties() {
222 return $this->skipUnknownProperties;
223 }
224
225 /**
226 * Returns the sub-configuration for the passed $propertyName. Must ALWAYS return a valid configuration object!
227 *
228 * @param string $propertyName
229 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface the property mapping configuration for the given $propertyName.
230 * @api
231 */
232 public function getConfigurationFor($propertyName) {
233 if (isset($this->subConfigurationForProperty[$propertyName])) {
234 return $this->subConfigurationForProperty[$propertyName];
235 } elseif (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
236 return $this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER];
237 }
238
239 return new \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration();
240 }
241
242 /**
243 * Maps the given $sourcePropertyName to a target property name.
244 *
245 * @param string $sourcePropertyName
246 * @return string property name of target
247 * @api
248 */
249 public function getTargetPropertyName($sourcePropertyName) {
250 if (isset($this->mapping[$sourcePropertyName])) {
251 return $this->mapping[$sourcePropertyName];
252 }
253 return $sourcePropertyName;
254 }
255
256 /**
257 * @param string $typeConverterClassName
258 * @param string $key
259 * @return mixed configuration value for the specific $typeConverterClassName. Can be used by Type Converters to fetch converter-specific configuration.
260 * @api
261 */
262 public function getConfigurationValue($typeConverterClassName, $key) {
263 if (!isset($this->configuration[$typeConverterClassName][$key])) {
264 return NULL;
265 }
266
267 return $this->configuration[$typeConverterClassName][$key];
268 }
269
270 /**
271 * Define renaming from Source to Target property.
272 *
273 * @param string $sourcePropertyName
274 * @param string $targetPropertyName
275 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
276 * @api
277 */
278 public function setMapping($sourcePropertyName, $targetPropertyName) {
279 $this->mapping[$sourcePropertyName] = $targetPropertyName;
280 return $this;
281 }
282
283 /**
284 * Set all options for the given $typeConverter.
285 *
286 * @param string $typeConverter class name of type converter
287 * @param array $options
288 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
289 * @api
290 */
291 public function setTypeConverterOptions($typeConverter, array $options) {
292 if (strpos($typeConverter, '_') !== FALSE) {
293 $typeConverter = ClassLoader::getClassNameForAlias($typeConverter);
294 }
295 foreach ($this->getTypeConvertersWithParentClasses($typeConverter) as $typeConverter) {
296 $this->configuration[$typeConverter] = $options;
297 }
298 return $this;
299 }
300
301 /**
302 * Set a single option (denoted by $optionKey) for the given $typeConverter.
303 *
304 * @param string $typeConverter class name of type converter
305 * @param string $optionKey
306 * @param mixed $optionValue
307 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
308 * @api
309 */
310 public function setTypeConverterOption($typeConverter, $optionKey, $optionValue) {
311 if (strpos($typeConverter, '_') !== FALSE) {
312 $typeConverter = ClassLoader::getClassNameForAlias($typeConverter);
313 }
314 foreach ($this->getTypeConvertersWithParentClasses($typeConverter) as $typeConverter) {
315 $this->configuration[$typeConverter][$optionKey] = $optionValue;
316 }
317 return $this;
318 }
319
320 /**
321 * Get type converter classes including parents for the given type converter
322 *
323 * When setting an option on a subclassed type converter, this option must also be set on
324 * all its parent type converters.
325 *
326 * @param string $typeConverter The type converter class
327 * @return array Class names of type converters
328 */
329 protected function getTypeConvertersWithParentClasses($typeConverter) {
330 $typeConverterClasses = class_parents($typeConverter);
331 $typeConverterClasses = $typeConverterClasses === FALSE ? array() : $typeConverterClasses;
332 $typeConverterClasses[] = $typeConverter;
333 return $typeConverterClasses;
334 }
335
336 /**
337 * Returns the configuration for the specific property path, ready to be modified. Should be used
338 * inside a fluent interface like:
339 * $configuration->forProperty('foo.bar')->setTypeConverterOption(....)
340 *
341 * @param string $propertyPath
342 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration (or a subclass thereof)
343 * @api
344 */
345 public function forProperty($propertyPath) {
346 $splittedPropertyPath = explode('.', $propertyPath);
347 return $this->traverseProperties($splittedPropertyPath);
348 }
349
350 /**
351 * Traverse the property configuration. Only used by forProperty().
352 *
353 * @param array $splittedPropertyPath
354 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration (or a subclass thereof)
355 */
356 public function traverseProperties(array $splittedPropertyPath) {
357 if (count($splittedPropertyPath) === 0) {
358 return $this;
359 }
360
361 $currentProperty = array_shift($splittedPropertyPath);
362 if (!isset($this->subConfigurationForProperty[$currentProperty])) {
363 $type = get_class($this);
364 if (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
365 $this->subConfigurationForProperty[$currentProperty] = clone $this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER];
366 } else {
367 $this->subConfigurationForProperty[$currentProperty] = new $type;
368 }
369 }
370 return $this->subConfigurationForProperty[$currentProperty]->traverseProperties($splittedPropertyPath);
371 }
372
373 /**
374 * Return the type converter set for this configuration.
375 *
376 * @return \TYPO3\CMS\Extbase\Property\TypeConverterInterface
377 * @api
378 */
379 public function getTypeConverter() {
380 return $this->typeConverter;
381 }
382
383 /**
384 * Set a type converter which should be used for this specific conversion.
385 *
386 * @param \TYPO3\CMS\Extbase\Property\TypeConverterInterface $typeConverter
387 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
388 * @api
389 */
390 public function setTypeConverter(\TYPO3\CMS\Extbase\Property\TypeConverterInterface $typeConverter) {
391 $this->typeConverter = $typeConverter;
392 return $this;
393 }
394 }
395
396 ?>