Completed
Push — 3.x ( ac1e9d...46d14e )
by Oskar
01:34
created

ModelManagerTest::valueObjectDataProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\DoctrineORMAdminBundle\Tests\Model;
15
16
use Doctrine\Common\Collections\ArrayCollection;
17
use Doctrine\Common\Persistence\ManagerRegistry;
18
use Doctrine\Common\Persistence\ObjectManager;
19
use Doctrine\DBAL\Connection;
20
use Doctrine\DBAL\DBALException;
21
use Doctrine\DBAL\Platforms\MySqlPlatform;
22
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
23
use Doctrine\DBAL\Types\Type;
24
use Doctrine\ORM\Configuration;
25
use Doctrine\ORM\EntityManager;
26
use Doctrine\ORM\EntityManagerInterface;
27
use Doctrine\ORM\Mapping\ClassMetadata;
28
use Doctrine\ORM\Mapping\ClassMetadataFactory;
29
use Doctrine\ORM\Mapping\ClassMetadataInfo;
30
use Doctrine\ORM\OptimisticLockException;
31
use Doctrine\ORM\Query;
32
use Doctrine\ORM\QueryBuilder;
33
use PHPUnit\Framework\TestCase;
34
use Sonata\AdminBundle\Datagrid\Datagrid;
35
use Sonata\AdminBundle\Datagrid\DatagridInterface;
36
use Sonata\AdminBundle\Exception\LockException;
37
use Sonata\AdminBundle\Exception\ModelManagerException;
38
use Sonata\AdminBundle\Filter\FilterInterface;
39
use Sonata\DoctrineORMAdminBundle\Admin\FieldDescription;
40
use Sonata\DoctrineORMAdminBundle\Datagrid\OrderByToSelectWalker;
41
use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery;
42
use Sonata\DoctrineORMAdminBundle\Model\ModelManager;
43
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\ProductIdType;
44
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\UuidBinaryType;
45
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\UuidType;
46
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\ValueObjectWithMagicToStringImpl;
47
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\ValueObjectWithToStringImpl;
48
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\AbstractEntity;
49
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\AssociatedEntity;
50
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\ContainerEntity;
51
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\Embeddable\EmbeddedEntity;
52
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\Embeddable\SubEmbeddedEntity;
53
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\Product;
54
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\ProductId;
55
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\ProtectedEntity;
56
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\SimpleEntity;
57
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\UuidBinaryEntity;
58
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\UuidEntity;
59
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\VersionedEntity;
60
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Util\NonIntegerIdentifierTestClass;
61
use Symfony\Bridge\Doctrine\RegistryInterface;
62
63
class ModelManagerTest extends TestCase
64
{
65
    public static function setUpBeforeClass(): void
66
    {
67
        if (!Type::hasType(UuidType::NAME)) {
68
            Type::addType(UuidType::NAME, UuidType::class);
69
        }
70
        if (!Type::hasType(UuidBinaryType::NAME)) {
71
            Type::addType(UuidBinaryType::NAME, UuidBinaryType::class);
72
        }
73
        if (!Type::hasType(ProductIdType::NAME)) {
74
            Type::addType(ProductIdType::NAME, ProductIdType::class);
75
        }
76
    }
77
78
    public function valueObjectDataProvider()
79
    {
80
        return [
81
            'value object with toString implementation' => [ValueObjectWithToStringImpl::class],
82
            'value object with magic toString implementation' => [ValueObjectWithMagicToStringImpl::class],
83
        ];
84
    }
85
86
    /**
87
     * @dataProvider valueObjectDataProvider
88
     */
89
    public function testGetIdentifierValuesWhenIdentifierIsValueObjectWithToStringMethod($vbClassName)
90
    {
91
        $entity = new UuidBinaryEntity(new $vbClassName('a7ef873a-e7b5-11e9-81b4-2a2ae2dbcce4'));
92
93
        $platform = $this->createMock(MySqlPlatform::class);
94
95
        $connection = $this->createMock(Connection::class);
96
        $connection->method('getDatabasePlatform')->willReturn($platform);
97
98
        $classMetadata = $this->createMock(ClassMetadataInfo::class);
99
        $classMetadata->method('getIdentifierValues')->willReturn([$entity->getId()]);
100
        $classMetadata->method('getTypeOfField')->willReturn(UuidBinaryType::NAME);
101
102
        $classMetadataFactory = $this->createMock(ClassMetadataFactory::class);
103
        $classMetadataFactory->method('getMetadataFor')->willReturn($classMetadata);
104
105
        $entityManager = $this->createMock(EntityManager::class);
106
        $entityManager->method('getMetadataFactory')->willReturn($classMetadataFactory);
107
        $entityManager->method('getConnection')->willReturn($connection);
108
109
        $registry = $this->createMock(RegistryInterface::class);
110
        $registry->method('getManagerForClass')->willReturn($entityManager);
111
112
        $manager = new ModelManager($registry);
113
114
        $this->assertSame(
115
            ['a7ef873a-e7b5-11e9-81b4-2a2ae2dbcce4'],
116
            $manager->getIdentifierValues($entity)
117
        );
118
    }
119
120
    public function testInstantiateWithDeprecatedRegistryInterface(): void
121
    {
122
        $registry = $this->createMock(RegistryInterface::class);
123
        $manager = new ModelManager($registry);
124
        $em = $this->createMock(EntityManagerInterface::class);
125
126
        $registry->expects($this->once())
127
            ->method('getManagerForClass')
128
            ->with('x')
129
            ->willReturn($em)
130
        ;
131
        $this->assertSame($em, $manager->getEntityManager('x'));
132
    }
133
134
    public function testSortParameters(): void
135
    {
136
        $registry = $this->createMock(ManagerRegistry::class);
137
138
        $manager = new ModelManager($registry);
139
140
        $datagrid1 = $this->createMock(Datagrid::class);
141
        $datagrid2 = $this->createMock(Datagrid::class);
142
143
        $field1 = new FieldDescription();
144
        $field1->setName('field1');
145
146
        $field2 = new FieldDescription();
147
        $field2->setName('field2');
148
149
        $field3 = new FieldDescription();
150
        $field3->setName('field3');
151
        $field3->setOption('sortable', 'field3sortBy');
152
153
        $datagrid1
154
            ->expects($this->any())
155
            ->method('getValues')
156
            ->willReturn([
157
                '_sort_by' => $field1,
158
                '_sort_order' => 'ASC',
159
            ]);
160
161
        $datagrid2
162
            ->expects($this->any())
163
            ->method('getValues')
164
            ->willReturn([
165
                '_sort_by' => $field3,
166
                '_sort_order' => 'ASC',
167
            ]);
168
169
        $parameters = $manager->getSortParameters($field1, $datagrid1);
170
171
        $this->assertSame('DESC', $parameters['filter']['_sort_order']);
172
        $this->assertSame('field1', $parameters['filter']['_sort_by']);
173
174
        $parameters = $manager->getSortParameters($field2, $datagrid1);
175
176
        $this->assertSame('ASC', $parameters['filter']['_sort_order']);
177
        $this->assertSame('field2', $parameters['filter']['_sort_by']);
178
179
        $parameters = $manager->getSortParameters($field3, $datagrid1);
180
181
        $this->assertSame('ASC', $parameters['filter']['_sort_order']);
182
        $this->assertSame('field3sortBy', $parameters['filter']['_sort_by']);
183
184
        $parameters = $manager->getSortParameters($field3, $datagrid2);
185
186
        $this->assertSame('DESC', $parameters['filter']['_sort_order']);
187
        $this->assertSame('field3sortBy', $parameters['filter']['_sort_by']);
188
    }
189
190
    public function getVersionDataProvider()
191
    {
192
        return [
193
            [true],
194
            [false],
195
        ];
196
    }
197
198
    /**
199
     * @dataProvider getVersionDataProvider
200
     */
201
    public function testGetVersion($isVersioned): void
202
    {
203
        $object = new VersionedEntity();
204
205
        $modelManager = $this->getMockBuilder(ModelManager::class)
206
            ->disableOriginalConstructor()
207
            ->setMethods(['getMetadata'])
208
            ->getMock();
209
210
        $metadata = $this->getMetadata(\get_class($object), $isVersioned);
211
212
        $modelManager->expects($this->any())
213
            ->method('getMetadata')
214
            ->willReturn($metadata);
215
216
        if ($isVersioned) {
217
            $object->version = 123;
218
219
            $this->assertNotNull($modelManager->getLockVersion($object));
220
        } else {
221
            $this->assertNull($modelManager->getLockVersion($object));
222
        }
223
    }
224
225
    public function lockDataProvider()
226
    {
227
        return [
228
            [true,  false],
229
            [true,  true],
230
            [false, false],
231
        ];
232
    }
233
234
    /**
235
     * @dataProvider lockDataProvider
236
     */
237
    public function testLock($isVersioned, $expectsException): void
238
    {
239
        $object = new VersionedEntity();
240
241
        $em = $this->getMockBuilder(EntityManager::class)
242
            ->disableOriginalConstructor()
243
            ->setMethods(['lock'])
244
            ->getMock();
245
246
        $modelManager = $this->getMockBuilder(ModelManager::class)
247
            ->disableOriginalConstructor()
248
            ->setMethods(['getMetadata', 'getEntityManager'])
249
            ->getMock();
250
251
        $modelManager->expects($this->any())
252
            ->method('getEntityManager')
253
            ->willReturn($em);
254
255
        $metadata = $this->getMetadata(\get_class($object), $isVersioned);
256
257
        $modelManager->expects($this->any())
258
            ->method('getMetadata')
259
            ->willReturn($metadata);
260
261
        $em->expects($isVersioned ? $this->once() : $this->never())
262
            ->method('lock');
263
264
        if ($expectsException) {
265
            $em->expects($this->once())
266
                ->method('lock')
267
                ->will($this->throwException(OptimisticLockException::lockFailed($object)));
268
269
            $this->expectException(LockException::class);
270
        }
271
272
        $modelManager->lock($object, 123);
273
    }
274
275
    public function testGetParentMetadataForProperty(): void
276
    {
277
        $containerEntityClass = ContainerEntity::class;
278
        $associatedEntityClass = AssociatedEntity::class;
279
        $embeddedEntityClass = EmbeddedEntity::class;
280
        $modelManagerClass = ModelManager::class;
281
282
        $em = $this->createMock(EntityManager::class);
283
284
        /** @var \PHPUnit_Framework_MockObject_MockObject|ModelManager $modelManager */
285
        $modelManager = $this->getMockBuilder($modelManagerClass)
286
            ->disableOriginalConstructor()
287
            ->setMethods(['getMetadata', 'getEntityManager'])
288
            ->getMock();
289
290
        $modelManager->expects($this->any())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Sonata\DoctrineOR...dle\Model\ModelManager>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
291
            ->method('getEntityManager')
292
            ->willReturn($em);
293
294
        $containerEntityMetadata = $this->getMetadataForContainerEntity();
295
        $associatedEntityMetadata = $this->getMetadataForAssociatedEntity();
296
        $embeddedEntityMetadata = $this->getMetadataForEmbeddedEntity();
297
298
        $modelManager->expects($this->any())->method('getMetadata')
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Sonata\DoctrineOR...dle\Model\ModelManager>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
299
            ->willReturnMap(
300
301
                    [
302
                        [$containerEntityClass, $containerEntityMetadata],
303
                        [$embeddedEntityClass, $embeddedEntityMetadata],
304
                        [$associatedEntityClass, $associatedEntityMetadata],
305
                    ]
306
307
            );
308
309
        /** @var ClassMetadata $metadata */
310
        list($metadata, $lastPropertyName) = $modelManager
311
            ->getParentMetadataForProperty($containerEntityClass, 'plainField');
312
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'integer');
313
314
        list($metadata, $lastPropertyName) = $modelManager
315
            ->getParentMetadataForProperty($containerEntityClass, 'associatedEntity.plainField');
316
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'string');
317
318
        list($metadata, $lastPropertyName) = $modelManager
319
            ->getParentMetadataForProperty($containerEntityClass, 'embeddedEntity.plainField');
320
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
321
322
        list($metadata, $lastPropertyName) = $modelManager
323
            ->getParentMetadataForProperty($containerEntityClass, 'associatedEntity.embeddedEntity.plainField');
324
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
325
326
        list($metadata, $lastPropertyName) = $modelManager
327
            ->getParentMetadataForProperty(
328
                $containerEntityClass,
329
                'associatedEntity.embeddedEntity.subEmbeddedEntity.plainField'
330
            );
331
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
332
    }
333
334
    public function getMetadataForEmbeddedEntity()
335
    {
336
        $metadata = new ClassMetadata(EmbeddedEntity::class);
337
338
        $metadata->fieldMappings = [
339
            'plainField' => [
340
                'fieldName' => 'plainField',
341
                'columnName' => 'plainField',
342
                'type' => 'boolean',
343
            ],
344
        ];
345
346
        return $metadata;
347
    }
348
349
    public function getMetadataForSubEmbeddedEntity()
350
    {
351
        $metadata = new ClassMetadata(SubEmbeddedEntity::class);
352
353
        $metadata->fieldMappings = [
354
            'plainField' => [
355
                'fieldName' => 'plainField',
356
                'columnName' => 'plainField',
357
                'type' => 'boolean',
358
            ],
359
        ];
360
361
        return $metadata;
362
    }
363
364
    public function getMetadataForAssociatedEntity()
365
    {
366
        $embeddedEntityClass = EmbeddedEntity::class;
367
        $subEmbeddedEntityClass = SubEmbeddedEntity::class;
368
369
        $metadata = new ClassMetadata(AssociatedEntity::class);
370
371
        $metadata->fieldMappings = [
372
            'plainField' => [
373
                'fieldName' => 'plainField',
374
                'columnName' => 'plainField',
375
                'type' => 'string',
376
            ],
377
        ];
378
379
        $metadata->embeddedClasses['embeddedEntity'] = [
380
            'class' => $embeddedEntityClass,
381
            'columnPrefix' => 'embedded_entity_',
382
        ];
383
        $metadata->embeddedClasses['embeddedEntity.subEmbeddedEntity'] = [
384
            'class' => $subEmbeddedEntityClass,
385
            'columnPrefix' => 'embedded_entity_sub_embedded_entity_',
386
            'declaredField' => 'embeddedEntity',
387
            'originalField' => 'subEmbeddedEntity',
388
        ];
389
390
        $metadata->inlineEmbeddable('embeddedEntity', $this->getMetadataForEmbeddedEntity());
391
        $metadata->inlineEmbeddable('embeddedEntity.subEmbeddedEntity', $this->getMetadataForSubEmbeddedEntity());
392
393
        return $metadata;
394
    }
395
396
    public function getMetadataForContainerEntity()
397
    {
398
        $containerEntityClass = ContainerEntity::class;
399
        $associatedEntityClass = AssociatedEntity::class;
400
        $embeddedEntityClass = EmbeddedEntity::class;
401
        $subEmbeddedEntityClass = SubEmbeddedEntity::class;
402
403
        $metadata = new ClassMetadata($containerEntityClass);
404
405
        $metadata->fieldMappings = [
406
            'plainField' => [
407
                'fieldName' => 'plainField',
408
                'columnName' => 'plainField',
409
                'type' => 'integer',
410
            ],
411
        ];
412
413
        $metadata->associationMappings['associatedEntity'] = [
414
            'fieldName' => 'associatedEntity',
415
            'targetEntity' => $associatedEntityClass,
416
            'sourceEntity' => $containerEntityClass,
417
        ];
418
419
        $metadata->embeddedClasses['embeddedEntity'] = [
420
            'class' => $embeddedEntityClass,
421
            'columnPrefix' => 'embeddedEntity',
422
        ];
423
        $metadata->embeddedClasses['embeddedEntity.subEmbeddedEntity'] = [
424
            'class' => $subEmbeddedEntityClass,
425
            'columnPrefix' => 'embedded_entity_sub_embedded_entity_',
426
            'declaredField' => 'embeddedEntity',
427
            'originalField' => 'subEmbeddedEntity',
428
        ];
429
430
        $metadata->inlineEmbeddable('embeddedEntity', $this->getMetadataForEmbeddedEntity());
431
        $metadata->inlineEmbeddable('embeddedEntity.subEmbeddedEntity', $this->getMetadataForSubEmbeddedEntity());
432
433
        return $metadata;
434
    }
435
436
    public function testGetIdentifierValuesForIdInObjectTypeBinaryToStringSupport(): void
437
    {
438
        $uuid = new NonIntegerIdentifierTestClass('efbcfc4b-8c43-4d42-aa4c-d707e55151ac');
439
440
        $entity = new UuidEntity($uuid);
441
442
        $meta = $this->createMock(ClassMetadata::class);
443
        $meta->expects($this->any())
444
            ->method('getIdentifierValues')
445
            ->willReturn([$entity->getId()]);
446
        $meta->expects($this->any())
447
            ->method('getTypeOfField')
448
            ->willReturn(UuidBinaryType::NAME); //'uuid_binary'
449
450
        $mf = $this->createMock(ClassMetadataFactory::class);
451
        $mf->expects($this->any())
452
            ->method('getMetadataFor')
453
            ->willReturn($meta);
454
455
        $platform = $this->createMock(PostgreSqlPlatform::class);
456
        $platform->expects($this->any())
457
            ->method('hasDoctrineTypeMappingFor')
458
            ->with(UuidBinaryType::NAME)
459
            ->willReturn(true);
460
        $platform->expects($this->any())
461
            ->method('getDoctrineTypeMapping')
462
            ->with(UuidBinaryType::NAME)
463
            ->willReturn('binary');
464
465
        $conn = $this->createMock(Connection::class);
466
        $conn->expects($this->any())
467
            ->method('getDatabasePlatform')
468
            ->willReturn($platform);
469
470
        $em = $this->createMock(EntityManager::class);
471
        $em->expects($this->any())
472
            ->method('getMetadataFactory')
473
            ->willReturn($mf);
474
        $em->expects($this->any())
475
            ->method('getConnection')
476
            ->willReturn($conn);
477
478
        $registry = $this->createMock(ManagerRegistry::class);
479
        $registry->expects($this->any())
480
            ->method('getManagerForClass')
481
            ->willReturn($em);
482
483
        $manager = new ModelManager($registry);
484
        $result = $manager->getIdentifierValues($entity);
485
486
        $this->assertSame($entity->getId()->toString(), $result[0]);
487
    }
488
489
    public function testNonIntegerIdentifierType(): void
490
    {
491
        $uuid = new NonIntegerIdentifierTestClass('efbcfc4b-8c43-4d42-aa4c-d707e55151ac');
492
        $entity = new UuidEntity($uuid);
493
494
        $meta = $this->createMock(ClassMetadata::class);
495
        $meta->expects($this->any())
496
            ->method('getIdentifierValues')
497
            ->willReturn([$entity->getId()]);
498
        $meta->expects($this->any())
499
            ->method('getTypeOfField')
500
            ->willReturn(UuidType::NAME);
501
502
        $mf = $this->createMock(ClassMetadataFactory::class);
503
        $mf->expects($this->any())
504
            ->method('getMetadataFor')
505
            ->willReturn($meta);
506
507
        $platform = $this->createMock(PostgreSqlPlatform::class);
508
        $platform->expects($this->any())
509
            ->method('hasDoctrineTypeMappingFor')
510
            ->with(UuidType::NAME)
511
            ->willReturn(false);
512
        $platform->expects($this->never())
513
            ->method('getDoctrineTypeMapping');
514
515
        $conn = $this->createMock(Connection::class);
516
        $conn->expects($this->any())
517
            ->method('getDatabasePlatform')
518
            ->willReturn($platform);
519
520
        $em = $this->createMock(EntityManager::class);
521
        $em->expects($this->any())
522
            ->method('getMetadataFactory')
523
            ->willReturn($mf);
524
        $em->expects($this->any())
525
            ->method('getConnection')
526
            ->willReturn($conn);
527
528
        $registry = $this->createMock(ManagerRegistry::class);
529
        $registry->expects($this->any())
530
            ->method('getManagerForClass')
531
            ->willReturn($em);
532
533
        $manager = new ModelManager($registry);
534
        $result = $manager->getIdentifierValues($entity);
535
536
        $this->assertSame($entity->getId()->toString(), $result[0]);
537
    }
538
539
    public function testIntegerIdentifierType(): void
540
    {
541
        $id = new ProductId(12345);
542
        $entity = new Product($id, 'Some product');
543
544
        $meta = $this->createMock(ClassMetadata::class);
545
        $meta->expects($this->any())
546
            ->method('getIdentifierValues')
547
            ->willReturn([$entity->getId()]);
548
        $meta->expects($this->any())
549
            ->method('getTypeOfField')
550
            ->willReturn(ProductIdType::NAME);
551
552
        $mf = $this->createMock(ClassMetadataFactory::class);
553
        $mf->expects($this->any())
554
            ->method('getMetadataFor')
555
            ->willReturn($meta);
556
557
        $platform = $this->createMock(PostgreSqlPlatform::class);
558
        $platform->expects($this->any())
559
            ->method('hasDoctrineTypeMappingFor')
560
            ->with(ProductIdType::NAME)
561
            ->willReturn(false);
562
        $platform->expects($this->never())
563
            ->method('getDoctrineTypeMapping');
564
565
        $conn = $this->createMock(Connection::class);
566
        $conn->expects($this->any())
567
            ->method('getDatabasePlatform')
568
            ->willReturn($platform);
569
570
        $em = $this->createMock(EntityManager::class);
571
        $em->expects($this->any())
572
            ->method('getMetadataFactory')
573
            ->willReturn($mf);
574
        $em->expects($this->any())
575
            ->method('getConnection')
576
            ->willReturn($conn);
577
578
        $registry = $this->createMock(ManagerRegistry::class);
579
        $registry->expects($this->any())
580
            ->method('getManagerForClass')
581
            ->willReturn($em);
582
583
        $manager = new ModelManager($registry);
584
        $result = $manager->getIdentifierValues($entity);
585
586
        $this->assertSame((string) $entity->getId()->getId(), $result[0]);
587
    }
588
589
    public function testAssociationIdentifierType(): void
590
    {
591
        $entity = new ContainerEntity(new AssociatedEntity(42, new EmbeddedEntity()), new EmbeddedEntity());
592
593
        $meta = $this->createMock(ClassMetadata::class);
594
        $meta->expects($this->any())
595
            ->method('getIdentifierValues')
596
            ->willReturn([$entity->getAssociatedEntity()->getPlainField()]);
597
        $meta->expects($this->any())
598
            ->method('getTypeOfField')
599
            ->willReturn(null);
600
601
        $mf = $this->createMock(ClassMetadataFactory::class);
602
        $mf->expects($this->any())
603
            ->method('getMetadataFor')
604
            ->willReturn($meta);
605
606
        $platform = $this->createMock(PostgreSqlPlatform::class);
607
        $platform->expects($this->never())
608
            ->method('hasDoctrineTypeMappingFor');
609
610
        $conn = $this->createMock(Connection::class);
611
        $conn->expects($this->any())
612
            ->method('getDatabasePlatform')
613
            ->willReturn($platform);
614
615
        $em = $this->createMock(EntityManager::class);
616
        $em->expects($this->any())
617
            ->method('getMetadataFactory')
618
            ->willReturn($mf);
619
        $em->expects($this->any())
620
            ->method('getConnection')
621
            ->willReturn($conn);
622
623
        $registry = $this->createMock(ManagerRegistry::class);
624
        $registry->expects($this->any())
625
            ->method('getManagerForClass')
626
            ->willReturn($em);
627
628
        $manager = new ModelManager($registry);
629
        $result = $manager->getIdentifierValues($entity);
630
631
        $this->assertSame(42, $result[0]);
632
    }
633
634
    /**
635
     * [sortBy, sortOrder, isAddOrderBy].
636
     *
637
     * @return array
638
     */
639
    public function getSortableInDataSourceIteratorDataProvider()
640
    {
641
        return [
642
            [null, null, false],
643
            ['', 'ASC', false],
644
            ['field', 'ASC', true],
645
            ['field', null, true],
646
        ];
647
    }
648
649
    /**
650
     * @dataProvider getSortableInDataSourceIteratorDataProvider
651
     *
652
     * @param string|null $sortBy
653
     * @param string|null $sortOrder
654
     * @param bool        $isAddOrderBy
655
     */
656
    public function testSortableInDataSourceIterator($sortBy, $sortOrder, $isAddOrderBy): void
657
    {
658
        $datagrid = $this->getMockForAbstractClass(DatagridInterface::class);
659
        $configuration = $this->getMockBuilder(Configuration::class)->getMock();
660
        $configuration->expects($this->any())
661
            ->method('getDefaultQueryHints')
662
            ->willReturn([]);
663
664
        $em = $this->getMockBuilder(EntityManager::class)
665
            ->disableOriginalConstructor()
666
            ->getMock();
667
668
        $em->expects($this->any())
669
            ->method('getConfiguration')
670
            ->willReturn($configuration);
671
672
        $queryBuilder = $this->getMockBuilder(QueryBuilder::class)
673
            ->setConstructorArgs([$em])
674
            ->getMock();
675
        $query = new Query($em);
676
677
        $proxyQuery = $this->getMockBuilder(ProxyQuery::class)
678
            ->setConstructorArgs([$queryBuilder])
679
            ->setMethods(['getSortBy', 'getSortOrder', 'getRootAliases'])
680
            ->getMock();
681
682
        $proxyQuery->expects($this->any())
683
            ->method('getSortOrder')
684
            ->willReturn($sortOrder);
685
686
        $proxyQuery->expects($this->any())
687
            ->method('getSortBy')
688
            ->willReturn($sortBy);
689
690
        $queryBuilder->expects($isAddOrderBy ? $this->atLeastOnce() : $this->never())
691
            ->method('addOrderBy');
692
693
        $proxyQuery->expects($this->any())
694
            ->method('getRootAliases')
695
            ->willReturn(['a']);
696
697
        $queryBuilder->expects($this->any())
698
            ->method('getQuery')
699
            ->willReturn($query);
700
701
        $datagrid->expects($this->any())
702
            ->method('getQuery')
703
            ->willReturn($proxyQuery);
704
705
        $registry = $this->getMockBuilder(RegistryInterface::class)->getMock();
706
        $manager = new ModelManager($registry);
707
        $manager->getDataSourceIterator($datagrid, []);
708
709
        if ($isAddOrderBy) {
710
            $this->assertArrayHasKey($key = 'doctrine.customTreeWalkers', $hints = $query->getHints());
711
            $this->assertContains(OrderByToSelectWalker::class, $hints[$key]);
712
        }
713
    }
714
715
    public function testModelReverseTransform(): void
716
    {
717
        $class = SimpleEntity::class;
718
719
        $metadataFactory = $this->createMock(ClassMetadataFactory::class);
720
        $modelManager = $this->createMock(ObjectManager::class);
721
        $registry = $this->createMock(ManagerRegistry::class);
722
723
        $classMetadata = new ClassMetadata($class);
724
        $classMetadata->reflClass = new \ReflectionClass($class);
725
726
        $modelManager->expects($this->once())
727
            ->method('getMetadataFactory')
728
            ->willReturn($metadataFactory);
729
        $metadataFactory->expects($this->once())
730
            ->method('getMetadataFor')
731
            ->with($class)
732
            ->willReturn($classMetadata);
733
        $registry->expects($this->once())
734
            ->method('getManagerForClass')
735
            ->with($class)
736
            ->willReturn($modelManager);
737
738
        $manager = new ModelManager($registry);
739
        $this->assertInstanceOf($class, $object = $manager->modelReverseTransform(
740
            $class,
741
            [
742
                'schmeckles' => 42,
743
                'multi_word_property' => 'hello',
744
            ]
745
        ));
746
        $this->assertSame(42, $object->getSchmeckles());
747
        $this->assertSame('hello', $object->getMultiWordProperty());
748
    }
749
750
    public function testCollections(): void
751
    {
752
        $registry = $this->createMock(ManagerRegistry::class);
753
        $model = new ModelManager($registry);
754
755
        $collection = $model->getModelCollectionInstance('whyDoWeEvenHaveThisParameter');
756
        $this->assertInstanceOf(ArrayCollection::class, $collection);
757
758
        $item1 = 'item1';
759
        $item2 = 'item2';
760
        $model->collectionAddElement($collection, $item1);
0 ignored issues
show
Bug introduced by
It seems like $collection defined by $model->getModelCollecti...EvenHaveThisParameter') on line 755 can also be of type object<ArrayAccess>; however, Sonata\DoctrineORMAdminB...:collectionAddElement() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Documentation introduced by
$item1 is of type string, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
761
        $model->collectionAddElement($collection, $item2);
0 ignored issues
show
Documentation introduced by
$item2 is of type string, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
762
763
        $this->assertTrue($model->collectionHasElement($collection, $item1));
764
765
        $model->collectionRemoveElement($collection, $item1);
766
767
        $this->assertFalse($model->collectionHasElement($collection, $item1));
768
769
        $model->collectionClear($collection);
770
771
        $this->assertTrue($collection->isEmpty());
0 ignored issues
show
Bug introduced by
The method isEmpty cannot be called on $collection (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
772
    }
773
774
    public function testModelTransform(): void
775
    {
776
        $registry = $this->createMock(ManagerRegistry::class);
777
        $model = new ModelManager($registry);
778
779
        $result = $model->modelTransform('thisIsNotUsed', 'doWeNeedThisMethod');
0 ignored issues
show
Documentation introduced by
'doWeNeedThisMethod' is of type string, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
780
781
        $this->assertSame('doWeNeedThisMethod', $result);
782
    }
783
784
    public function testGetPaginationParameters(): void
785
    {
786
        $datagrid = $this->createMock(DatagridInterface::class);
787
        $filter = $this->createMock(FilterInterface::class);
788
        $registry = $this->createMock(ManagerRegistry::class);
789
790
        $datagrid->expects($this->once())
791
            ->method('getValues')
792
            ->willReturn(['_sort_by' => $filter]);
793
794
        $filter->expects($this->once())
795
            ->method('getName')
796
            ->willReturn($name = 'test');
797
798
        $model = new ModelManager($registry);
799
800
        $result = $model->getPaginationParameters($datagrid, $page = 5);
801
802
        $this->assertSame($page, $result['filter']['_page']);
803
        $this->assertSame($name, $result['filter']['_sort_by']);
804
    }
805
806
    public function testGetModelInstanceException(): void
807
    {
808
        $registry = $this->createMock(ManagerRegistry::class);
809
810
        $model = new ModelManager($registry);
811
812
        $this->expectException(\RuntimeException::class);
813
814
        $model->getModelInstance(AbstractEntity::class);
815
    }
816
817
    public function testGetModelInstanceForProtectedEntity(): void
818
    {
819
        $registry = $this->createMock(ManagerRegistry::class);
820
821
        $model = new ModelManager($registry);
822
823
        $this->assertInstanceOf(ProtectedEntity::class, $model->getModelInstance(ProtectedEntity::class));
824
    }
825
826
    public function testGetEntityManagerException(): void
827
    {
828
        $registry = $this->createMock(ManagerRegistry::class);
829
830
        $model = new ModelManager($registry);
831
832
        $this->expectException(\RuntimeException::class);
833
834
        $model->getEntityManager(VersionedEntity::class);
835
    }
836
837
    public function testGetNewFieldDescriptionInstanceException(): void
838
    {
839
        $registry = $this->createMock(ManagerRegistry::class);
840
841
        $model = new ModelManager($registry);
842
843
        $this->expectException(\RuntimeException::class);
844
845
        $model->getNewFieldDescriptionInstance(VersionedEntity::class, [], []);
0 ignored issues
show
Documentation introduced by
array() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
846
    }
847
848
    /**
849
     * @dataProvider createUpdateRemoveData
850
     */
851
    public function testCreate($exception): void
852
    {
853
        $registry = $this->createMock(ManagerRegistry::class);
854
855
        $entityManger = $this->createMock(EntityManager::class);
856
857
        $registry->expects($this->once())
858
            ->method('getManagerForClass')
859
            ->willReturn($entityManger);
860
861
        $entityManger->expects($this->once())
862
            ->method('persist');
863
864
        $entityManger->expects($this->once())
865
            ->method('flush')
866
            ->willThrowException($exception);
867
868
        $model = new ModelManager($registry);
869
870
        $this->expectException(ModelManagerException::class);
871
872
        $model->create(new VersionedEntity());
873
    }
874
875
    public function createUpdateRemoveData()
876
    {
877
        return [
878
            'PDOException' => [
879
                new \PDOException(),
880
            ],
881
            'DBALException' => [
882
                new DBALException(),
883
            ],
884
        ];
885
    }
886
887
    /**
888
     * @dataProvider createUpdateRemoveData
889
     */
890
    public function testUpdate($exception): void
891
    {
892
        $registry = $this->createMock(ManagerRegistry::class);
893
894
        $entityManger = $this->createMock(EntityManager::class);
895
896
        $registry->expects($this->once())
897
            ->method('getManagerForClass')
898
            ->willReturn($entityManger);
899
900
        $entityManger->expects($this->once())
901
            ->method('persist');
902
903
        $entityManger->expects($this->once())
904
            ->method('flush')
905
            ->willThrowException($exception);
906
907
        $model = new ModelManager($registry);
908
909
        $this->expectException(ModelManagerException::class);
910
911
        $model->update(new VersionedEntity());
912
    }
913
914
    /**
915
     * @dataProvider createUpdateRemoveData
916
     */
917
    public function testRemove($exception): void
918
    {
919
        $registry = $this->createMock(ManagerRegistry::class);
920
921
        $entityManger = $this->createMock(EntityManager::class);
922
923
        $registry->expects($this->once())
924
            ->method('getManagerForClass')
925
            ->willReturn($entityManger);
926
927
        $entityManger->expects($this->once())
928
            ->method('remove');
929
930
        $entityManger->expects($this->once())
931
            ->method('flush')
932
            ->willThrowException($exception);
933
934
        $model = new ModelManager($registry);
935
936
        $this->expectException(ModelManagerException::class);
937
938
        $model->delete(new VersionedEntity());
939
    }
940
941
    public function testFindBadId(): void
942
    {
943
        $registry = $this->createMock(ManagerRegistry::class);
944
945
        $model = new ModelManager($registry);
946
947
        $this->assertNull($model->find('notImportant', null));
948
    }
949
950
    public function testGetUrlsafeIdentifierException(): void
951
    {
952
        $registry = $this->createMock(ManagerRegistry::class);
953
954
        $model = new ModelManager($registry);
955
956
        $this->expectException(\RuntimeException::class);
957
958
        $model->getNormalizedIdentifier('test');
0 ignored issues
show
Documentation introduced by
'test' is of type string, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
959
    }
960
961
    public function testGetUrlsafeIdentifierNull(): void
962
    {
963
        $registry = $this->createMock(ManagerRegistry::class);
964
965
        $model = new ModelManager($registry);
966
967
        $this->assertNull($model->getNormalizedIdentifier(null));
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
968
    }
969
970
    private function getMetadata($class, $isVersioned)
971
    {
972
        $metadata = new ClassMetadata($class);
973
974
        $metadata->isVersioned = $isVersioned;
975
976
        if ($isVersioned) {
977
            $versionField = 'version';
978
            $metadata->versionField = $versionField;
979
            $metadata->reflFields[$versionField] = new \ReflectionProperty($class, $versionField);
980
        }
981
982
        return $metadata;
983
    }
984
}
985