Completed
Pull Request — 3.x (#1078)
by Vincent
01:28
created

ModelManagerTest::setup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\DoctrineORMAdminBundle\Tests\Model;
15
16
use Doctrine\Common\Collections\ArrayCollection;
17
use Doctrine\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
use Symfony\Component\PropertyAccess\PropertyAccess;
64
65
class ModelManagerTest extends TestCase
66
{
67
    /**
68
     * @var ManagerRegistry|MockObject
69
     */
70
    private $registry;
71
72
    /**
73
     * @var ModelManager
74
     */
75
    private $modelManager;
76
77
    public function setup(): void
78
    {
79
        $this->registry = $this->createMock(ManagerRegistry::class);
80
        $this->modelManager = new ModelManager($this->registry, PropertyAccess::createPropertyAccessor());
81
    }
82
83
    public static function setUpBeforeClass(): void
84
    {
85
        if (!Type::hasType(UuidType::NAME)) {
86
            Type::addType(UuidType::NAME, UuidType::class);
87
        }
88
        if (!Type::hasType(UuidBinaryType::NAME)) {
89
            Type::addType(UuidBinaryType::NAME, UuidBinaryType::class);
90
        }
91
        if (!Type::hasType(ProductIdType::NAME)) {
92
            Type::addType(ProductIdType::NAME, ProductIdType::class);
93
        }
94
    }
95
96
    public function valueObjectDataProvider(): array
97
    {
98
        return [
99
            'value object with toString implementation' => [ValueObjectWithToStringImpl::class],
100
            'value object with magic toString implementation' => [ValueObjectWithMagicToStringImpl::class],
101
        ];
102
    }
103
104
    /**
105
     * @dataProvider valueObjectDataProvider
106
     */
107
    public function testGetIdentifierValuesWhenIdentifierIsValueObjectWithToStringMethod(string $vbClassName): void
108
    {
109
        $entity = new UuidBinaryEntity(new $vbClassName('a7ef873a-e7b5-11e9-81b4-2a2ae2dbcce4'));
110
111
        $platform = $this->createMock(MySqlPlatform::class);
112
113
        $connection = $this->createMock(Connection::class);
114
        $connection->method('getDatabasePlatform')->willReturn($platform);
115
116
        $classMetadata = $this->createMock(ClassMetadataInfo::class);
117
        $classMetadata->method('getIdentifierValues')->willReturn([$entity->getId()]);
118
        $classMetadata->method('getTypeOfField')->willReturn(UuidBinaryType::NAME);
119
120
        $classMetadataFactory = $this->createMock(ClassMetadataFactory::class);
121
        $classMetadataFactory->method('getMetadataFor')->willReturn($classMetadata);
122
123
        $entityManager = $this->createMock(EntityManager::class);
124
        $entityManager->method('getMetadataFactory')->willReturn($classMetadataFactory);
125
        $entityManager->method('getConnection')->willReturn($connection);
126
127
        $this->registry->method('getManagerForClass')->willReturn($entityManager);
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Doctrine\Persistence\ManagerRegistry>.

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...
128
129
        $this->assertSame(
130
            ['a7ef873a-e7b5-11e9-81b4-2a2ae2dbcce4'],
131
            $this->modelManager->getIdentifierValues($entity)
132
        );
133
    }
134
135
    public function testInstantiateWithDeprecatedRegistryInterface(): void
136
    {
137
        $em = $this->createMock(EntityManagerInterface::class);
138
139
        $this->registry->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Doctrine\Persistence\ManagerRegistry>.

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...
140
            ->method('getManagerForClass')
141
            ->with('x')
142
            ->willReturn($em)
143
        ;
144
        $this->assertSame($em, $this->modelManager->getEntityManager('x'));
145
    }
146
147
    public function testSortParameters(): void
148
    {
149
        $datagrid1 = $this->createMock(Datagrid::class);
150
        $datagrid2 = $this->createMock(Datagrid::class);
151
152
        $field1 = new FieldDescription();
153
        $field1->setName('field1');
154
155
        $field2 = new FieldDescription();
156
        $field2->setName('field2');
157
158
        $field3 = new FieldDescription();
159
        $field3->setName('field3');
160
        $field3->setOption('sortable', 'field3sortBy');
161
162
        $datagrid1
163
            ->expects($this->any())
164
            ->method('getValues')
165
            ->willReturn([
166
                '_sort_by' => $field1,
167
                '_sort_order' => 'ASC',
168
            ]);
169
170
        $datagrid2
171
            ->expects($this->any())
172
            ->method('getValues')
173
            ->willReturn([
174
                '_sort_by' => $field3,
175
                '_sort_order' => 'ASC',
176
            ]);
177
178
        $parameters = $this->modelManager->getSortParameters($field1, $datagrid1);
179
180
        $this->assertSame('DESC', $parameters['filter']['_sort_order']);
181
        $this->assertSame('field1', $parameters['filter']['_sort_by']);
182
183
        $parameters = $this->modelManager->getSortParameters($field2, $datagrid1);
184
185
        $this->assertSame('ASC', $parameters['filter']['_sort_order']);
186
        $this->assertSame('field2', $parameters['filter']['_sort_by']);
187
188
        $parameters = $this->modelManager->getSortParameters($field3, $datagrid1);
189
190
        $this->assertSame('ASC', $parameters['filter']['_sort_order']);
191
        $this->assertSame('field3sortBy', $parameters['filter']['_sort_by']);
192
193
        $parameters = $this->modelManager->getSortParameters($field3, $datagrid2);
194
195
        $this->assertSame('DESC', $parameters['filter']['_sort_order']);
196
        $this->assertSame('field3sortBy', $parameters['filter']['_sort_by']);
197
    }
198
199
    public function getVersionDataProvider()
200
    {
201
        return [
202
            [true],
203
            [false],
204
        ];
205
    }
206
207
    /**
208
     * @dataProvider getVersionDataProvider
209
     */
210
    public function testGetVersion($isVersioned): void
211
    {
212
        $object = new VersionedEntity();
213
214
        $modelManager = $this->getMockBuilder(ModelManager::class)
215
            ->disableOriginalConstructor()
216
            ->setMethods(['getMetadata'])
217
            ->getMock();
218
219
        $metadata = $this->getMetadata(\get_class($object), $isVersioned);
220
221
        $modelManager->expects($this->any())
222
            ->method('getMetadata')
223
            ->willReturn($metadata);
224
225
        if ($isVersioned) {
226
            $object->version = 123;
227
228
            $this->assertNotNull($modelManager->getLockVersion($object));
229
        } else {
230
            $this->assertNull($modelManager->getLockVersion($object));
231
        }
232
    }
233
234
    public function lockDataProvider()
235
    {
236
        return [
237
            [true,  false],
238
            [true,  true],
239
            [false, false],
240
        ];
241
    }
242
243
    /**
244
     * @dataProvider lockDataProvider
245
     */
246
    public function testLock($isVersioned, $expectsException): void
247
    {
248
        $object = new VersionedEntity();
249
250
        $em = $this->getMockBuilder(EntityManager::class)
251
            ->disableOriginalConstructor()
252
            ->setMethods(['lock'])
253
            ->getMock();
254
255
        $modelManager = $this->getMockBuilder(ModelManager::class)
256
            ->disableOriginalConstructor()
257
            ->setMethods(['getMetadata', 'getEntityManager'])
258
            ->getMock();
259
260
        $modelManager->expects($this->any())
261
            ->method('getEntityManager')
262
            ->willReturn($em);
263
264
        $metadata = $this->getMetadata(\get_class($object), $isVersioned);
265
266
        $modelManager->expects($this->any())
267
            ->method('getMetadata')
268
            ->willReturn($metadata);
269
270
        $em->expects($isVersioned ? $this->once() : $this->never())
271
            ->method('lock');
272
273
        if ($expectsException) {
274
            $em->expects($this->once())
275
                ->method('lock')
276
                ->will($this->throwException(OptimisticLockException::lockFailed($object)));
277
278
            $this->expectException(LockException::class);
279
        }
280
281
        $modelManager->lock($object, 123);
282
    }
283
284
    public function testGetParentMetadataForProperty(): void
285
    {
286
        $containerEntityClass = ContainerEntity::class;
287
        $associatedEntityClass = AssociatedEntity::class;
288
        $embeddedEntityClass = EmbeddedEntity::class;
289
        $modelManagerClass = ModelManager::class;
290
291
        $em = $this->createMock(EntityManager::class);
292
293
        /** @var MockObject|ModelManager $modelManager */
294
        $modelManager = $this->getMockBuilder($modelManagerClass)
295
            ->disableOriginalConstructor()
296
            ->setMethods(['getMetadata', 'getEntityManager'])
297
            ->getMock();
298
299
        $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...
300
            ->method('getEntityManager')
301
            ->willReturn($em);
302
303
        $containerEntityMetadata = $this->getMetadataForContainerEntity();
304
        $associatedEntityMetadata = $this->getMetadataForAssociatedEntity();
305
        $embeddedEntityMetadata = $this->getMetadataForEmbeddedEntity();
306
307
        $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...
308
            ->willReturnMap(
309
                [
310
                        [$containerEntityClass, $containerEntityMetadata],
311
                        [$embeddedEntityClass, $embeddedEntityMetadata],
312
                        [$associatedEntityClass, $associatedEntityMetadata],
313
                    ]
314
            );
315
316
        /** @var ClassMetadata $metadata */
317
        list($metadata, $lastPropertyName) = $modelManager
318
            ->getParentMetadataForProperty($containerEntityClass, 'plainField');
319
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'integer');
320
321
        list($metadata, $lastPropertyName) = $modelManager
322
            ->getParentMetadataForProperty($containerEntityClass, 'associatedEntity.plainField');
323
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'string');
324
325
        list($metadata, $lastPropertyName) = $modelManager
326
            ->getParentMetadataForProperty($containerEntityClass, 'embeddedEntity.plainField');
327
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
328
329
        list($metadata, $lastPropertyName) = $modelManager
330
            ->getParentMetadataForProperty($containerEntityClass, 'associatedEntity.embeddedEntity.plainField');
331
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
332
333
        list($metadata, $lastPropertyName) = $modelManager
334
            ->getParentMetadataForProperty(
335
                $containerEntityClass,
336
                'associatedEntity.embeddedEntity.subEmbeddedEntity.plainField'
337
            );
338
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
339
    }
340
341
    public function getMetadataForEmbeddedEntity()
342
    {
343
        $metadata = new ClassMetadata(EmbeddedEntity::class);
344
345
        $metadata->fieldMappings = [
346
            'plainField' => [
347
                'fieldName' => 'plainField',
348
                'columnName' => 'plainField',
349
                'type' => 'boolean',
350
            ],
351
        ];
352
353
        return $metadata;
354
    }
355
356
    public function getMetadataForSubEmbeddedEntity()
357
    {
358
        $metadata = new ClassMetadata(SubEmbeddedEntity::class);
359
360
        $metadata->fieldMappings = [
361
            'plainField' => [
362
                'fieldName' => 'plainField',
363
                'columnName' => 'plainField',
364
                'type' => 'boolean',
365
            ],
366
        ];
367
368
        return $metadata;
369
    }
370
371
    public function getMetadataForAssociatedEntity()
372
    {
373
        $embeddedEntityClass = EmbeddedEntity::class;
374
        $subEmbeddedEntityClass = SubEmbeddedEntity::class;
375
376
        $metadata = new ClassMetadata(AssociatedEntity::class);
377
378
        $metadata->fieldMappings = [
379
            'plainField' => [
380
                'fieldName' => 'plainField',
381
                'columnName' => 'plainField',
382
                'type' => 'string',
383
            ],
384
        ];
385
386
        $metadata->embeddedClasses['embeddedEntity'] = [
387
            'class' => $embeddedEntityClass,
388
            'columnPrefix' => 'embedded_entity_',
389
        ];
390
        $metadata->embeddedClasses['embeddedEntity.subEmbeddedEntity'] = [
391
            'class' => $subEmbeddedEntityClass,
392
            'columnPrefix' => 'embedded_entity_sub_embedded_entity_',
393
            'declaredField' => 'embeddedEntity',
394
            'originalField' => 'subEmbeddedEntity',
395
        ];
396
397
        $metadata->inlineEmbeddable('embeddedEntity', $this->getMetadataForEmbeddedEntity());
398
        $metadata->inlineEmbeddable('embeddedEntity.subEmbeddedEntity', $this->getMetadataForSubEmbeddedEntity());
399
400
        return $metadata;
401
    }
402
403
    public function getMetadataForContainerEntity()
404
    {
405
        $containerEntityClass = ContainerEntity::class;
406
        $associatedEntityClass = AssociatedEntity::class;
407
        $embeddedEntityClass = EmbeddedEntity::class;
408
        $subEmbeddedEntityClass = SubEmbeddedEntity::class;
409
410
        $metadata = new ClassMetadata($containerEntityClass);
411
412
        $metadata->fieldMappings = [
413
            'plainField' => [
414
                'fieldName' => 'plainField',
415
                'columnName' => 'plainField',
416
                'type' => 'integer',
417
            ],
418
        ];
419
420
        $metadata->associationMappings['associatedEntity'] = [
421
            'fieldName' => 'associatedEntity',
422
            'targetEntity' => $associatedEntityClass,
423
            'sourceEntity' => $containerEntityClass,
424
        ];
425
426
        $metadata->embeddedClasses['embeddedEntity'] = [
427
            'class' => $embeddedEntityClass,
428
            'columnPrefix' => 'embeddedEntity',
429
        ];
430
        $metadata->embeddedClasses['embeddedEntity.subEmbeddedEntity'] = [
431
            'class' => $subEmbeddedEntityClass,
432
            'columnPrefix' => 'embedded_entity_sub_embedded_entity_',
433
            'declaredField' => 'embeddedEntity',
434
            'originalField' => 'subEmbeddedEntity',
435
        ];
436
437
        $metadata->inlineEmbeddable('embeddedEntity', $this->getMetadataForEmbeddedEntity());
438
        $metadata->inlineEmbeddable('embeddedEntity.subEmbeddedEntity', $this->getMetadataForSubEmbeddedEntity());
439
440
        return $metadata;
441
    }
442
443
    public function testGetIdentifierValuesForIdInObjectTypeBinaryToStringSupport(): void
444
    {
445
        $uuid = new NonIntegerIdentifierTestClass('efbcfc4b-8c43-4d42-aa4c-d707e55151ac');
446
447
        $entity = new UuidEntity($uuid);
448
449
        $meta = $this->createMock(ClassMetadata::class);
450
        $meta->expects($this->any())
451
            ->method('getIdentifierValues')
452
            ->willReturn([$entity->getId()]);
453
        $meta->expects($this->any())
454
            ->method('getTypeOfField')
455
            ->willReturn(UuidBinaryType::NAME); //'uuid_binary'
456
457
        $mf = $this->createMock(ClassMetadataFactory::class);
458
        $mf->expects($this->any())
459
            ->method('getMetadataFor')
460
            ->willReturn($meta);
461
462
        $platform = $this->createMock(PostgreSqlPlatform::class);
463
        $platform->expects($this->any())
464
            ->method('hasDoctrineTypeMappingFor')
465
            ->with(UuidBinaryType::NAME)
466
            ->willReturn(true);
467
        $platform->expects($this->any())
468
            ->method('getDoctrineTypeMapping')
469
            ->with(UuidBinaryType::NAME)
470
            ->willReturn('binary');
471
472
        $conn = $this->createMock(Connection::class);
473
        $conn->expects($this->any())
474
            ->method('getDatabasePlatform')
475
            ->willReturn($platform);
476
477
        $em = $this->createMock(EntityManager::class);
478
        $em->expects($this->any())
479
            ->method('getMetadataFactory')
480
            ->willReturn($mf);
481
        $em->expects($this->any())
482
            ->method('getConnection')
483
            ->willReturn($conn);
484
485
        $this->registry->expects($this->any())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Doctrine\Persistence\ManagerRegistry>.

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...
486
            ->method('getManagerForClass')
487
            ->willReturn($em);
488
489
        $result = $this->modelManager->getIdentifierValues($entity);
490
491
        $this->assertSame($entity->getId()->toString(), $result[0]);
492
    }
493
494
    public function testNonIntegerIdentifierType(): void
495
    {
496
        $uuid = new NonIntegerIdentifierTestClass('efbcfc4b-8c43-4d42-aa4c-d707e55151ac');
497
        $entity = new UuidEntity($uuid);
498
499
        $meta = $this->createMock(ClassMetadata::class);
500
        $meta->expects($this->any())
501
            ->method('getIdentifierValues')
502
            ->willReturn([$entity->getId()]);
503
        $meta->expects($this->any())
504
            ->method('getTypeOfField')
505
            ->willReturn(UuidType::NAME);
506
507
        $mf = $this->createMock(ClassMetadataFactory::class);
508
        $mf->expects($this->any())
509
            ->method('getMetadataFor')
510
            ->willReturn($meta);
511
512
        $platform = $this->createMock(PostgreSqlPlatform::class);
513
        $platform->expects($this->any())
514
            ->method('hasDoctrineTypeMappingFor')
515
            ->with(UuidType::NAME)
516
            ->willReturn(false);
517
        $platform->expects($this->never())
518
            ->method('getDoctrineTypeMapping');
519
520
        $conn = $this->createMock(Connection::class);
521
        $conn->expects($this->any())
522
            ->method('getDatabasePlatform')
523
            ->willReturn($platform);
524
525
        $em = $this->createMock(EntityManager::class);
526
        $em->expects($this->any())
527
            ->method('getMetadataFactory')
528
            ->willReturn($mf);
529
        $em->expects($this->any())
530
            ->method('getConnection')
531
            ->willReturn($conn);
532
533
        $this->registry->expects($this->any())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Doctrine\Persistence\ManagerRegistry>.

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...
534
            ->method('getManagerForClass')
535
            ->willReturn($em);
536
537
        $result = $this->modelManager->getIdentifierValues($entity);
538
539
        $this->assertSame($entity->getId()->toString(), $result[0]);
540
    }
541
542
    public function testIntegerIdentifierType(): void
543
    {
544
        $id = new ProductId(12345);
545
        $entity = new Product($id, 'Some product');
546
547
        $meta = $this->createMock(ClassMetadata::class);
548
        $meta->expects($this->any())
549
            ->method('getIdentifierValues')
550
            ->willReturn([$entity->getId()]);
551
        $meta->expects($this->any())
552
            ->method('getTypeOfField')
553
            ->willReturn(ProductIdType::NAME);
554
555
        $mf = $this->createMock(ClassMetadataFactory::class);
556
        $mf->expects($this->any())
557
            ->method('getMetadataFor')
558
            ->willReturn($meta);
559
560
        $platform = $this->createMock(PostgreSqlPlatform::class);
561
        $platform->expects($this->any())
562
            ->method('hasDoctrineTypeMappingFor')
563
            ->with(ProductIdType::NAME)
564
            ->willReturn(false);
565
        $platform->expects($this->never())
566
            ->method('getDoctrineTypeMapping');
567
568
        $conn = $this->createMock(Connection::class);
569
        $conn->expects($this->any())
570
            ->method('getDatabasePlatform')
571
            ->willReturn($platform);
572
573
        $em = $this->createMock(EntityManager::class);
574
        $em->expects($this->any())
575
            ->method('getMetadataFactory')
576
            ->willReturn($mf);
577
        $em->expects($this->any())
578
            ->method('getConnection')
579
            ->willReturn($conn);
580
581
        $this->registry->expects($this->any())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Doctrine\Persistence\ManagerRegistry>.

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...
582
            ->method('getManagerForClass')
583
            ->willReturn($em);
584
585
        $result = $this->modelManager->getIdentifierValues($entity);
586
587
        $this->assertSame((string) $entity->getId()->getId(), $result[0]);
588
    }
589
590
    public function testAssociationIdentifierType(): void
591
    {
592
        $entity = new ContainerEntity(new AssociatedEntity(42, new EmbeddedEntity()), new EmbeddedEntity());
593
594
        $meta = $this->createMock(ClassMetadata::class);
595
        $meta->expects($this->any())
596
            ->method('getIdentifierValues')
597
            ->willReturn([$entity->getAssociatedEntity()->getPlainField()]);
598
        $meta->expects($this->any())
599
            ->method('getTypeOfField')
600
            ->willReturn(null);
601
602
        $mf = $this->createMock(ClassMetadataFactory::class);
603
        $mf->expects($this->any())
604
            ->method('getMetadataFor')
605
            ->willReturn($meta);
606
607
        $platform = $this->createMock(PostgreSqlPlatform::class);
608
        $platform->expects($this->never())
609
            ->method('hasDoctrineTypeMappingFor');
610
611
        $conn = $this->createMock(Connection::class);
612
        $conn->expects($this->any())
613
            ->method('getDatabasePlatform')
614
            ->willReturn($platform);
615
616
        $em = $this->createMock(EntityManager::class);
617
        $em->expects($this->any())
618
            ->method('getMetadataFactory')
619
            ->willReturn($mf);
620
        $em->expects($this->any())
621
            ->method('getConnection')
622
            ->willReturn($conn);
623
624
        $this->registry->expects($this->any())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Doctrine\Persistence\ManagerRegistry>.

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...
625
            ->method('getManagerForClass')
626
            ->willReturn($em);
627
628
        $result = $this->modelManager->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
        $this->modelManager->getDataSourceIterator($datagrid, []);
705
706
        if ($isAddOrderBy) {
707
            $this->assertArrayHasKey($key = 'doctrine.customTreeWalkers', $hints = $query->getHints());
708
            $this->assertContains(OrderByToSelectWalker::class, $hints[$key]);
709
        }
710
    }
711
712
    public function testModelReverseTransform(): void
713
    {
714
        $class = SimpleEntity::class;
715
716
        $metadataFactory = $this->createMock(ClassMetadataFactory::class);
717
        $objectManager = $this->createMock(ObjectManager::class);
718
        $registry = $this->createMock(ManagerRegistry::class);
0 ignored issues
show
Unused Code introduced by
$registry is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
719
720
        $classMetadata = new ClassMetadata($class);
721
        $classMetadata->reflClass = new \ReflectionClass($class);
722
723
        $objectManager->expects($this->once())
724
            ->method('getMetadataFactory')
725
            ->willReturn($metadataFactory);
726
        $metadataFactory->expects($this->once())
727
            ->method('getMetadataFor')
728
            ->with($class)
729
            ->willReturn($classMetadata);
730
        $this->registry->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Doctrine\Persistence\ManagerRegistry>.

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...
731
            ->method('getManagerForClass')
732
            ->with($class)
733
            ->willReturn($objectManager);
734
735
        $this->assertInstanceOf($class, $object = $this->modelManager->modelReverseTransform(
736
            $class,
737
            [
738
                'schmeckles' => 42,
739
                'multi_word_property' => 'hello',
740
            ]
741
        ));
742
        $this->assertSame(42, $object->getSchmeckles());
743
        $this->assertSame('hello', $object->getMultiWordProperty());
744
    }
745
746
    public function testCollections(): void
747
    {
748
        $collection = $this->modelManager->getModelCollectionInstance('whyDoWeEvenHaveThisParameter');
749
        $this->assertInstanceOf(ArrayCollection::class, $collection);
750
751
        $item1 = 'item1';
752
        $item2 = 'item2';
753
        $this->modelManager->collectionAddElement($collection, $item1);
0 ignored issues
show
Bug introduced by
It seems like $collection defined by $this->modelManager->get...EvenHaveThisParameter') on line 748 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...
754
        $this->modelManager->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...
755
756
        $this->assertTrue($this->modelManager->collectionHasElement($collection, $item1));
757
758
        $this->modelManager->collectionRemoveElement($collection, $item1);
759
760
        $this->assertFalse($this->modelManager->collectionHasElement($collection, $item1));
761
762
        $this->modelManager->collectionClear($collection);
763
764
        $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...
765
    }
766
767
    public function testModelTransform(): void
768
    {
769
        $result = $this->modelManager->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...
770
771
        $this->assertSame('doWeNeedThisMethod', $result);
772
    }
773
774
    public function testGetPaginationParameters(): void
775
    {
776
        $datagrid = $this->createMock(DatagridInterface::class);
777
        $field = $this->createMock(FieldDescriptionInterface::class);
778
779
        $datagrid->expects($this->once())
780
            ->method('getValues')
781
            ->willReturn(['_sort_by' => $field]);
782
783
        $field->expects($this->once())
784
            ->method('getName')
785
            ->willReturn($name = 'test');
786
787
        $result = $this->modelManager->getPaginationParameters($datagrid, $page = 5);
788
789
        $this->assertSame($page, $result['filter']['_page']);
790
        $this->assertSame($name, $result['filter']['_sort_by']);
791
    }
792
793
    public function testGetModelInstanceException(): void
794
    {
795
        $this->expectException(\RuntimeException::class);
796
797
        $this->modelManager->getModelInstance(AbstractEntity::class);
798
    }
799
800
    public function testGetModelInstanceForProtectedEntity(): void
801
    {
802
        $this->assertInstanceOf(ProtectedEntity::class, $this->modelManager->getModelInstance(ProtectedEntity::class));
803
    }
804
805
    public function testGetEntityManagerException(): void
806
    {
807
        $this->expectException(\RuntimeException::class);
808
809
        $this->modelManager->getEntityManager(VersionedEntity::class);
810
    }
811
812
    public function testGetNewFieldDescriptionInstanceException(): void
813
    {
814
        $this->expectException(\RuntimeException::class);
815
816
        $this->modelManager->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...
817
    }
818
819
    /**
820
     * @dataProvider createUpdateRemoveData
821
     */
822
    public function testCreate($exception): void
823
    {
824
        $entityManger = $this->createMock(EntityManager::class);
825
826
        $this->registry->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Doctrine\Persistence\ManagerRegistry>.

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...
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
        $this->expectException(ModelManagerException::class);
838
839
        $this->modelManager->create(new VersionedEntity());
840
    }
841
842
    public function createUpdateRemoveData()
843
    {
844
        return [
845
            'PDOException' => [
846
                new \PDOException(),
847
            ],
848
            'DBALException' => [
849
                new DBALException(),
850
            ],
851
        ];
852
    }
853
854
    /**
855
     * @dataProvider createUpdateRemoveData
856
     */
857
    public function testUpdate($exception): void
858
    {
859
        $entityManger = $this->createMock(EntityManager::class);
860
861
        $this->registry->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Doctrine\Persistence\ManagerRegistry>.

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...
862
            ->method('getManagerForClass')
863
            ->willReturn($entityManger);
864
865
        $entityManger->expects($this->once())
866
            ->method('persist');
867
868
        $entityManger->expects($this->once())
869
            ->method('flush')
870
            ->willThrowException($exception);
871
872
        $this->expectException(ModelManagerException::class);
873
874
        $this->modelManager->update(new VersionedEntity());
875
    }
876
877
    /**
878
     * @dataProvider createUpdateRemoveData
879
     */
880
    public function testRemove($exception): void
881
    {
882
        $entityManger = $this->createMock(EntityManager::class);
883
884
        $this->registry->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Doctrine\Persistence\ManagerRegistry>.

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...
885
            ->method('getManagerForClass')
886
            ->willReturn($entityManger);
887
888
        $entityManger->expects($this->once())
889
            ->method('remove');
890
891
        $entityManger->expects($this->once())
892
            ->method('flush')
893
            ->willThrowException($exception);
894
895
        $this->expectException(ModelManagerException::class);
896
897
        $this->modelManager->delete(new VersionedEntity());
898
    }
899
900
    /**
901
     * NEXT_MAJOR: Remove this method.
902
     *
903
     * @group legacy
904
     *
905
     * @expectedDeprecation Passing null as argument 1 for Sonata\DoctrineORMAdminBundle\Model\ModelManager::find() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20 and will be not allowed in version 4.0.
906
     */
907
    public function testFindBadId(): void
908
    {
909
        $this->assertNull($this->modelManager->find('notImportant', null));
910
    }
911
912
    /**
913
     * @dataProvider getWrongEntities
914
     *
915
     * @param mixed $entity
916
     */
917
    public function testNormalizedIdentifierException($entity): void
918
    {
919
        $this->expectException(\RuntimeException::class);
920
921
        $this->modelManager->getNormalizedIdentifier($entity);
922
    }
923
924
    public function getWrongEntities(): iterable
925
    {
926
        yield [0];
927
        yield [1];
928
        yield [false];
929
        yield [true];
930
        yield [[]];
931
        yield [''];
932
        yield ['sonata-project'];
933
    }
934
935
    /**
936
     * NEXT_MAJOR: Remove this method.
937
     *
938
     * @group legacy
939
     *
940
     * @expectedDeprecation Passing null as argument 1 for Sonata\DoctrineORMAdminBundle\Model\ModelManager::getNormalizedIdentifier() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20 and will be not allowed in version 4.0.
941
     */
942
    public function testGetUrlsafeIdentifierNull(): void
943
    {
944
        $this->assertNull($this->modelManager->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...
945
    }
946
947
    private function getMetadata($class, $isVersioned)
948
    {
949
        $metadata = new ClassMetadata($class);
950
951
        $metadata->isVersioned = $isVersioned;
952
953
        if ($isVersioned) {
954
            $versionField = 'version';
955
            $metadata->versionField = $versionField;
956
            $metadata->reflFields[$versionField] = new \ReflectionProperty($class, $versionField);
957
        }
958
959
        return $metadata;
960
    }
961
}
962