42a17d6e2b4ffffc13440e5d3bd9c2f8531ee029
[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\ClassLoadingInformation;
25
26 /**
27 * Concrete configuration object for the PropertyMapper.
28 *
29 * @api
30 */
31 class PropertyMappingConfiguration implements 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 \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface[]
52 */
53 protected $subConfigurationForProperty = [];
54
55 /**
56 * Keys which should be renamed
57 *
58 * @var array
59 */
60 protected $mapping = [];
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 = [];
73
74 /**
75 * List of property names to be skipped during property mapping
76 *
77 * @var array
78 */
79 protected $propertiesToSkip = [];
80
81 /**
82 * List of disallowed property names which will be ignored while property mapping
83 *
84 * @var array
85 */
86 protected $propertiesNotToBeMapped = [];
87
88 /**
89 * If TRUE, unknown properties will be skipped during property mapping
90 *
91 * @var bool
92 */
93 protected $skipUnknownProperties = false;
94
95 /**
96 * If TRUE, unknown properties will be mapped.
97 *
98 * @var bool
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 bool TRUE if the given propertyName should be mapped, FALSE otherwise.
112 * @api
113 */
114 public function shouldMap($propertyName)
115 {
116 if (isset($this->propertiesNotToBeMapped[$propertyName])) {
117 return false;
118 }
119
120 if (isset($this->propertiesToBeMapped[$propertyName])) {
121 return true;
122 }
123
124 if (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
125 return true;
126 }
127
128 return $this->mapUnknownProperties;
129 }
130
131 /**
132 * Check if the given $propertyName should be skipped during mapping.
133 *
134 * @param string $propertyName
135 * @return bool
136 * @api
137 */
138 public function shouldSkip($propertyName)
139 {
140 return isset($this->propertiesToSkip[$propertyName]);
141 }
142
143 /**
144 * Allow all properties in property mapping, even unknown ones.
145 *
146 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
147 * @api
148 */
149 public function allowAllProperties()
150 {
151 $this->mapUnknownProperties = true;
152 return $this;
153 }
154
155 /**
156 * Allow a list of specific properties. All arguments of
157 * allowProperties are used here (varargs).
158 *
159 * Example: allowProperties('title', 'content', 'author')
160 *
161 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration
162 * @api
163 */
164 public function allowProperties()
165 {
166 foreach (func_get_args() as $propertyName) {
167 $this->propertiesToBeMapped[$propertyName] = $propertyName;
168 }
169 return $this;
170 }
171
172 /**
173 * Skip a list of specific properties. All arguments of
174 * skipProperties are used here (varargs).
175 *
176 * Example: skipProperties('unused', 'dummy')
177 *
178 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
179 * @api
180 */
181 public function skipProperties()
182 {
183 foreach (func_get_args() as $propertyName) {
184 $this->propertiesToSkip[$propertyName] = $propertyName;
185 }
186 return $this;
187 }
188
189 /**
190 * Allow all properties during property mapping, but reject a few
191 * selected ones (blacklist).
192 *
193 * Example: allowAllPropertiesExcept('password', 'userGroup')
194 *
195 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
196 * @api
197 */
198 public function allowAllPropertiesExcept()
199 {
200 $this->mapUnknownProperties = true;
201
202 foreach (func_get_args() as $propertyName) {
203 $this->propertiesNotToBeMapped[$propertyName] = $propertyName;
204 }
205 return $this;
206 }
207
208 /**
209 * When this is enabled, properties that are disallowed will be skipped
210 * instead of triggering an error during mapping.
211 *
212 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
213 * @api
214 */
215 public function skipUnknownProperties()
216 {
217 $this->skipUnknownProperties = true;
218 return $this;
219 }
220
221 /**
222 * Whether unknown (unconfigured) properties should be skipped during
223 * mapping, instead if causing an error.
224 *
225 * @return bool
226 * @api
227 */
228 public function shouldSkipUnknownProperties()
229 {
230 return $this->skipUnknownProperties;
231 }
232
233 /**
234 * Returns the sub-configuration for the passed $propertyName. Must ALWAYS return a valid configuration object!
235 *
236 * @param string $propertyName
237 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface the property mapping configuration for the given $propertyName.
238 * @api
239 */
240 public function getConfigurationFor($propertyName)
241 {
242 if (isset($this->subConfigurationForProperty[$propertyName])) {
243 return $this->subConfigurationForProperty[$propertyName];
244 }
245 if (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
246 return $this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER];
247 }
248
249 return new self();
250 }
251
252 /**
253 * Maps the given $sourcePropertyName to a target property name.
254 *
255 * @param string $sourcePropertyName
256 * @return string property name of target
257 * @api
258 */
259 public function getTargetPropertyName($sourcePropertyName)
260 {
261 if (isset($this->mapping[$sourcePropertyName])) {
262 return $this->mapping[$sourcePropertyName];
263 }
264 return $sourcePropertyName;
265 }
266
267 /**
268 * @param string $typeConverterClassName
269 * @param string $key
270 * @return mixed configuration value for the specific $typeConverterClassName. Can be used by Type Converters to fetch converter-specific configuration.
271 * @api
272 */
273 public function getConfigurationValue($typeConverterClassName, $key)
274 {
275 if (!isset($this->configuration[$typeConverterClassName][$key])) {
276 return null;
277 }
278
279 return $this->configuration[$typeConverterClassName][$key];
280 }
281
282 /**
283 * Define renaming from Source to Target property.
284 *
285 * @param string $sourcePropertyName
286 * @param string $targetPropertyName
287 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
288 * @api
289 */
290 public function setMapping($sourcePropertyName, $targetPropertyName)
291 {
292 $this->mapping[$sourcePropertyName] = $targetPropertyName;
293 return $this;
294 }
295
296 /**
297 * Set all options for the given $typeConverter.
298 *
299 * @param string $typeConverter class name of type converter
300 * @param array $options
301 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
302 * @api
303 */
304 public function setTypeConverterOptions($typeConverter, array $options)
305 {
306 if (strpos($typeConverter, '_') !== false) {
307 $typeConverter = ClassLoadingInformation::getClassNameForAlias($typeConverter);
308 }
309 foreach ($this->getTypeConvertersWithParentClasses($typeConverter) as $typeConverter) {
310 $this->configuration[$typeConverter] = $options;
311 }
312 return $this;
313 }
314
315 /**
316 * Set a single option (denoted by $optionKey) for the given $typeConverter.
317 *
318 * @param string $typeConverter class name of type converter
319 * @param string $optionKey
320 * @param mixed $optionValue
321 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
322 * @api
323 */
324 public function setTypeConverterOption($typeConverter, $optionKey, $optionValue)
325 {
326 if (strpos($typeConverter, '_') !== false) {
327 $typeConverter = ClassLoadingInformation::getClassNameForAlias($typeConverter);
328 }
329 foreach ($this->getTypeConvertersWithParentClasses($typeConverter) as $typeConverter) {
330 $this->configuration[$typeConverter][$optionKey] = $optionValue;
331 }
332 return $this;
333 }
334
335 /**
336 * Get type converter classes including parents for the given type converter
337 *
338 * When setting an option on a subclassed type converter, this option must also be set on
339 * all its parent type converters.
340 *
341 * @param string $typeConverter The type converter class
342 * @return array Class names of type converters
343 */
344 protected function getTypeConvertersWithParentClasses($typeConverter)
345 {
346 $typeConverterClasses = class_parents($typeConverter);
347 $typeConverterClasses = $typeConverterClasses === false ? [] : $typeConverterClasses;
348 $typeConverterClasses[] = $typeConverter;
349 return $typeConverterClasses;
350 }
351
352 /**
353 * Returns the configuration for the specific property path, ready to be modified. Should be used
354 * inside a fluent interface like:
355 * $configuration->forProperty('foo.bar')->setTypeConverterOption(....)
356 *
357 * @param string $propertyPath
358 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration (or a subclass thereof)
359 * @api
360 */
361 public function forProperty($propertyPath)
362 {
363 $splittedPropertyPath = explode('.', $propertyPath);
364 return $this->traverseProperties($splittedPropertyPath);
365 }
366
367 /**
368 * Traverse the property configuration. Only used by forProperty().
369 *
370 * @param array $splittedPropertyPath
371 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration (or a subclass thereof)
372 */
373 public function traverseProperties(array $splittedPropertyPath)
374 {
375 if (empty($splittedPropertyPath)) {
376 return $this;
377 }
378
379 $currentProperty = array_shift($splittedPropertyPath);
380 if (!isset($this->subConfigurationForProperty[$currentProperty])) {
381 $type = static::class;
382 if (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
383 $this->subConfigurationForProperty[$currentProperty] = clone $this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER];
384 } else {
385 $this->subConfigurationForProperty[$currentProperty] = new $type;
386 }
387 }
388 return $this->subConfigurationForProperty[$currentProperty]->traverseProperties($splittedPropertyPath);
389 }
390
391 /**
392 * Return the type converter set for this configuration.
393 *
394 * @return \TYPO3\CMS\Extbase\Property\TypeConverterInterface
395 * @api
396 */
397 public function getTypeConverter()
398 {
399 return $this->typeConverter;
400 }
401
402 /**
403 * Set a type converter which should be used for this specific conversion.
404 *
405 * @param \TYPO3\CMS\Extbase\Property\TypeConverterInterface $typeConverter
406 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
407 * @api
408 */
409 public function setTypeConverter(\TYPO3\CMS\Extbase\Property\TypeConverterInterface $typeConverter)
410 {
411 $this->typeConverter = $typeConverter;
412 return $this;
413 }
414 }