Completed
Pull Request — master (#1045)
by Mathieu
11:02
created

eprecatedRegistryInterface()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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