[CLEANUP] Remove redundant code in Persistence
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Tests / Unit / Persistence / Generic / Mapper / DataMapperTest.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic\Mapper;
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 use TYPO3\CMS\Core\Tests\AccessibleObjectInterface;
17 use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap;
18 use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
19
20 /**
21 * Test case
22 */
23 class DataMapperTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
24
25 /**
26 * @test
27 */
28 public function mapMapsArrayToObjectByCallingmapToObject() {
29 $rows = array(array('uid' => '1234'));
30 $object = new \stdClass();
31 /** @var DataMapper|AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject $dataMapper */
32 $dataMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMapper', array('mapSingleRow', 'getTargetType'));
33 $dataMapper->expects($this->any())->method('getTargetType')->will($this->returnArgument(1));
34 $dataMapFactory = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMapFactory');
35 $dataMapper->_set('dataMapFactory', $dataMapFactory);
36 $dataMapper->expects($this->once())->method('mapSingleRow')->with($rows[0])->will($this->returnValue($object));
37 $dataMapper->map(get_class($object), $rows);
38 }
39
40 /**
41 * @test
42 */
43 public function mapSingleRowReturnsObjectFromIdentityMapIfAvailable() {
44 $row = array('uid' => '1234');
45 $object = new \stdClass();
46 $identityMap = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\IdentityMap');
47 $identityMap->expects($this->once())->method('hasIdentifier')->with('1234')->will($this->returnValue(TRUE));
48 $identityMap->expects($this->once())->method('getObjectByIdentifier')->with('1234')->will($this->returnValue($object));
49 $dataMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMapper', array('dummy'));
50 $dataMapper->_set('identityMap', $identityMap);
51 $dataMapper->_call('mapSingleRow', get_class($object), $row);
52 }
53
54 /**
55 * @test
56 */
57 public function thawPropertiesSetsPropertyValues() {
58 $className = 'Class' . md5(uniqid(mt_rand(), TRUE));
59 $classNameWithNS = __NAMESPACE__ . '\\' . $className;
60 eval('namespace ' . __NAMESPACE__ . '; class ' . $className . ' extends \\TYPO3\\CMS\\Extbase\\DomainObject\\AbstractEntity {
61 public $firstProperty; public $secondProperty; public $thirdProperty; public $fourthProperty;
62 }'
63 );
64 $object = new $classNameWithNS();
65 $row = array(
66 'uid' => '1234',
67 'firstProperty' => 'firstValue',
68 'secondProperty' => 1234,
69 'thirdProperty' => 1.234,
70 'fourthProperty' => FALSE
71 );
72 $columnMaps = array(
73 'uid' => new ColumnMap('uid', 'uid'),
74 'pid' => new ColumnMap('pid', 'pid'),
75 'firstProperty' => new ColumnMap('firstProperty', 'firstProperty'),
76 'secondProperty' => new ColumnMap('secondProperty', 'secondProperty'),
77 'thirdProperty' => new ColumnMap('thirdProperty', 'thirdProperty')
78 );
79 $dataMap = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMap', array('dummy'), array($className, $className));
80 $dataMap->_set('columnMaps', $columnMaps);
81 $dataMaps = array(
82 $classNameWithNS => $dataMap
83 );
84 /** @var AccessibleObjectInterface|\TYPO3\CMS\Extbase\Reflection\ClassSchema $classSchema */
85 $classSchema = $this->getAccessibleMock('TYPO3\CMS\Extbase\Reflection\ClassSchema', array('dummy'), array($classNameWithNS));
86 $classSchema->_set('typeHandlingService', new \TYPO3\CMS\Extbase\Service\TypeHandlingService());
87 $classSchema->addProperty('pid', 'integer');
88 $classSchema->addProperty('uid', 'integer');
89 $classSchema->addProperty('firstProperty', 'string');
90 $classSchema->addProperty('secondProperty', 'integer');
91 $classSchema->addProperty('thirdProperty', 'float');
92 $classSchema->addProperty('fourthProperty', 'boolean');
93 $mockReflectionService = $this->getMock('TYPO3\\CMS\\Extbase\\Reflection\\ReflectionService', array('getClassSchema'));
94 $mockReflectionService->expects($this->any())->method('getClassSchema')->will($this->returnValue($classSchema));
95 $dataMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMapper', array('dummy'));
96 $dataMapper->_set('dataMaps', $dataMaps);
97 $dataMapper->_set('reflectionService', $mockReflectionService);
98 $dataMapper->_call('thawProperties', $object, $row);
99 $this->assertAttributeEquals('firstValue', 'firstProperty', $object);
100 $this->assertAttributeEquals(1234, 'secondProperty', $object);
101 $this->assertAttributeEquals(1.234, 'thirdProperty', $object);
102 $this->assertAttributeEquals(FALSE, 'fourthProperty', $object);
103 }
104
105 /**
106 * Test if fetchRelatedEager method returns NULL when $fieldValue = '' and relation type == RELATION_HAS_ONE
107 *
108 * @test
109 */
110 public function fetchRelatedEagerReturnsNullForEmptyRelationHasOne() {
111 $columnMap = new ColumnMap('columnName', 'propertyName');
112 $columnMap->setTypeOfRelation(ColumnMap::RELATION_HAS_ONE);
113 $dataMap = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMap', array('getColumnMap'), array(), '', FALSE);
114 $dataMap->expects($this->any())->method('getColumnMap')->will($this->returnValue($columnMap));
115 $dataMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMapper', array('getDataMap'));
116 $dataMapper->expects($this->any())->method('getDataMap')->will($this->returnValue($dataMap));
117 $result = $dataMapper->_call('fetchRelatedEager', $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractEntity'), 'SomeName', '');
118 $this->assertEquals(NULL, $result);
119 }
120
121 /**
122 * Test if fetchRelatedEager method returns empty array when $fieldValue = '' and relation type != RELATION_HAS_ONE
123 *
124 * @test
125 */
126 public function fetchRelatedEagerReturnsEmptyArrayForEmptyRelationNotHasOne() {
127 $columnMap = new ColumnMap('columnName', 'propertyName');
128 $columnMap->setTypeOfRelation(ColumnMap::RELATION_BELONGS_TO_MANY);
129 $dataMap = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMap', array('getColumnMap'), array(), '', FALSE);
130 $dataMap->expects($this->any())->method('getColumnMap')->will($this->returnValue($columnMap));
131 $dataMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMapper', array('getDataMap'));
132 $dataMapper->expects($this->any())->method('getDataMap')->will($this->returnValue($dataMap));
133 $result = $dataMapper->_call('fetchRelatedEager', $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractEntity'), 'SomeName', '');
134 $this->assertEquals(array(), $result);
135 }
136
137 /**
138 * Test if fetchRelatedEager method returns NULL when $fieldValue = ''
139 * and relation type == RELATION_HAS_ONE without calling fetchRelated
140 *
141 * @test
142 */
143 public function mapObjectToClassPropertyReturnsNullForEmptyRelationHasOne() {
144 $columnMap = new ColumnMap('columnName', 'propertyName');
145 $columnMap->setTypeOfRelation(ColumnMap::RELATION_HAS_ONE);
146 $dataMap = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMap', array('getColumnMap'), array(), '', FALSE);
147 $dataMap->expects($this->any())->method('getColumnMap')->will($this->returnValue($columnMap));
148 $dataMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMapper', array('getDataMap', 'fetchRelated'));
149 $dataMapper->expects($this->any())->method('getDataMap')->will($this->returnValue($dataMap));
150 $dataMapper->expects($this->never())->method('fetchRelated');
151 $result = $dataMapper->_call('mapObjectToClassProperty', $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractEntity'), 'SomeName', '');
152 $this->assertEquals(NULL, $result);
153 }
154
155 /**
156 * Test if mapObjectToClassProperty method returns objects
157 * that are already registered in the persistence session
158 * without query it from the persistence layer
159 *
160 * @test
161 */
162 public function mapObjectToClassPropertyReturnsExistingObjectWithoutCallingFetchRelated() {
163 $columnMap = new ColumnMap('columnName', 'propertyName');
164 $columnMap->setTypeOfRelation(ColumnMap::RELATION_HAS_ONE);
165 $dataMap = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMap', array('getColumnMap'), array(), '', FALSE);
166
167 $className = 'Class1' . md5(uniqid(mt_rand(), TRUE));
168 $classNameWithNS = __NAMESPACE__ . '\\' . $className;
169 eval('namespace ' . __NAMESPACE__ . '; class ' . $className . ' extends \\TYPO3\\CMS\\Extbase\\DomainObject\\AbstractEntity { public $relationProperty; }');
170 $object = new $classNameWithNS();
171
172 $className2 = 'Class2' . md5(uniqid(mt_rand(), TRUE));
173 $className2WithNS = __NAMESPACE__ . '\\' . $className2;
174 eval('namespace ' . __NAMESPACE__ . '; class ' . $className2 . ' extends \\TYPO3\\CMS\\Extbase\\DomainObject\\AbstractEntity { }');
175 $child = new $className2WithNS();
176 $classSchema2 = $this->getAccessibleMock('TYPO3\CMS\Extbase\Reflection\ClassSchema', array('dummy'), array($className2WithNS));
177 $classSchema2->_set('typeHandlingService', new \TYPO3\CMS\Extbase\Service\TypeHandlingService());
178
179 /** @var \TYPO3\CMS\Extbase\Reflection\ClassSchema|AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject $classSchema1 */
180 $classSchema1 = $this->getAccessibleMock('TYPO3\CMS\Extbase\Reflection\ClassSchema', array('dummy'), array($classNameWithNS));
181 $classSchema1->_set('typeHandlingService', new \TYPO3\CMS\Extbase\Service\TypeHandlingService());
182 $classSchema1->addProperty('relationProperty', $className2WithNS);
183 $identifier = 1;
184
185 $session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
186 $session->registerObject($child, $identifier);
187
188 $mockReflectionService = $this->getMock('\TYPO3\CMS\Extbase\Reflection\ReflectionService', array('getClassSchema'), array(), '', FALSE);
189 $mockReflectionService->expects($this->any())->method('getClassSchema')->will($this->returnValue($classSchema1));
190
191 $dataMap->expects($this->any())->method('getColumnMap')->will($this->returnValue($columnMap));
192 $dataMapper = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMapper', array('getDataMap', 'getNonEmptyRelationValue'));
193 $dataMapper->_set('reflectionService', $mockReflectionService);
194 $dataMapper->_set('persistenceSession', $session);
195 $dataMapper->expects($this->any())->method('getDataMap')->will($this->returnValue($dataMap));
196 $dataMapper->expects($this->never())->method('getNonEmptyRelationValue');
197 $result = $dataMapper->_call('mapObjectToClassProperty', $object, 'relationProperty', $identifier);
198 $this->assertEquals($child, $result);
199 }
200
201 /**
202 * Data provider for date checks. Date will be stored based on UTC in
203 * the database. That's why it's not possible to check for explicit date
204 * strings but using the date('c') conversion instead, which considers the
205 * current local timezone setting.
206 *
207 * @return array
208 */
209 public function mapDateTimeHandlesDifferentFieldEvaluationsDataProvider() {
210 return array(
211 'nothing' => array(NULL, NULL, NULL),
212 'timestamp' => array(1, NULL, date('c', 1)),
213 'empty date' => array('0000-00-00', 'date', NULL),
214 'valid date' => array('2013-01-01', 'date', date('c', strtotime('2013-01-01T00:00:00+00:00'))),
215 'empty datetime' => array('0000-00-00 00:00:00', 'datetime', NULL),
216 'valid datetime' => array('2013-01-01 01:02:03', 'datetime', date('c', strtotime('2013-01-01T01:02:03+00:00'))),
217 );
218 }
219
220 /**
221 * @param NULL|string|integer $value
222 * @param NULL|string $storageFormat
223 * @param NULL|string $expectedValue
224 * @test
225 * @dataProvider mapDateTimeHandlesDifferentFieldEvaluationsDataProvider
226 */
227 public function mapDateTimeHandlesDifferentFieldEvaluations($value, $storageFormat, $expectedValue) {
228 $accessibleClassName = $this->buildAccessibleProxy('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Mapper\\DataMapper');
229
230 /** @var DataMapper|AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject $accessibleDataMapFactory */
231 $accessibleDataMapFactory = new $accessibleClassName();
232
233 /** @var $dateTime NULL|\DateTime */
234 $dateTime = $accessibleDataMapFactory->_callRef('mapDateTime', $value, $storageFormat);
235
236 if ($expectedValue === NULL) {
237 $this->assertNull($dateTime);
238 } else {
239 $this->assertEquals($expectedValue, $dateTime->format('c'));
240 }
241 }
242
243 /**
244 * @test
245 */
246 public function getPlainValueReturnsCorrectDateTimeFormat() {
247 $subject = new DataMapper();
248 $columnMap = new ColumnMap('column_name', 'propertyName');
249 $columnMap->setDateTimeStorageFormat('datetime');
250 $datetimeAsString = '2013-04-15 09:30:00';
251 $input = new \DateTime($datetimeAsString);
252 $this->assertEquals('2013-04-15 09:30:00', $subject->getPlainValue($input, $columnMap));
253 $columnMap->setDateTimeStorageFormat('date');
254 $this->assertEquals('2013-04-15', $subject->getPlainValue($input, $columnMap));
255 }
256
257 /**
258 * @test
259 * @dataProvider getPlainValueReturnsExpectedValuesDataProvider
260 */
261 public function getPlainValueReturnsExpectedValues($expectedValue, $input) {
262 $dataMapper = new DataMapper();
263 $this->assertSame($expectedValue, $dataMapper->getPlainValue($input));
264 }
265
266 /**
267 * @return array
268 */
269 public function getPlainValueReturnsExpectedValuesDataProvider() {
270 return array(
271 'datetime to timestamp' => array('1365866253', new \DateTime('@1365866253')),
272 'boolean true to 1' => array(1, TRUE),
273 'boolean false to 0' => array(0, FALSE),
274 'NULL is handled' => array('NULL', NULL),
275 'plain value is returned unchanged' => array('RANDOM string', 'RANDOM string'),
276 'array is flattened' => array('a,b,c', array('a', 'b', 'c')),
277 'deep array is flattened' => array('a,b,c', array(array('a', 'b'), 'c')),
278 );
279 }
280
281 /**
282 * @test
283 * @expectedException \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException
284 */
285 public function getPlainValueCallsGetRealInstanceOnInputIfInputIsInstanceOfLazyLoadingProxy() {
286 $dataMapper = new DataMapper();
287 $input = $this->getMock(
288 'TYPO3\\CMS\\Extbase\\Persistence\\Generic\\LazyLoadingProxy',
289 array(),
290 array(),
291 '',
292 FALSE
293 );
294 $input->expects($this->once())->method('_loadRealInstance')->will($this->returnValue($dataMapper));
295 $dataMapper->getPlainValue($input);
296 }
297
298 /**
299 * @test
300 */
301 public function getPlainValueCallsGetUidOnDomainObjectInterfaceInput() {
302 $dataMapper = new DataMapper();
303 $input = $this->getMock(
304 'TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface',
305 array(),
306 array(),
307 '',
308 FALSE
309 );
310 $input->expects($this->once())->method('getUid')->will($this->returnValue(23));
311 $this->assertSame(23, $dataMapper->getPlainValue($input));
312 }
313 }