Completed
Push — master ( f92116...19d34a )
by Vincent
15s queued 12s
created

ModelManagerTest::testSortParameters()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 51
rs 9.069
c 0
b 0
f 0
cc 1
nc 1
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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(): array
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(): array
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
    public function getSortableInDataSourceIteratorDataProvider(): array
636
    {
637
        return [
638
            [null, null, false],
639
            ['', 'ASC', false],
640
            ['field', 'ASC', true],
641
            ['field', null, true],
642
        ];
643
    }
644
645
    /**
646
     * @dataProvider getSortableInDataSourceIteratorDataProvider
647
     *
648
     * @param string|null $sortBy
649
     * @param string|null $sortOrder
650
     * @param bool        $isAddOrderBy
651
     */
652
    public function testSortableInDataSourceIterator($sortBy, $sortOrder, $isAddOrderBy): void
653
    {
654
        $datagrid = $this->getMockForAbstractClass(DatagridInterface::class);
655
        $configuration = $this->getMockBuilder(Configuration::class)->getMock();
656
        $configuration->expects($this->any())
657
            ->method('getDefaultQueryHints')
658
            ->willReturn([]);
659
660
        $em = $this->getMockBuilder(EntityManager::class)
661
            ->disableOriginalConstructor()
662
            ->getMock();
663
664
        $em->expects($this->any())
665
            ->method('getConfiguration')
666
            ->willReturn($configuration);
667
668
        $queryBuilder = $this->getMockBuilder(QueryBuilder::class)
669
            ->setConstructorArgs([$em])
670
            ->getMock();
671
        $query = new Query($em);
672
673
        $proxyQuery = $this->getMockBuilder(ProxyQuery::class)
674
            ->setConstructorArgs([$queryBuilder])
675
            ->setMethods(['getSortBy', 'getSortOrder', 'getRootAliases'])
676
            ->getMock();
677
678
        $proxyQuery->expects($this->any())
679
            ->method('getSortOrder')
680
            ->willReturn($sortOrder);
681
682
        $proxyQuery->expects($this->any())
683
            ->method('getSortBy')
684
            ->willReturn($sortBy);
685
686
        $queryBuilder->expects($isAddOrderBy ? $this->atLeastOnce() : $this->never())
687
            ->method('addOrderBy');
688
689
        $proxyQuery->expects($this->any())
690
            ->method('getRootAliases')
691
            ->willReturn(['a']);
692
693
        $queryBuilder->expects($this->any())
694
            ->method('getQuery')
695
            ->willReturn($query);
696
697
        $datagrid->expects($this->any())
698
            ->method('getQuery')
699
            ->willReturn($proxyQuery);
700
701
        $this->modelManager->getDataSourceIterator($datagrid, []);
702
703
        if ($isAddOrderBy) {
704
            $this->assertArrayHasKey($key = 'doctrine.customTreeWalkers', $hints = $query->getHints());
705
            $this->assertContains(OrderByToSelectWalker::class, $hints[$key]);
706
        }
707
    }
708
709
    public function testModelReverseTransform(): void
710
    {
711
        $class = SimpleEntity::class;
712
713
        $metadataFactory = $this->createMock(ClassMetadataFactory::class);
714
        $objectManager = $this->createMock(ObjectManager::class);
715
        $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...
716
717
        $classMetadata = new ClassMetadata($class);
718
        $classMetadata->reflClass = new \ReflectionClass($class);
719
720
        $objectManager->expects($this->once())
721
            ->method('getMetadataFactory')
722
            ->willReturn($metadataFactory);
723
        $metadataFactory->expects($this->once())
724
            ->method('getMetadataFor')
725
            ->with($class)
726
            ->willReturn($classMetadata);
727
        $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...
728
            ->method('getManagerForClass')
729
            ->with($class)
730
            ->willReturn($objectManager);
731
732
        $this->assertInstanceOf($class, $object = $this->modelManager->modelReverseTransform(
733
            $class,
734
            [
735
                'schmeckles' => 42,
736
                'multi_word_property' => 'hello',
737
            ]
738
        ));
739
        $this->assertSame(42, $object->getSchmeckles());
740
        $this->assertSame('hello', $object->getMultiWordProperty());
741
    }
742
743
    public function testCollections(): void
744
    {
745
        $collection = $this->modelManager->getModelCollectionInstance('whyDoWeEvenHaveThisParameter');
746
        $this->assertInstanceOf(ArrayCollection::class, $collection);
747
748
        $item1 = 'item1';
749
        $item2 = 'item2';
750
        $this->modelManager->collectionAddElement($collection, $item1);
0 ignored issues
show
Bug introduced by
It seems like $collection defined by $this->modelManager->get...EvenHaveThisParameter') on line 745 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...
751
        $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...
752
753
        $this->assertTrue($this->modelManager->collectionHasElement($collection, $item1));
754
755
        $this->modelManager->collectionRemoveElement($collection, $item1);
756
757
        $this->assertFalse($this->modelManager->collectionHasElement($collection, $item1));
758
759
        $this->modelManager->collectionClear($collection);
760
761
        $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...
762
    }
763
764
    public function testModelTransform(): void
765
    {
766
        $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...
767
768
        $this->assertSame('doWeNeedThisMethod', $result);
769
    }
770
771
    public function testGetPaginationParameters(): void
772
    {
773
        $datagrid = $this->createMock(DatagridInterface::class);
774
        $field = $this->createMock(FieldDescriptionInterface::class);
775
776
        $datagrid->expects($this->once())
777
            ->method('getValues')
778
            ->willReturn(['_sort_by' => $field]);
779
780
        $field->expects($this->once())
781
            ->method('getName')
782
            ->willReturn($name = 'test');
783
784
        $result = $this->modelManager->getPaginationParameters($datagrid, $page = 5);
785
786
        $this->assertSame($page, $result['filter']['_page']);
787
        $this->assertSame($name, $result['filter']['_sort_by']);
788
    }
789
790
    public function testGetModelInstanceException(): void
791
    {
792
        $this->expectException(\RuntimeException::class);
793
794
        $this->modelManager->getModelInstance(AbstractEntity::class);
795
    }
796
797
    public function testGetModelInstanceForProtectedEntity(): void
798
    {
799
        $this->assertInstanceOf(ProtectedEntity::class, $this->modelManager->getModelInstance(ProtectedEntity::class));
800
    }
801
802
    public function testGetEntityManagerException(): void
803
    {
804
        $this->expectException(\RuntimeException::class);
805
806
        $this->modelManager->getEntityManager(VersionedEntity::class);
807
    }
808
809
    public function testGetNewFieldDescriptionInstanceException(): void
810
    {
811
        $this->expectException(\RuntimeException::class);
812
813
        $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...
814
    }
815
816
    /**
817
     * @dataProvider createUpdateRemoveData
818
     */
819
    public function testCreate($exception): void
820
    {
821
        $entityManger = $this->createMock(EntityManager::class);
822
823
        $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...
824
            ->method('getManagerForClass')
825
            ->willReturn($entityManger);
826
827
        $entityManger->expects($this->once())
828
            ->method('persist');
829
830
        $entityManger->expects($this->once())
831
            ->method('flush')
832
            ->willThrowException($exception);
833
834
        $this->expectException(ModelManagerException::class);
835
836
        $this->modelManager->create(new VersionedEntity());
837
    }
838
839
    public function createUpdateRemoveData(): array
840
    {
841
        return [
842
            'PDOException' => [
843
                new \PDOException(),
844
            ],
845
            'DBALException' => [
846
                new DBALException(),
847
            ],
848
        ];
849
    }
850
851
    /**
852
     * @dataProvider createUpdateRemoveData
853
     */
854
    public function testUpdate($exception): void
855
    {
856
        $entityManger = $this->createMock(EntityManager::class);
857
858
        $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...
859
            ->method('getManagerForClass')
860
            ->willReturn($entityManger);
861
862
        $entityManger->expects($this->once())
863
            ->method('persist');
864
865
        $entityManger->expects($this->once())
866
            ->method('flush')
867
            ->willThrowException($exception);
868
869
        $this->expectException(ModelManagerException::class);
870
871
        $this->modelManager->update(new VersionedEntity());
872
    }
873
874
    /**
875
     * @dataProvider createUpdateRemoveData
876
     */
877
    public function testRemove($exception): void
878
    {
879
        $entityManger = $this->createMock(EntityManager::class);
880
881
        $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...
882
            ->method('getManagerForClass')
883
            ->willReturn($entityManger);
884
885
        $entityManger->expects($this->once())
886
            ->method('remove');
887
888
        $entityManger->expects($this->once())
889
            ->method('flush')
890
            ->willThrowException($exception);
891
892
        $this->expectException(ModelManagerException::class);
893
894
        $this->modelManager->delete(new VersionedEntity());
895
    }
896
897
    /**
898
     * NEXT_MAJOR: Remove this method.
899
     *
900
     * @group legacy
901
     *
902
     * @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.
903
     */
904
    public function testFindBadId(): void
905
    {
906
        $this->assertNull($this->modelManager->find('notImportant', null));
907
    }
908
909
    /**
910
     * @dataProvider getWrongEntities
911
     *
912
     * @param mixed $entity
913
     */
914
    public function testNormalizedIdentifierException($entity): void
915
    {
916
        $this->expectException(\RuntimeException::class);
917
918
        $this->modelManager->getNormalizedIdentifier($entity);
919
    }
920
921
    public function getWrongEntities(): iterable
922
    {
923
        yield [0];
924
        yield [1];
925
        yield [false];
926
        yield [true];
927
        yield [[]];
928
        yield [''];
929
        yield ['sonata-project'];
930
    }
931
932
    /**
933
     * NEXT_MAJOR: Remove this method.
934
     *
935
     * @group legacy
936
     *
937
     * @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.
938
     */
939
    public function testGetUrlsafeIdentifierNull(): void
940
    {
941
        $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...
942
    }
943
944
    private function getMetadata($class, $isVersioned)
945
    {
946
        $metadata = new ClassMetadata($class);
947
948
        $metadata->isVersioned = $isVersioned;
949
950
        if ($isVersioned) {
951
            $versionField = 'version';
952
            $metadata->versionField = $versionField;
953
            $metadata->reflFields[$versionField] = new \ReflectionProperty($class, $versionField);
954
        }
955
956
        return $metadata;
957
    }
958
}
959