Completed
Pull Request — 3.x (#1030)
by Javier
01:48
created

ModelManagerTest::getWrongEntities()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
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\DBAL\Connection;
18
use Doctrine\DBAL\DBALException;
19
use Doctrine\DBAL\Platforms\MySqlPlatform;
20
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
21
use Doctrine\DBAL\Types\Type;
22
use Doctrine\ORM\Configuration;
23
use Doctrine\ORM\EntityManager;
24
use Doctrine\ORM\EntityManagerInterface;
25
use Doctrine\ORM\Mapping\ClassMetadata;
26
use Doctrine\ORM\Mapping\ClassMetadataFactory;
27
use Doctrine\ORM\Mapping\ClassMetadataInfo;
28
use Doctrine\ORM\OptimisticLockException;
29
use Doctrine\ORM\Query;
30
use Doctrine\ORM\QueryBuilder;
31
use Doctrine\Persistence\ManagerRegistry;
32
use Doctrine\Persistence\ObjectManager;
33
use PHPUnit\Framework\MockObject\MockObject;
34
use PHPUnit\Framework\TestCase;
35
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
36
use Sonata\AdminBundle\Datagrid\Datagrid;
37
use Sonata\AdminBundle\Datagrid\DatagridInterface;
38
use Sonata\AdminBundle\Exception\LockException;
39
use Sonata\AdminBundle\Exception\ModelManagerException;
40
use Sonata\DoctrineORMAdminBundle\Admin\FieldDescription;
41
use Sonata\DoctrineORMAdminBundle\Datagrid\OrderByToSelectWalker;
42
use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery;
43
use Sonata\DoctrineORMAdminBundle\Model\ModelManager;
44
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\ProductIdType;
45
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\UuidBinaryType;
46
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\UuidType;
47
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\ValueObjectWithMagicToStringImpl;
48
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\ValueObjectWithToStringImpl;
49
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\AbstractEntity;
50
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\AssociatedEntity;
51
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\ContainerEntity;
52
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\Embeddable\EmbeddedEntity;
53
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\Embeddable\SubEmbeddedEntity;
54
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\Product;
55
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\ProductId;
56
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\ProtectedEntity;
57
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\SimpleEntity;
58
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\UuidBinaryEntity;
59
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\UuidEntity;
60
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\VersionedEntity;
61
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Util\NonIntegerIdentifierTestClass;
62
use Symfony\Bridge\Doctrine\RegistryInterface;
63
64
class ModelManagerTest extends TestCase
65
{
66
    public static function setUpBeforeClass(): void
67
    {
68
        if (!Type::hasType(UuidType::NAME)) {
69
            Type::addType(UuidType::NAME, UuidType::class);
70
        }
71
        if (!Type::hasType(UuidBinaryType::NAME)) {
72
            Type::addType(UuidBinaryType::NAME, UuidBinaryType::class);
73
        }
74
        if (!Type::hasType(ProductIdType::NAME)) {
75
            Type::addType(ProductIdType::NAME, ProductIdType::class);
76
        }
77
    }
78
79
    public function valueObjectDataProvider(): array
80
    {
81
        return [
82
            'value object with toString implementation' => [ValueObjectWithToStringImpl::class],
83
            'value object with magic toString implementation' => [ValueObjectWithMagicToStringImpl::class],
84
        ];
85
    }
86
87
    /**
88
     * @dataProvider valueObjectDataProvider
89
     */
90
    public function testGetIdentifierValuesWhenIdentifierIsValueObjectWithToStringMethod(string $vbClassName): void
91
    {
92
        $entity = new UuidBinaryEntity(new $vbClassName('a7ef873a-e7b5-11e9-81b4-2a2ae2dbcce4'));
93
94
        $platform = $this->createMock(MySqlPlatform::class);
95
96
        $connection = $this->createMock(Connection::class);
97
        $connection->method('getDatabasePlatform')->willReturn($platform);
98
99
        $classMetadata = $this->createMock(ClassMetadataInfo::class);
100
        $classMetadata->method('getIdentifierValues')->willReturn([$entity->getId()]);
101
        $classMetadata->method('getTypeOfField')->willReturn(UuidBinaryType::NAME);
102
103
        $classMetadataFactory = $this->createMock(ClassMetadataFactory::class);
104
        $classMetadataFactory->method('getMetadataFor')->willReturn($classMetadata);
105
106
        $entityManager = $this->createMock(EntityManager::class);
107
        $entityManager->method('getMetadataFactory')->willReturn($classMetadataFactory);
108
        $entityManager->method('getConnection')->willReturn($connection);
109
110
        $registry = $this->createMock(RegistryInterface::class);
111
        $registry->method('getManagerForClass')->willReturn($entityManager);
112
113
        $manager = new ModelManager($registry);
114
115
        $this->assertSame(
116
            ['a7ef873a-e7b5-11e9-81b4-2a2ae2dbcce4'],
117
            $manager->getIdentifierValues($entity)
118
        );
119
    }
120
121
    public function testInstantiateWithDeprecatedRegistryInterface(): void
122
    {
123
        $registry = $this->createMock(RegistryInterface::class);
124
        $manager = new ModelManager($registry);
125
        $em = $this->createMock(EntityManagerInterface::class);
126
127
        $registry->expects($this->once())
128
            ->method('getManagerForClass')
129
            ->with('x')
130
            ->willReturn($em)
131
        ;
132
        $this->assertSame($em, $manager->getEntityManager('x'));
133
    }
134
135
    public function testSortParameters(): void
136
    {
137
        $registry = $this->createMock(ManagerRegistry::class);
138
139
        $manager = new ModelManager($registry);
140
141
        $datagrid1 = $this->createMock(Datagrid::class);
142
        $datagrid2 = $this->createMock(Datagrid::class);
143
144
        $field1 = new FieldDescription();
145
        $field1->setName('field1');
146
147
        $field2 = new FieldDescription();
148
        $field2->setName('field2');
149
150
        $field3 = new FieldDescription();
151
        $field3->setName('field3');
152
        $field3->setOption('sortable', 'field3sortBy');
153
154
        $datagrid1
155
            ->expects($this->any())
156
            ->method('getValues')
157
            ->willReturn([
158
                '_sort_by' => $field1,
159
                '_sort_order' => 'ASC',
160
            ]);
161
162
        $datagrid2
163
            ->expects($this->any())
164
            ->method('getValues')
165
            ->willReturn([
166
                '_sort_by' => $field3,
167
                '_sort_order' => 'ASC',
168
            ]);
169
170
        $parameters = $manager->getSortParameters($field1, $datagrid1);
171
172
        $this->assertSame('DESC', $parameters['filter']['_sort_order']);
173
        $this->assertSame('field1', $parameters['filter']['_sort_by']);
174
175
        $parameters = $manager->getSortParameters($field2, $datagrid1);
176
177
        $this->assertSame('ASC', $parameters['filter']['_sort_order']);
178
        $this->assertSame('field2', $parameters['filter']['_sort_by']);
179
180
        $parameters = $manager->getSortParameters($field3, $datagrid1);
181
182
        $this->assertSame('ASC', $parameters['filter']['_sort_order']);
183
        $this->assertSame('field3sortBy', $parameters['filter']['_sort_by']);
184
185
        $parameters = $manager->getSortParameters($field3, $datagrid2);
186
187
        $this->assertSame('DESC', $parameters['filter']['_sort_order']);
188
        $this->assertSame('field3sortBy', $parameters['filter']['_sort_by']);
189
    }
190
191
    public function getVersionDataProvider()
192
    {
193
        return [
194
            [true],
195
            [false],
196
        ];
197
    }
198
199
    /**
200
     * @dataProvider getVersionDataProvider
201
     */
202
    public function testGetVersion($isVersioned): void
203
    {
204
        $object = new VersionedEntity();
205
206
        $modelManager = $this->getMockBuilder(ModelManager::class)
207
            ->disableOriginalConstructor()
208
            ->setMethods(['getMetadata'])
209
            ->getMock();
210
211
        $metadata = $this->getMetadata(\get_class($object), $isVersioned);
212
213
        $modelManager->expects($this->any())
214
            ->method('getMetadata')
215
            ->willReturn($metadata);
216
217
        if ($isVersioned) {
218
            $object->version = 123;
219
220
            $this->assertNotNull($modelManager->getLockVersion($object));
221
        } else {
222
            $this->assertNull($modelManager->getLockVersion($object));
223
        }
224
    }
225
226
    public function lockDataProvider()
227
    {
228
        return [
229
            [true,  false],
230
            [true,  true],
231
            [false, false],
232
        ];
233
    }
234
235
    /**
236
     * @dataProvider lockDataProvider
237
     */
238
    public function testLock($isVersioned, $expectsException): void
239
    {
240
        $object = new VersionedEntity();
241
242
        $em = $this->getMockBuilder(EntityManager::class)
243
            ->disableOriginalConstructor()
244
            ->setMethods(['lock'])
245
            ->getMock();
246
247
        $modelManager = $this->getMockBuilder(ModelManager::class)
248
            ->disableOriginalConstructor()
249
            ->setMethods(['getMetadata', 'getEntityManager'])
250
            ->getMock();
251
252
        $modelManager->expects($this->any())
253
            ->method('getEntityManager')
254
            ->willReturn($em);
255
256
        $metadata = $this->getMetadata(\get_class($object), $isVersioned);
257
258
        $modelManager->expects($this->any())
259
            ->method('getMetadata')
260
            ->willReturn($metadata);
261
262
        $em->expects($isVersioned ? $this->once() : $this->never())
263
            ->method('lock');
264
265
        if ($expectsException) {
266
            $em->expects($this->once())
267
                ->method('lock')
268
                ->will($this->throwException(OptimisticLockException::lockFailed($object)));
269
270
            $this->expectException(LockException::class);
271
        }
272
273
        $modelManager->lock($object, 123);
274
    }
275
276
    public function testGetParentMetadataForProperty(): void
277
    {
278
        $containerEntityClass = ContainerEntity::class;
279
        $associatedEntityClass = AssociatedEntity::class;
280
        $embeddedEntityClass = EmbeddedEntity::class;
281
        $modelManagerClass = ModelManager::class;
282
283
        $em = $this->createMock(EntityManager::class);
284
285
        /** @var MockObject|ModelManager $modelManager */
286
        $modelManager = $this->getMockBuilder($modelManagerClass)
287
            ->disableOriginalConstructor()
288
            ->setMethods(['getMetadata', 'getEntityManager'])
289
            ->getMock();
290
291
        $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...
292
            ->method('getEntityManager')
293
            ->willReturn($em);
294
295
        $containerEntityMetadata = $this->getMetadataForContainerEntity();
296
        $associatedEntityMetadata = $this->getMetadataForAssociatedEntity();
297
        $embeddedEntityMetadata = $this->getMetadataForEmbeddedEntity();
298
299
        $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...
300
            ->willReturnMap(
301
                [
302
                        [$containerEntityClass, $containerEntityMetadata],
303
                        [$embeddedEntityClass, $embeddedEntityMetadata],
304
                        [$associatedEntityClass, $associatedEntityMetadata],
305
                    ]
306
            );
307
308
        /** @var ClassMetadata $metadata */
309
        list($metadata, $lastPropertyName) = $modelManager
310
            ->getParentMetadataForProperty($containerEntityClass, 'plainField');
311
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'integer');
312
313
        list($metadata, $lastPropertyName) = $modelManager
314
            ->getParentMetadataForProperty($containerEntityClass, 'associatedEntity.plainField');
315
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'string');
316
317
        list($metadata, $lastPropertyName) = $modelManager
318
            ->getParentMetadataForProperty($containerEntityClass, 'embeddedEntity.plainField');
319
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
320
321
        list($metadata, $lastPropertyName) = $modelManager
322
            ->getParentMetadataForProperty($containerEntityClass, 'associatedEntity.embeddedEntity.plainField');
323
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
324
325
        list($metadata, $lastPropertyName) = $modelManager
326
            ->getParentMetadataForProperty(
327
                $containerEntityClass,
328
                'associatedEntity.embeddedEntity.subEmbeddedEntity.plainField'
329
            );
330
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
331
    }
332
333
    public function getMetadataForEmbeddedEntity()
334
    {
335
        $metadata = new ClassMetadata(EmbeddedEntity::class);
336
337
        $metadata->fieldMappings = [
338
            'plainField' => [
339
                'fieldName' => 'plainField',
340
                'columnName' => 'plainField',
341
                'type' => 'boolean',
342
            ],
343
        ];
344
345
        return $metadata;
346
    }
347
348
    public function getMetadataForSubEmbeddedEntity()
349
    {
350
        $metadata = new ClassMetadata(SubEmbeddedEntity::class);
351
352
        $metadata->fieldMappings = [
353
            'plainField' => [
354
                'fieldName' => 'plainField',
355
                'columnName' => 'plainField',
356
                'type' => 'boolean',
357
            ],
358
        ];
359
360
        return $metadata;
361
    }
362
363
    public function getMetadataForAssociatedEntity()
364
    {
365
        $embeddedEntityClass = EmbeddedEntity::class;
366
        $subEmbeddedEntityClass = SubEmbeddedEntity::class;
367
368
        $metadata = new ClassMetadata(AssociatedEntity::class);
369
370
        $metadata->fieldMappings = [
371
            'plainField' => [
372
                'fieldName' => 'plainField',
373
                'columnName' => 'plainField',
374
                'type' => 'string',
375
            ],
376
        ];
377
378
        $metadata->embeddedClasses['embeddedEntity'] = [
379
            'class' => $embeddedEntityClass,
380
            'columnPrefix' => 'embedded_entity_',
381
        ];
382
        $metadata->embeddedClasses['embeddedEntity.subEmbeddedEntity'] = [
383
            'class' => $subEmbeddedEntityClass,
384
            'columnPrefix' => 'embedded_entity_sub_embedded_entity_',
385
            'declaredField' => 'embeddedEntity',
386
            'originalField' => 'subEmbeddedEntity',
387
        ];
388
389
        $metadata->inlineEmbeddable('embeddedEntity', $this->getMetadataForEmbeddedEntity());
390
        $metadata->inlineEmbeddable('embeddedEntity.subEmbeddedEntity', $this->getMetadataForSubEmbeddedEntity());
391
392
        return $metadata;
393
    }
394
395
    public function getMetadataForContainerEntity()
396
    {
397
        $containerEntityClass = ContainerEntity::class;
398
        $associatedEntityClass = AssociatedEntity::class;
399
        $embeddedEntityClass = EmbeddedEntity::class;
400
        $subEmbeddedEntityClass = SubEmbeddedEntity::class;
401
402
        $metadata = new ClassMetadata($containerEntityClass);
403
404
        $metadata->fieldMappings = [
405
            'plainField' => [
406
                'fieldName' => 'plainField',
407
                'columnName' => 'plainField',
408
                'type' => 'integer',
409
            ],
410
        ];
411
412
        $metadata->associationMappings['associatedEntity'] = [
413
            'fieldName' => 'associatedEntity',
414
            'targetEntity' => $associatedEntityClass,
415
            'sourceEntity' => $containerEntityClass,
416
        ];
417
418
        $metadata->embeddedClasses['embeddedEntity'] = [
419
            'class' => $embeddedEntityClass,
420
            'columnPrefix' => 'embeddedEntity',
421
        ];
422
        $metadata->embeddedClasses['embeddedEntity.subEmbeddedEntity'] = [
423
            'class' => $subEmbeddedEntityClass,
424
            'columnPrefix' => 'embedded_entity_sub_embedded_entity_',
425
            'declaredField' => 'embeddedEntity',
426
            'originalField' => 'subEmbeddedEntity',
427
        ];
428
429
        $metadata->inlineEmbeddable('embeddedEntity', $this->getMetadataForEmbeddedEntity());
430
        $metadata->inlineEmbeddable('embeddedEntity.subEmbeddedEntity', $this->getMetadataForSubEmbeddedEntity());
431
432
        return $metadata;
433
    }
434
435
    public function testGetIdentifierValuesForIdInObjectTypeBinaryToStringSupport(): void
436
    {
437
        $uuid = new NonIntegerIdentifierTestClass('efbcfc4b-8c43-4d42-aa4c-d707e55151ac');
438
439
        $entity = new UuidEntity($uuid);
440
441
        $meta = $this->createMock(ClassMetadata::class);
442
        $meta->expects($this->any())
443
            ->method('getIdentifierValues')
444
            ->willReturn([$entity->getId()]);
445
        $meta->expects($this->any())
446
            ->method('getTypeOfField')
447
            ->willReturn(UuidBinaryType::NAME); //'uuid_binary'
448
449
        $mf = $this->createMock(ClassMetadataFactory::class);
450
        $mf->expects($this->any())
451
            ->method('getMetadataFor')
452
            ->willReturn($meta);
453
454
        $platform = $this->createMock(PostgreSqlPlatform::class);
455
        $platform->expects($this->any())
456
            ->method('hasDoctrineTypeMappingFor')
457
            ->with(UuidBinaryType::NAME)
458
            ->willReturn(true);
459
        $platform->expects($this->any())
460
            ->method('getDoctrineTypeMapping')
461
            ->with(UuidBinaryType::NAME)
462
            ->willReturn('binary');
463
464
        $conn = $this->createMock(Connection::class);
465
        $conn->expects($this->any())
466
            ->method('getDatabasePlatform')
467
            ->willReturn($platform);
468
469
        $em = $this->createMock(EntityManager::class);
470
        $em->expects($this->any())
471
            ->method('getMetadataFactory')
472
            ->willReturn($mf);
473
        $em->expects($this->any())
474
            ->method('getConnection')
475
            ->willReturn($conn);
476
477
        $registry = $this->createMock(ManagerRegistry::class);
478
        $registry->expects($this->any())
479
            ->method('getManagerForClass')
480
            ->willReturn($em);
481
482
        $manager = new ModelManager($registry);
483
        $result = $manager->getIdentifierValues($entity);
484
485
        $this->assertSame($entity->getId()->toString(), $result[0]);
486
    }
487
488
    public function testNonIntegerIdentifierType(): void
489
    {
490
        $uuid = new NonIntegerIdentifierTestClass('efbcfc4b-8c43-4d42-aa4c-d707e55151ac');
491
        $entity = new UuidEntity($uuid);
492
493
        $meta = $this->createMock(ClassMetadata::class);
494
        $meta->expects($this->any())
495
            ->method('getIdentifierValues')
496
            ->willReturn([$entity->getId()]);
497
        $meta->expects($this->any())
498
            ->method('getTypeOfField')
499
            ->willReturn(UuidType::NAME);
500
501
        $mf = $this->createMock(ClassMetadataFactory::class);
502
        $mf->expects($this->any())
503
            ->method('getMetadataFor')
504
            ->willReturn($meta);
505
506
        $platform = $this->createMock(PostgreSqlPlatform::class);
507
        $platform->expects($this->any())
508
            ->method('hasDoctrineTypeMappingFor')
509
            ->with(UuidType::NAME)
510
            ->willReturn(false);
511
        $platform->expects($this->never())
512
            ->method('getDoctrineTypeMapping');
513
514
        $conn = $this->createMock(Connection::class);
515
        $conn->expects($this->any())
516
            ->method('getDatabasePlatform')
517
            ->willReturn($platform);
518
519
        $em = $this->createMock(EntityManager::class);
520
        $em->expects($this->any())
521
            ->method('getMetadataFactory')
522
            ->willReturn($mf);
523
        $em->expects($this->any())
524
            ->method('getConnection')
525
            ->willReturn($conn);
526
527
        $registry = $this->createMock(ManagerRegistry::class);
528
        $registry->expects($this->any())
529
            ->method('getManagerForClass')
530
            ->willReturn($em);
531
532
        $manager = new ModelManager($registry);
533
        $result = $manager->getIdentifierValues($entity);
534
535
        $this->assertSame($entity->getId()->toString(), $result[0]);
536
    }
537
538
    public function testIntegerIdentifierType(): void
539
    {
540
        $id = new ProductId(12345);
541
        $entity = new Product($id, 'Some product');
542
543
        $meta = $this->createMock(ClassMetadata::class);
544
        $meta->expects($this->any())
545
            ->method('getIdentifierValues')
546
            ->willReturn([$entity->getId()]);
547
        $meta->expects($this->any())
548
            ->method('getTypeOfField')
549
            ->willReturn(ProductIdType::NAME);
550
551
        $mf = $this->createMock(ClassMetadataFactory::class);
552
        $mf->expects($this->any())
553
            ->method('getMetadataFor')
554
            ->willReturn($meta);
555
556
        $platform = $this->createMock(PostgreSqlPlatform::class);
557
        $platform->expects($this->any())
558
            ->method('hasDoctrineTypeMappingFor')
559
            ->with(ProductIdType::NAME)
560
            ->willReturn(false);
561
        $platform->expects($this->never())
562
            ->method('getDoctrineTypeMapping');
563
564
        $conn = $this->createMock(Connection::class);
565
        $conn->expects($this->any())
566
            ->method('getDatabasePlatform')
567
            ->willReturn($platform);
568
569
        $em = $this->createMock(EntityManager::class);
570
        $em->expects($this->any())
571
            ->method('getMetadataFactory')
572
            ->willReturn($mf);
573
        $em->expects($this->any())
574
            ->method('getConnection')
575
            ->willReturn($conn);
576
577
        $registry = $this->createMock(ManagerRegistry::class);
578
        $registry->expects($this->any())
579
            ->method('getManagerForClass')
580
            ->willReturn($em);
581
582
        $manager = new ModelManager($registry);
583
        $result = $manager->getIdentifierValues($entity);
584
585
        $this->assertSame((string) $entity->getId()->getId(), $result[0]);
586
    }
587
588
    public function testAssociationIdentifierType(): void
589
    {
590
        $entity = new ContainerEntity(new AssociatedEntity(42, new EmbeddedEntity()), new EmbeddedEntity());
591
592
        $meta = $this->createMock(ClassMetadata::class);
593
        $meta->expects($this->any())
594
            ->method('getIdentifierValues')
595
            ->willReturn([$entity->getAssociatedEntity()->getPlainField()]);
596
        $meta->expects($this->any())
597
            ->method('getTypeOfField')
598
            ->willReturn(null);
599
600
        $mf = $this->createMock(ClassMetadataFactory::class);
601
        $mf->expects($this->any())
602
            ->method('getMetadataFor')
603
            ->willReturn($meta);
604
605
        $platform = $this->createMock(PostgreSqlPlatform::class);
606
        $platform->expects($this->never())
607
            ->method('hasDoctrineTypeMappingFor');
608
609
        $conn = $this->createMock(Connection::class);
610
        $conn->expects($this->any())
611
            ->method('getDatabasePlatform')
612
            ->willReturn($platform);
613
614
        $em = $this->createMock(EntityManager::class);
615
        $em->expects($this->any())
616
            ->method('getMetadataFactory')
617
            ->willReturn($mf);
618
        $em->expects($this->any())
619
            ->method('getConnection')
620
            ->willReturn($conn);
621
622
        $registry = $this->createMock(ManagerRegistry::class);
623
        $registry->expects($this->any())
624
            ->method('getManagerForClass')
625
            ->willReturn($em);
626
627
        $manager = new ModelManager($registry);
628
        $result = $manager->getIdentifierValues($entity);
629
630
        $this->assertSame(42, $result[0]);
631
    }
632
633
    /**
634
     * [sortBy, sortOrder, isAddOrderBy].
635
     *
636
     * @return array
637
     */
638
    public function getSortableInDataSourceIteratorDataProvider()
639
    {
640
        return [
641
            [null, null, false],
642
            ['', 'ASC', false],
643
            ['field', 'ASC', true],
644
            ['field', null, true],
645
        ];
646
    }
647
648
    /**
649
     * @dataProvider getSortableInDataSourceIteratorDataProvider
650
     *
651
     * @param string|null $sortBy
652
     * @param string|null $sortOrder
653
     * @param bool        $isAddOrderBy
654
     */
655
    public function testSortableInDataSourceIterator($sortBy, $sortOrder, $isAddOrderBy): void
656
    {
657
        $datagrid = $this->getMockForAbstractClass(DatagridInterface::class);
658
        $configuration = $this->getMockBuilder(Configuration::class)->getMock();
659
        $configuration->expects($this->any())
660
            ->method('getDefaultQueryHints')
661
            ->willReturn([]);
662
663
        $em = $this->getMockBuilder(EntityManager::class)
664
            ->disableOriginalConstructor()
665
            ->getMock();
666
667
        $em->expects($this->any())
668
            ->method('getConfiguration')
669
            ->willReturn($configuration);
670
671
        $queryBuilder = $this->getMockBuilder(QueryBuilder::class)
672
            ->setConstructorArgs([$em])
673
            ->getMock();
674
        $query = new Query($em);
675
676
        $proxyQuery = $this->getMockBuilder(ProxyQuery::class)
677
            ->setConstructorArgs([$queryBuilder])
678
            ->setMethods(['getSortBy', 'getSortOrder', 'getRootAliases'])
679
            ->getMock();
680
681
        $proxyQuery->expects($this->any())
682
            ->method('getSortOrder')
683
            ->willReturn($sortOrder);
684
685
        $proxyQuery->expects($this->any())
686
            ->method('getSortBy')
687
            ->willReturn($sortBy);
688
689
        $queryBuilder->expects($isAddOrderBy ? $this->atLeastOnce() : $this->never())
690
            ->method('addOrderBy');
691
692
        $proxyQuery->expects($this->any())
693
            ->method('getRootAliases')
694
            ->willReturn(['a']);
695
696
        $queryBuilder->expects($this->any())
697
            ->method('getQuery')
698
            ->willReturn($query);
699
700
        $datagrid->expects($this->any())
701
            ->method('getQuery')
702
            ->willReturn($proxyQuery);
703
704
        $registry = $this->getMockBuilder(RegistryInterface::class)->getMock();
705
        $manager = new ModelManager($registry);
706
        $manager->getDataSourceIterator($datagrid, []);
707
708
        if ($isAddOrderBy) {
709
            $this->assertArrayHasKey($key = 'doctrine.customTreeWalkers', $hints = $query->getHints());
710
            $this->assertContains(OrderByToSelectWalker::class, $hints[$key]);
711
        }
712
    }
713
714
    public function testModelReverseTransform(): void
715
    {
716
        $class = SimpleEntity::class;
717
718
        $metadataFactory = $this->createMock(ClassMetadataFactory::class);
719
        $modelManager = $this->createMock(ObjectManager::class);
720
        $registry = $this->createMock(ManagerRegistry::class);
721
722
        $classMetadata = new ClassMetadata($class);
723
        $classMetadata->reflClass = new \ReflectionClass($class);
724
725
        $modelManager->expects($this->once())
726
            ->method('getMetadataFactory')
727
            ->willReturn($metadataFactory);
728
        $metadataFactory->expects($this->once())
729
            ->method('getMetadataFor')
730
            ->with($class)
731
            ->willReturn($classMetadata);
732
        $registry->expects($this->once())
733
            ->method('getManagerForClass')
734
            ->with($class)
735
            ->willReturn($modelManager);
736
737
        $manager = new ModelManager($registry);
738
        $this->assertInstanceOf($class, $object = $manager->modelReverseTransform(
739
            $class,
740
            [
741
                'schmeckles' => 42,
742
                'multi_word_property' => 'hello',
743
            ]
744
        ));
745
        $this->assertSame(42, $object->getSchmeckles());
746
        $this->assertSame('hello', $object->getMultiWordProperty());
747
    }
748
749
    public function testCollections(): void
750
    {
751
        $registry = $this->createMock(ManagerRegistry::class);
752
        $model = new ModelManager($registry);
753
754
        $collection = $model->getModelCollectionInstance('whyDoWeEvenHaveThisParameter');
755
        $this->assertInstanceOf(ArrayCollection::class, $collection);
756
757
        $item1 = 'item1';
758
        $item2 = 'item2';
759
        $model->collectionAddElement($collection, $item1);
0 ignored issues
show
Bug introduced by
It seems like $collection defined by $model->getModelCollecti...EvenHaveThisParameter') on line 754 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...
760
        $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...
761
762
        $this->assertTrue($model->collectionHasElement($collection, $item1));
763
764
        $model->collectionRemoveElement($collection, $item1);
765
766
        $this->assertFalse($model->collectionHasElement($collection, $item1));
767
768
        $model->collectionClear($collection);
769
770
        $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...
771
    }
772
773
    public function testModelTransform(): void
774
    {
775
        $registry = $this->createMock(ManagerRegistry::class);
776
        $model = new ModelManager($registry);
777
778
        $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...
779
780
        $this->assertSame('doWeNeedThisMethod', $result);
781
    }
782
783
    public function testGetPaginationParameters(): void
784
    {
785
        $datagrid = $this->createMock(DatagridInterface::class);
786
        $field = $this->createMock(FieldDescriptionInterface::class);
787
        $registry = $this->createMock(ManagerRegistry::class);
788
789
        $datagrid->expects($this->once())
790
            ->method('getValues')
791
            ->willReturn(['_sort_by' => $field]);
792
793
        $field->expects($this->once())
794
            ->method('getName')
795
            ->willReturn($name = 'test');
796
797
        $model = new ModelManager($registry);
798
799
        $result = $model->getPaginationParameters($datagrid, $page = 5);
800
801
        $this->assertSame($page, $result['filter']['_page']);
802
        $this->assertSame($name, $result['filter']['_sort_by']);
803
    }
804
805
    public function testGetModelInstanceException(): void
806
    {
807
        $registry = $this->createMock(ManagerRegistry::class);
808
809
        $model = new ModelManager($registry);
810
811
        $this->expectException(\RuntimeException::class);
812
813
        $model->getModelInstance(AbstractEntity::class);
814
    }
815
816
    public function testGetModelInstanceForProtectedEntity(): void
817
    {
818
        $registry = $this->createMock(ManagerRegistry::class);
819
820
        $model = new ModelManager($registry);
821
822
        $this->assertInstanceOf(ProtectedEntity::class, $model->getModelInstance(ProtectedEntity::class));
823
    }
824
825
    public function testGetEntityManagerException(): void
826
    {
827
        $registry = $this->createMock(ManagerRegistry::class);
828
829
        $model = new ModelManager($registry);
830
831
        $this->expectException(\RuntimeException::class);
832
833
        $model->getEntityManager(VersionedEntity::class);
834
    }
835
836
    public function testGetNewFieldDescriptionInstanceException(): void
837
    {
838
        $registry = $this->createMock(ManagerRegistry::class);
839
840
        $model = new ModelManager($registry);
841
842
        $this->expectException(\RuntimeException::class);
843
844
        $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...
845
    }
846
847
    /**
848
     * @dataProvider createUpdateRemoveData
849
     */
850
    public function testCreate($exception): void
851
    {
852
        $registry = $this->createMock(ManagerRegistry::class);
853
854
        $entityManger = $this->createMock(EntityManager::class);
855
856
        $registry->expects($this->once())
857
            ->method('getManagerForClass')
858
            ->willReturn($entityManger);
859
860
        $entityManger->expects($this->once())
861
            ->method('persist');
862
863
        $entityManger->expects($this->once())
864
            ->method('flush')
865
            ->willThrowException($exception);
866
867
        $model = new ModelManager($registry);
868
869
        $this->expectException(ModelManagerException::class);
870
871
        $model->create(new VersionedEntity());
872
    }
873
874
    public function createUpdateRemoveData()
875
    {
876
        return [
877
            'PDOException' => [
878
                new \PDOException(),
879
            ],
880
            'DBALException' => [
881
                new DBALException(),
882
            ],
883
        ];
884
    }
885
886
    /**
887
     * @dataProvider createUpdateRemoveData
888
     */
889
    public function testUpdate($exception): void
890
    {
891
        $registry = $this->createMock(ManagerRegistry::class);
892
893
        $entityManger = $this->createMock(EntityManager::class);
894
895
        $registry->expects($this->once())
896
            ->method('getManagerForClass')
897
            ->willReturn($entityManger);
898
899
        $entityManger->expects($this->once())
900
            ->method('persist');
901
902
        $entityManger->expects($this->once())
903
            ->method('flush')
904
            ->willThrowException($exception);
905
906
        $model = new ModelManager($registry);
907
908
        $this->expectException(ModelManagerException::class);
909
910
        $model->update(new VersionedEntity());
911
    }
912
913
    /**
914
     * @dataProvider createUpdateRemoveData
915
     */
916
    public function testRemove($exception): void
917
    {
918
        $registry = $this->createMock(ManagerRegistry::class);
919
920
        $entityManger = $this->createMock(EntityManager::class);
921
922
        $registry->expects($this->once())
923
            ->method('getManagerForClass')
924
            ->willReturn($entityManger);
925
926
        $entityManger->expects($this->once())
927
            ->method('remove');
928
929
        $entityManger->expects($this->once())
930
            ->method('flush')
931
            ->willThrowException($exception);
932
933
        $model = new ModelManager($registry);
934
935
        $this->expectException(ModelManagerException::class);
936
937
        $model->delete(new VersionedEntity());
938
    }
939
940
    public function testFindBadId(): void
941
    {
942
        $registry = $this->createMock(ManagerRegistry::class);
943
944
        $model = new ModelManager($registry);
945
946
        $this->assertNull($model->find('notImportant', null));
947
    }
948
949
    /**
950
     * @dataProvider getWrongEntities
951
     *
952
     * @param mixed $entity
953
     */
954
    public function testNormalizedIdentifierException($entity): void
955
    {
956
        $registry = $this->createMock(ManagerRegistry::class);
957
958
        $model = new ModelManager($registry);
959
960
        $this->expectException(\RuntimeException::class);
961
962
        $model->getNormalizedIdentifier($entity);
963
    }
964
965
    public function getWrongEntities(): iterable
966
    {
967
        yield [0];
968
        yield [1];
969
        yield [false];
970
        yield [true];
971
        yield [[]];
972
        yield [''];
973
        yield ['sonata-project'];
974
    }
975
976
    public function testGetUrlsafeIdentifierNull(): void
977
    {
978
        $registry = $this->createMock(ManagerRegistry::class);
979
980
        $model = new ModelManager($registry);
981
982
        $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...
983
    }
984
985
    private function getMetadata($class, $isVersioned)
986
    {
987
        $metadata = new ClassMetadata($class);
988
989
        $metadata->isVersioned = $isVersioned;
990
991
        if ($isVersioned) {
992
            $versionField = 'version';
993
            $metadata->versionField = $versionField;
994
            $metadata->reflFields[$versionField] = new \ReflectionProperty($class, $versionField);
995
        }
996
997
        return $metadata;
998
    }
999
}
1000