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