3ce0c22bfa9bc1fe4ff8c2e3e2c47a4ae67577a4
[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 } elseif (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
245 return $this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER];
246 }
247
248 return new PropertyMappingConfiguration();
249 }
250
251 /**
252 * Maps the given $sourcePropertyName to a target property name.
253 *
254 * @param string $sourcePropertyName
255 * @return string property name of target
256 * @api
257 */
258 public function getTargetPropertyName($sourcePropertyName)
259 {
260 if (isset($this->mapping[$sourcePropertyName])) {
261 return $this->mapping[$sourcePropertyName];
262 }
263 return $sourcePropertyName;
264 }
265
266 /**
267 * @param string $typeConverterClassName
268 * @param string $key
269 * @return mixed configuration value for the specific $typeConverterClassName. Can be used by Type Converters to fetch converter-specific configuration.
270 * @api
271 */
272 public function getConfigurationValue($typeConverterClassName, $key)
273 {
274 if (!isset($this->configuration[$typeConverterClassName][$key])) {
275 return null;
276 }
277
278 return $this->configuration[$typeConverterClassName][$key];
279 }
280
281 /**
282 * Define renaming from Source to Target property.
283 *
284 * @param string $sourcePropertyName
285 * @param string $targetPropertyName
286 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
287 * @api
288 */
289 public function setMapping($sourcePropertyName, $targetPropertyName)
290 {
291 $this->mapping[$sourcePropertyName] = $targetPropertyName;
292 return $this;
293 }
294
295 /**
296 * Set all options for the given $typeConverter.
297 *
298 * @param string $typeConverter class name of type converter
299 * @param array $options
300 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
301 * @api
302 */
303 public function setTypeConverterOptions($typeConverter, array $options)
304 {
305 if (strpos($typeConverter, '_') !== false) {
306 $typeConverter = ClassLoadingInformation::getClassNameForAlias($typeConverter);
307 }
308 foreach ($this->getTypeConvertersWithParentClasses($typeConverter) as $typeConverter) {
309 $this->configuration[$typeConverter] = $options;
310 }
311 return $this;
312 }
313
314 /**
315 * Set a single option (denoted by $optionKey) for the given $typeConverter.
316 *
317 * @param string $typeConverter class name of type converter
318 * @param string $optionKey
319 * @param mixed $optionValue
320 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
321 * @api
322 */
323 public function setTypeConverterOption($typeConverter, $optionKey, $optionValue)
324 {
325 if (strpos($typeConverter, '_') !== false) {
326 $typeConverter = ClassLoadingInformation::getClassNameForAlias($typeConverter);
327 }
328 foreach ($this->getTypeConvertersWithParentClasses($typeConverter) as $typeConverter) {
329 $this->configuration[$typeConverter][$optionKey] = $optionValue;
330 }
331 return $this;
332 }
333
334 /**
335 * Get type converter classes including parents for the given type converter
336 *
337 * When setting an option on a subclassed type converter, this option must also be set on
338 * all its parent type converters.
339 *
340 * @param string $typeConverter The type converter class
341 * @return array Class names of type converters
342 */
343 protected function getTypeConvertersWithParentClasses($typeConverter)
344 {
345 $typeConverterClasses = class_parents($typeConverter);
346 $typeConverterClasses = $typeConverterClasses === false ? [] : $typeConverterClasses;
347 $typeConverterClasses[] = $typeConverter;
348 return $typeConverterClasses;
349 }
350
351 /**
352 * Returns the configuration for the specific property path, ready to be modified. Should be used
353 * inside a fluent interface like:
354 * $configuration->forProperty('foo.bar')->setTypeConverterOption(....)
355 *
356 * @param string $propertyPath
357 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration (or a subclass thereof)
358 * @api
359 */
360 public function forProperty($propertyPath)
361 {
362 $splittedPropertyPath = explode('.', $propertyPath);
363 return $this->traverseProperties($splittedPropertyPath);
364 }
365
366 /**
367 * Traverse the property configuration. Only used by forProperty().
368 *
369 * @param array $splittedPropertyPath
370 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration (or a subclass thereof)
371 */
372 public function traverseProperties(array $splittedPropertyPath)
373 {
374 if (empty($splittedPropertyPath)) {
375 return $this;
376 }
377
378 $currentProperty = array_shift($splittedPropertyPath);
379 if (!isset($this->subConfigurationForProperty[$currentProperty])) {
380 $type = get_class($this);
381 if (isset($this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER])) {
382 $this->subConfigurationForProperty[$currentProperty] = clone $this->subConfigurationForProperty[self::PROPERTY_PATH_PLACEHOLDER];
383 } else {
384 $this->subConfigurationForProperty[$currentProperty] = new $type;
385 }
386 }
387 return $this->subConfigurationForProperty[$currentProperty]->traverseProperties($splittedPropertyPath);
388 }
389
390 /**
391 * Return the type converter set for this configuration.
392 *
393 * @return \TYPO3\CMS\Extbase\Property\TypeConverterInterface
394 * @api
395 */
396 public function getTypeConverter()
397 {
398 return $this->typeConverter;
399 }
400
401 /**
402 * Set a type converter which should be used for this specific conversion.
403 *
404 * @param \TYPO3\CMS\Extbase\Property\TypeConverterInterface $typeConverter
405 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration this
406 * @api
407 */
408 public function setTypeConverter(\TYPO3\CMS\Extbase\Property\TypeConverterInterface $typeConverter)
409 {
410 $this->typeConverter = $typeConverter;
411 return $this;
412 }
413 }