Completed
Push — master ( 8a9820...9607d2 )
by
unknown
02:10 queued 10s
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(): array
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(): array
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 testNonIntegerIdentifierType(): void
436
    {
437
        $uuid = new NonIntegerIdentifierTestClass('efbcfc4b-8c43-4d42-aa4c-d707e55151ac');
438
        $entity = new UuidEntity($uuid);
439
440
        $meta = $this->createMock(ClassMetadata::class);
441
        $meta->expects($this->any())
442
            ->method('getIdentifierValues')
443
            ->willReturn([$entity->getId()]);
444
        $meta->expects($this->any())
445
            ->method('getTypeOfField')
446
            ->willReturn(UuidType::NAME);
447
448
        $mf = $this->createMock(ClassMetadataFactory::class);
449
        $mf->expects($this->any())
450
            ->method('getMetadataFor')
451
            ->willReturn($meta);
452
453
        $platform = $this->createMock(PostgreSqlPlatform::class);
454
        $platform->expects($this->any())
455
            ->method('hasDoctrineTypeMappingFor')
456
            ->with(UuidType::NAME)
457
            ->willReturn(false);
458
        $platform->expects($this->never())
459
            ->method('getDoctrineTypeMapping');
460
461
        $conn = $this->createMock(Connection::class);
462
        $conn->expects($this->any())
463
            ->method('getDatabasePlatform')
464
            ->willReturn($platform);
465
466
        $em = $this->createMock(EntityManager::class);
467
        $em->expects($this->any())
468
            ->method('getMetadataFactory')
469
            ->willReturn($mf);
470
        $em->expects($this->any())
471
            ->method('getConnection')
472
            ->willReturn($conn);
473
474
        $registry = $this->createMock(ManagerRegistry::class);
475
        $registry->expects($this->any())
476
            ->method('getManagerForClass')
477
            ->willReturn($em);
478
479
        $manager = new ModelManager($registry);
480
        $result = $manager->getIdentifierValues($entity);
481
482
        $this->assertSame($entity->getId()->toString(), $result[0]);
483
    }
484
485
    public function testIntegerIdentifierType(): void
486
    {
487
        $id = new ProductId(12345);
488
        $entity = new Product($id, 'Some product');
489
490
        $meta = $this->createMock(ClassMetadata::class);
491
        $meta->expects($this->any())
492
            ->method('getIdentifierValues')
493
            ->willReturn([$entity->getId()]);
494
        $meta->expects($this->any())
495
            ->method('getTypeOfField')
496
            ->willReturn(ProductIdType::NAME);
497
498
        $mf = $this->createMock(ClassMetadataFactory::class);
499
        $mf->expects($this->any())
500
            ->method('getMetadataFor')
501
            ->willReturn($meta);
502
503
        $platform = $this->createMock(PostgreSqlPlatform::class);
504
        $platform->expects($this->any())
505
            ->method('hasDoctrineTypeMappingFor')
506
            ->with(ProductIdType::NAME)
507
            ->willReturn(false);
508
        $platform->expects($this->never())
509
            ->method('getDoctrineTypeMapping');
510
511
        $conn = $this->createMock(Connection::class);
512
        $conn->expects($this->any())
513
            ->method('getDatabasePlatform')
514
            ->willReturn($platform);
515
516
        $em = $this->createMock(EntityManager::class);
517
        $em->expects($this->any())
518
            ->method('getMetadataFactory')
519
            ->willReturn($mf);
520
        $em->expects($this->any())
521
            ->method('getConnection')
522
            ->willReturn($conn);
523
524
        $registry = $this->createMock(ManagerRegistry::class);
525
        $registry->expects($this->any())
526
            ->method('getManagerForClass')
527
            ->willReturn($em);
528
529
        $manager = new ModelManager($registry);
530
        $result = $manager->getIdentifierValues($entity);
531
532
        $this->assertSame((string) $entity->getId()->getId(), $result[0]);
533
    }
534
535
    public function testAssociationIdentifierType(): void
536
    {
537
        $entity = new ContainerEntity(new AssociatedEntity(42, new EmbeddedEntity()), new EmbeddedEntity());
538
539
        $meta = $this->createMock(ClassMetadata::class);
540
        $meta->expects($this->any())
541
            ->method('getIdentifierValues')
542
            ->willReturn([$entity->getAssociatedEntity()->getPlainField()]);
543
        $meta->expects($this->any())
544
            ->method('getTypeOfField')
545
            ->willReturn(null);
546
547
        $mf = $this->createMock(ClassMetadataFactory::class);
548
        $mf->expects($this->any())
549
            ->method('getMetadataFor')
550
            ->willReturn($meta);
551
552
        $platform = $this->createMock(PostgreSqlPlatform::class);
553
        $platform->expects($this->never())
554
            ->method('hasDoctrineTypeMappingFor');
555
556
        $conn = $this->createMock(Connection::class);
557
        $conn->expects($this->any())
558
            ->method('getDatabasePlatform')
559
            ->willReturn($platform);
560
561
        $em = $this->createMock(EntityManager::class);
562
        $em->expects($this->any())
563
            ->method('getMetadataFactory')
564
            ->willReturn($mf);
565
        $em->expects($this->any())
566
            ->method('getConnection')
567
            ->willReturn($conn);
568
569
        $registry = $this->createMock(ManagerRegistry::class);
570
        $registry->expects($this->any())
571
            ->method('getManagerForClass')
572
            ->willReturn($em);
573
574
        $manager = new ModelManager($registry);
575
        $result = $manager->getIdentifierValues($entity);
576
577
        $this->assertSame(42, $result[0]);
578
    }
579
580
    /**
581
     * [sortBy, sortOrder, isAddOrderBy].
582
     */
583
    public function getSortableInDataSourceIteratorDataProvider(): array
584
    {
585
        return [
586
            [null, null, false],
587
            ['', 'ASC', false],
588
            ['field', 'ASC', true],
589
            ['field', null, true],
590
        ];
591
    }
592
593
    /**
594
     * @dataProvider getSortableInDataSourceIteratorDataProvider
595
     *
596
     * @param string|null $sortBy
597
     * @param string|null $sortOrder
598
     * @param bool        $isAddOrderBy
599
     */
600
    public function testSortableInDataSourceIterator($sortBy, $sortOrder, $isAddOrderBy): void
601
    {
602
        $datagrid = $this->getMockForAbstractClass(DatagridInterface::class);
603
        $configuration = $this->getMockBuilder(Configuration::class)->getMock();
604
        $configuration->expects($this->any())
605
            ->method('getDefaultQueryHints')
606
            ->willReturn([]);
607
608
        $em = $this->getMockBuilder(EntityManager::class)
609
            ->disableOriginalConstructor()
610
            ->getMock();
611
612
        $em->expects($this->any())
613
            ->method('getConfiguration')
614
            ->willReturn($configuration);
615
616
        $queryBuilder = $this->getMockBuilder(QueryBuilder::class)
617
            ->setConstructorArgs([$em])
618
            ->getMock();
619
        $query = new Query($em);
620
621
        $proxyQuery = $this->getMockBuilder(ProxyQuery::class)
622
            ->setConstructorArgs([$queryBuilder])
623
            ->setMethods(['getSortBy', 'getSortOrder', 'getRootAliases'])
624
            ->getMock();
625
626
        $proxyQuery->expects($this->any())
627
            ->method('getSortOrder')
628
            ->willReturn($sortOrder);
629
630
        $proxyQuery->expects($this->any())
631
            ->method('getSortBy')
632
            ->willReturn($sortBy);
633
634
        $queryBuilder->expects($isAddOrderBy ? $this->atLeastOnce() : $this->never())
635
            ->method('addOrderBy');
636
637
        $proxyQuery->expects($this->any())
638
            ->method('getRootAliases')
639
            ->willReturn(['a']);
640
641
        $queryBuilder->expects($this->any())
642
            ->method('getQuery')
643
            ->willReturn($query);
644
645
        $datagrid->expects($this->any())
646
            ->method('getQuery')
647
            ->willReturn($proxyQuery);
648
649
        $registry = $this->getMockBuilder(RegistryInterface::class)->getMock();
650
        $manager = new ModelManager($registry);
651
        $manager->getDataSourceIterator($datagrid, []);
652
653
        if ($isAddOrderBy) {
654
            $this->assertArrayHasKey($key = 'doctrine.customTreeWalkers', $hints = $query->getHints());
655
            $this->assertContains(OrderByToSelectWalker::class, $hints[$key]);
656
        }
657
    }
658
659
    public function testModelReverseTransform(): void
660
    {
661
        $class = SimpleEntity::class;
662
663
        $metadataFactory = $this->createMock(ClassMetadataFactory::class);
664
        $modelManager = $this->createMock(ObjectManager::class);
665
        $registry = $this->createMock(ManagerRegistry::class);
666
667
        $classMetadata = new ClassMetadata($class);
668
        $classMetadata->reflClass = new \ReflectionClass($class);
669
670
        $modelManager->expects($this->once())
671
            ->method('getMetadataFactory')
672
            ->willReturn($metadataFactory);
673
        $metadataFactory->expects($this->once())
674
            ->method('getMetadataFor')
675
            ->with($class)
676
            ->willReturn($classMetadata);
677
        $registry->expects($this->once())
678
            ->method('getManagerForClass')
679
            ->with($class)
680
            ->willReturn($modelManager);
681
682
        $manager = new ModelManager($registry);
683
        $this->assertInstanceOf($class, $object = $manager->modelReverseTransform(
684
            $class,
685
            [
686
                'schmeckles' => 42,
687
                'multi_word_property' => 'hello',
688
            ]
689
        ));
690
        $this->assertSame(42, $object->getSchmeckles());
691
        $this->assertSame('hello', $object->getMultiWordProperty());
692
    }
693
694
    public function testCollections(): void
695
    {
696
        $registry = $this->createMock(ManagerRegistry::class);
697
        $model = new ModelManager($registry);
698
699
        $collection = $model->getModelCollectionInstance('whyDoWeEvenHaveThisParameter');
700
        $this->assertInstanceOf(ArrayCollection::class, $collection);
701
702
        $item1 = 'item1';
703
        $item2 = 'item2';
704
        $model->collectionAddElement($collection, $item1);
0 ignored issues
show
Bug introduced by
It seems like $collection defined by $model->getModelCollecti...EvenHaveThisParameter') on line 699 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...
705
        $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...
706
707
        $this->assertTrue($model->collectionHasElement($collection, $item1));
708
709
        $model->collectionRemoveElement($collection, $item1);
710
711
        $this->assertFalse($model->collectionHasElement($collection, $item1));
712
713
        $model->collectionClear($collection);
714
715
        $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...
716
    }
717
718
    public function testModelTransform(): void
719
    {
720
        $registry = $this->createMock(ManagerRegistry::class);
721
        $model = new ModelManager($registry);
722
723
        $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...
724
725
        $this->assertSame('doWeNeedThisMethod', $result);
726
    }
727
728
    public function testGetPaginationParameters(): void
729
    {
730
        $datagrid = $this->createMock(DatagridInterface::class);
731
        $field = $this->createMock(FieldDescriptionInterface::class);
732
        $registry = $this->createMock(ManagerRegistry::class);
733
734
        $datagrid->expects($this->once())
735
            ->method('getValues')
736
            ->willReturn(['_sort_by' => $field]);
737
738
        $field->expects($this->once())
739
            ->method('getName')
740
            ->willReturn($name = 'test');
741
742
        $model = new ModelManager($registry);
743
744
        $result = $model->getPaginationParameters($datagrid, $page = 5);
745
746
        $this->assertSame($page, $result['filter']['_page']);
747
        $this->assertSame($name, $result['filter']['_sort_by']);
748
    }
749
750
    public function testGetModelInstanceException(): void
751
    {
752
        $registry = $this->createMock(ManagerRegistry::class);
753
754
        $model = new ModelManager($registry);
755
756
        $this->expectException(\RuntimeException::class);
757
758
        $model->getModelInstance(AbstractEntity::class);
759
    }
760
761
    public function testGetModelInstanceForProtectedEntity(): void
762
    {
763
        $registry = $this->createMock(ManagerRegistry::class);
764
765
        $model = new ModelManager($registry);
766
767
        $this->assertInstanceOf(ProtectedEntity::class, $model->getModelInstance(ProtectedEntity::class));
768
    }
769
770
    public function testGetEntityManagerException(): void
771
    {
772
        $registry = $this->createMock(ManagerRegistry::class);
773
774
        $model = new ModelManager($registry);
775
776
        $this->expectException(\RuntimeException::class);
777
778
        $model->getEntityManager(VersionedEntity::class);
779
    }
780
781
    public function testGetNewFieldDescriptionInstanceException(): void
782
    {
783
        $registry = $this->createMock(ManagerRegistry::class);
784
785
        $model = new ModelManager($registry);
786
787
        $this->expectException(\RuntimeException::class);
788
789
        $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...
790
    }
791
792
    /**
793
     * @dataProvider createUpdateRemoveData
794
     */
795
    public function testCreate($exception): void
796
    {
797
        $registry = $this->createMock(ManagerRegistry::class);
798
799
        $entityManger = $this->createMock(EntityManager::class);
800
801
        $registry->expects($this->once())
802
            ->method('getManagerForClass')
803
            ->willReturn($entityManger);
804
805
        $entityManger->expects($this->once())
806
            ->method('persist');
807
808
        $entityManger->expects($this->once())
809
            ->method('flush')
810
            ->willThrowException($exception);
811
812
        $model = new ModelManager($registry);
813
814
        $this->expectException(ModelManagerException::class);
815
816
        $model->create(new VersionedEntity());
817
    }
818
819
    public function createUpdateRemoveData(): array
820
    {
821
        return [
822
            'PDOException' => [
823
                new \PDOException(),
824
            ],
825
            'DBALException' => [
826
                new DBALException(),
827
            ],
828
        ];
829
    }
830
831
    /**
832
     * @dataProvider createUpdateRemoveData
833
     */
834
    public function testUpdate($exception): void
835
    {
836
        $registry = $this->createMock(ManagerRegistry::class);
837
838
        $entityManger = $this->createMock(EntityManager::class);
839
840
        $registry->expects($this->once())
841
            ->method('getManagerForClass')
842
            ->willReturn($entityManger);
843
844
        $entityManger->expects($this->once())
845
            ->method('persist');
846
847
        $entityManger->expects($this->once())
848
            ->method('flush')
849
            ->willThrowException($exception);
850
851
        $model = new ModelManager($registry);
852
853
        $this->expectException(ModelManagerException::class);
854
855
        $model->update(new VersionedEntity());
856
    }
857
858
    /**
859
     * @dataProvider createUpdateRemoveData
860
     */
861
    public function testRemove($exception): void
862
    {
863
        $registry = $this->createMock(ManagerRegistry::class);
864
865
        $entityManger = $this->createMock(EntityManager::class);
866
867
        $registry->expects($this->once())
868
            ->method('getManagerForClass')
869
            ->willReturn($entityManger);
870
871
        $entityManger->expects($this->once())
872
            ->method('remove');
873
874
        $entityManger->expects($this->once())
875
            ->method('flush')
876
            ->willThrowException($exception);
877
878
        $model = new ModelManager($registry);
879
880
        $this->expectException(ModelManagerException::class);
881
882
        $model->delete(new VersionedEntity());
883
    }
884
885
    public function testFindBadId(): void
886
    {
887
        $registry = $this->createMock(ManagerRegistry::class);
888
889
        $model = new ModelManager($registry);
890
891
        $this->assertNull($model->find('notImportant', null));
892
    }
893
894
    /**
895
     * @dataProvider getWrongEntities
896
     *
897
     * @param mixed $entity
898
     */
899
    public function testNormalizedIdentifierException($entity): void
900
    {
901
        $registry = $this->createMock(ManagerRegistry::class);
902
903
        $model = new ModelManager($registry);
904
905
        $this->expectException(\RuntimeException::class);
906
907
        $model->getNormalizedIdentifier($entity);
908
    }
909
910
    public function getWrongEntities(): iterable
911
    {
912
        yield [0];
913
        yield [1];
914
        yield [false];
915
        yield [true];
916
        yield [[]];
917
        yield [''];
918
        yield ['sonata-project'];
919
    }
920
921
    public function testGetUrlsafeIdentifierNull(): void
922
    {
923
        $registry = $this->createMock(ManagerRegistry::class);
924
925
        $model = new ModelManager($registry);
926
927
        $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...
928
    }
929
930
    private function getMetadata($class, $isVersioned)
931
    {
932
        $metadata = new ClassMetadata($class);
933
934
        $metadata->isVersioned = $isVersioned;
935
936
        if ($isVersioned) {
937
            $versionField = 'version';
938
            $metadata->versionField = $versionField;
939
            $metadata->reflFields[$versionField] = new \ReflectionProperty($class, $versionField);
940
        }
941
942
        return $metadata;
943
    }
944
}
945