Extbase:
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Property / Mapper.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 *
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
24
25 /**
26 * The Property Mapper maps properties from a source onto a given target object, often a
27 * (domain-) model. Which properties are required and how they should be filtered can
28 * be customized.
29 *
30 * During the mapping process, the property values are validated and the result of this
31 * validation can be queried.
32 *
33 * The following code would map the property of the source array to the target:
34 *
35 * $target = new ArrayObject();
36 * $source = new ArrayObject(
37 * array(
38 * 'someProperty' => 'SomeValue'
39 * )
40 * );
41 * $mapper->mapAndValidate(array('someProperty'), $source, $target);
42 *
43 * Now the target object equals the source object.
44 *
45 * @package TYPO3
46 * @subpackage extbase
47 * @version $Id: $
48 */
49 class Tx_Extbase_Property_Mapper {
50
51 /**
52 * Results of the last mapping operation
53 * @var Tx_Extbase_Property_MappingResults
54 */
55 protected $mappingResults;
56
57 /**
58 * @var Tx_Extbase_Validation_ValidatorResolver
59 */
60 protected $validatorResolver;
61
62
63 /**
64 * Constructs the Property Mapper.
65 */
66 public function __construct() {
67 $this->validatorResolver = t3lib_div::makeInstance('Tx_Extbase_Validation_ValidatorResolver');
68 }
69
70 /**
71 * Maps the given properties to the target object and validates the properties according to the defined
72 * validators. If the result object is not valid, the operation will be undone (the target object remains
73 * unchanged) and this method returns FALSE.
74 *
75 * If in doubt, always prefer this method over the map() method because skipping validation can easily become
76 * a security issue.
77 *
78 * @param array $propertyNames Names of the properties to map.
79 * @param mixed $source Source containing the properties to map to the target object. Must either be an array, ArrayObject or any other object.
80 * @param object $target The target object
81 * @param Tx_Extbase_Validation_Validator_ObjectValidatorInterface $targetObjectValidator A validator used for validating the target object
82 * @param array $optionalPropertyNames Names of optional properties. If a property is specified here and it doesn't exist in the source, no error is issued.
83 * @return boolean TRUE if the mapped properties are valid, otherwise FALSE
84 * @see getMappingResults()
85 * @see map()
86 */
87 public function mapAndValidate(array $propertyNames, $source, $target, $optionalPropertyNames = array(), Tx_Extbase_Validation_Validator_ObjectValidatorInterface $targetObjectValidator) {
88 $backupProperties = array();
89
90 $this->map($propertyNames, $source, $backupProperties, $optionalPropertyNames);
91 if ($this->mappingResults->hasErrors()) return FALSE;
92
93 $this->map($propertyNames, $source, $target, $optionalPropertyNames);
94 if ($this->mappingResults->hasErrors()) return FALSE;
95
96 if ($targetObjectValidator->isValid($target) !== TRUE) {
97 $this->mappingResults->addError('Validation errors: ' . implode('. ', $targetObjectValidator->getErrors()), '*');
98 $backupMappingResult = $this->mappingResults;
99 $this->map($propertyNames, $backupProperties, $source, $optionalPropertyNames);
100 $this->mappingResults = $backupMappingResult;
101 }
102 return (!$this->mappingResults->hasErrors());
103 }
104
105 /**
106 * Maps the given properties to the target object WITHOUT VALIDATING THE RESULT.
107 * If the properties could be set, this method returns TRUE, otherwise FALSE.
108 * Returning TRUE does not mean that the target object is valid and secure!
109 *
110 * Only use this method if you're sure that you don't need validation!
111 *
112 * @param array $propertyNames Names of the properties to map.
113 * @param mixed $source Source containing the properties to map to the target object. Must either be an array, ArrayObject or any other object.
114 * @param object $target The target object
115 * @param array $optionalPropertyNames Names of optional properties. If a property is specified here and it doesn't exist in the source, no error is issued.
116 * @return boolean TRUE if the properties could be mapped, otherwise FALSE
117 * @see mapAndValidate()
118 */
119 public function map(array $propertyNames, $source, $target, $optionalPropertyNames = array()) {
120 if (!is_object($source) && !is_array($source) && ($source instanceof ArrayAccess)) throw new Tx_Extbase_Property_Exception_InvalidSource('The source object must be a valid object or array, ' . gettype($target) . ' given.', 1187807099);
121 if (!is_object($target) && !is_array($target)) throw new Tx_Extbase_Property_Exception_InvalidTarget('The target object must be a valid object or array, ' . gettype($target) . ' given.', 1187807099);
122
123 $this->mappingResults = t3lib_div::makeInstance('Tx_Extbase_Property_MappingResults');
124 $propertyValues = array();
125
126 foreach ($propertyNames as $propertyName) {
127 if (is_array($source) || $source instanceof ArrayAccess) {
128 if (isset($source[$propertyName])) $propertyValues[$propertyName] = $source[$propertyName];
129 } else {
130 $propertyValues[$propertyName] = Tx_Extbase_Reflection_ObjectAccess::getProperty($source, $propertyName);
131 }
132 }
133 foreach ($propertyNames as $propertyName) {
134 if (isset($propertyValues[$propertyName])) {
135 if (is_array($target)) {
136 $target[$propertyName] = $source[$propertyName];
137 } elseif (Tx_Extbase_Reflection_ObjectAccess::setProperty($target, $propertyName, $propertyValues[$propertyName]) === FALSE) {
138 $this->mappingResults->addError("Property '$propertyName' could not be set.", $propertyName);
139 }
140 } elseif (!in_array($propertyName, $optionalPropertyNames)) {
141 $this->mappingResults->addError("Required property '$propertyName' does not exist.", $propertyName);
142 }
143 }
144 return (!$this->mappingResults->hasErrors() && !$this->mappingResults->hasWarnings());
145 }
146
147 /**
148 * Returns the results of the last mapping operation.
149 *
150 * @return Tx_Extbase_Property_MappingResults The mapping results (or NULL if no mapping has been carried out yet)
151 */
152 public function getMappingResults() {
153 return $this->mappingResults;
154 }
155 }
156
157 ?>