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