Completed
Pull Request — master (#1045)
by Mathieu
01:34
created

ModelManagerTest::testSortParameters()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 55
rs 8.9818
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\Mapping\ClassMetadata;
25
use Doctrine\ORM\Mapping\ClassMetadataFactory;
26
use Doctrine\ORM\Mapping\ClassMetadataInfo;
27
use Doctrine\ORM\OptimisticLockException;
28
use Doctrine\ORM\Query;
29
use Doctrine\ORM\QueryBuilder;
30
use Doctrine\Persistence\ManagerRegistry;
31
use Doctrine\Persistence\ObjectManager;
32
use PHPUnit\Framework\MockObject\MockObject;
33
use PHPUnit\Framework\TestCase;
34
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
35
use Sonata\AdminBundle\Datagrid\Datagrid;
36
use Sonata\AdminBundle\Datagrid\DatagridInterface;
37
use Sonata\AdminBundle\Exception\LockException;
38
use Sonata\AdminBundle\Exception\ModelManagerException;
39
use Sonata\DoctrineORMAdminBundle\Admin\FieldDescription;
40
use Sonata\DoctrineORMAdminBundle\Datagrid\OrderByToSelectWalker;
41
use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery;
42
use Sonata\DoctrineORMAdminBundle\Model\ModelManager;
43
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\ProductIdType;
44
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\UuidBinaryType;
45
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\UuidType;
46
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\ValueObjectWithMagicToStringImpl;
47
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\DoctrineType\ValueObjectWithToStringImpl;
48
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\AbstractEntity;
49
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\AssociatedEntity;
50
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\ContainerEntity;
51
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\Embeddable\EmbeddedEntity;
52
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\Embeddable\SubEmbeddedEntity;
53
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\Product;
54
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\ProductId;
55
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\ProtectedEntity;
56
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\SimpleEntity;
57
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\UuidBinaryEntity;
58
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\UuidEntity;
59
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\VersionedEntity;
60
use Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Util\NonIntegerIdentifierTestClass;
61
62
class ModelManagerTest extends TestCase
63
{
64
    public static function setUpBeforeClass(): void
65
    {
66
        if (!Type::hasType(UuidType::NAME)) {
67
            Type::addType(UuidType::NAME, UuidType::class);
68
        }
69
        if (!Type::hasType(UuidBinaryType::NAME)) {
70
            Type::addType(UuidBinaryType::NAME, UuidBinaryType::class);
71
        }
72
        if (!Type::hasType(ProductIdType::NAME)) {
73
            Type::addType(ProductIdType::NAME, ProductIdType::class);
74
        }
75
    }
76
77
    public function valueObjectDataProvider(): array
78
    {
79
        return [
80
            'value object with toString implementation' => [ValueObjectWithToStringImpl::class],
81
            'value object with magic toString implementation' => [ValueObjectWithMagicToStringImpl::class],
82
        ];
83
    }
84
85
    /**
86
     * @dataProvider valueObjectDataProvider
87
     */
88
    public function testGetIdentifierValuesWhenIdentifierIsValueObjectWithToStringMethod(string $vbClassName): void
89
    {
90
        $entity = new UuidBinaryEntity(new $vbClassName('a7ef873a-e7b5-11e9-81b4-2a2ae2dbcce4'));
91
92
        $platform = $this->createMock(MySqlPlatform::class);
93
94
        $connection = $this->createMock(Connection::class);
95
        $connection->method('getDatabasePlatform')->willReturn($platform);
96
97
        $classMetadata = $this->createMock(ClassMetadataInfo::class);
98
        $classMetadata->method('getIdentifierValues')->willReturn([$entity->getId()]);
99
        $classMetadata->method('getTypeOfField')->willReturn(UuidBinaryType::NAME);
100
101
        $classMetadataFactory = $this->createMock(ClassMetadataFactory::class);
102
        $classMetadataFactory->method('getMetadataFor')->willReturn($classMetadata);
103
104
        $entityManager = $this->createMock(EntityManager::class);
105
        $entityManager->method('getMetadataFactory')->willReturn($classMetadataFactory);
106
        $entityManager->method('getConnection')->willReturn($connection);
107
108
        $registry = $this->createMock(ManagerRegistry::class);
109
        $registry->method('getManagerForClass')->willReturn($entityManager);
110
111
        $manager = new ModelManager($registry);
112
113
        $this->assertSame(
114
            ['a7ef873a-e7b5-11e9-81b4-2a2ae2dbcce4'],
115
            $manager->getIdentifierValues($entity)
116
        );
117
    }
118
119
    public function testSortParameters(): void
120
    {
121
        $registry = $this->createMock(ManagerRegistry::class);
122
123
        $manager = new ModelManager($registry);
124
125
        $datagrid1 = $this->createMock(Datagrid::class);
126
        $datagrid2 = $this->createMock(Datagrid::class);
127
128
        $field1 = new FieldDescription();
129
        $field1->setName('field1');
130
131
        $field2 = new FieldDescription();
132
        $field2->setName('field2');
133
134
        $field3 = new FieldDescription();
135
        $field3->setName('field3');
136
        $field3->setOption('sortable', 'field3sortBy');
137
138
        $datagrid1
139
            ->expects($this->any())
140
            ->method('getValues')
141
            ->willReturn([
142
                '_sort_by' => $field1,
143
                '_sort_order' => 'ASC',
144
            ]);
145
146
        $datagrid2
147
            ->expects($this->any())
148
            ->method('getValues')
149
            ->willReturn([
150
                '_sort_by' => $field3,
151
                '_sort_order' => 'ASC',
152
            ]);
153
154
        $parameters = $manager->getSortParameters($field1, $datagrid1);
155
156
        $this->assertSame('DESC', $parameters['filter']['_sort_order']);
157
        $this->assertSame('field1', $parameters['filter']['_sort_by']);
158
159
        $parameters = $manager->getSortParameters($field2, $datagrid1);
160
161
        $this->assertSame('ASC', $parameters['filter']['_sort_order']);
162
        $this->assertSame('field2', $parameters['filter']['_sort_by']);
163
164
        $parameters = $manager->getSortParameters($field3, $datagrid1);
165
166
        $this->assertSame('ASC', $parameters['filter']['_sort_order']);
167
        $this->assertSame('field3sortBy', $parameters['filter']['_sort_by']);
168
169
        $parameters = $manager->getSortParameters($field3, $datagrid2);
170
171
        $this->assertSame('DESC', $parameters['filter']['_sort_order']);
172
        $this->assertSame('field3sortBy', $parameters['filter']['_sort_by']);
173
    }
174
175
    public function getVersionDataProvider(): array
176
    {
177
        return [
178
            [true],
179
            [false],
180
        ];
181
    }
182
183
    /**
184
     * @dataProvider getVersionDataProvider
185
     */
186
    public function testGetVersion($isVersioned): void
187
    {
188
        $object = new VersionedEntity();
189
190
        $modelManager = $this->getMockBuilder(ModelManager::class)
191
            ->disableOriginalConstructor()
192
            ->setMethods(['getMetadata'])
193
            ->getMock();
194
195
        $metadata = $this->getMetadata(\get_class($object), $isVersioned);
196
197
        $modelManager->expects($this->any())
198
            ->method('getMetadata')
199
            ->willReturn($metadata);
200
201
        if ($isVersioned) {
202
            $object->version = 123;
203
204
            $this->assertNotNull($modelManager->getLockVersion($object));
205
        } else {
206
            $this->assertNull($modelManager->getLockVersion($object));
207
        }
208
    }
209
210
    public function lockDataProvider(): array
211
    {
212
        return [
213
            [true,  false],
214
            [true,  true],
215
            [false, false],
216
        ];
217
    }
218
219
    /**
220
     * @dataProvider lockDataProvider
221
     */
222
    public function testLock($isVersioned, $expectsException): void
223
    {
224
        $object = new VersionedEntity();
225
226
        $em = $this->getMockBuilder(EntityManager::class)
227
            ->disableOriginalConstructor()
228
            ->setMethods(['lock'])
229
            ->getMock();
230
231
        $modelManager = $this->getMockBuilder(ModelManager::class)
232
            ->disableOriginalConstructor()
233
            ->setMethods(['getMetadata', 'getEntityManager'])
234
            ->getMock();
235
236
        $modelManager->expects($this->any())
237
            ->method('getEntityManager')
238
            ->willReturn($em);
239
240
        $metadata = $this->getMetadata(\get_class($object), $isVersioned);
241
242
        $modelManager->expects($this->any())
243
            ->method('getMetadata')
244
            ->willReturn($metadata);
245
246
        $em->expects($isVersioned ? $this->once() : $this->never())
247
            ->method('lock');
248
249
        if ($expectsException) {
250
            $em->expects($this->once())
251
                ->method('lock')
252
                ->will($this->throwException(OptimisticLockException::lockFailed($object)));
253
254
            $this->expectException(LockException::class);
255
        }
256
257
        $modelManager->lock($object, 123);
258
    }
259
260
    public function testGetParentMetadataForProperty(): void
261
    {
262
        $containerEntityClass = ContainerEntity::class;
263
        $associatedEntityClass = AssociatedEntity::class;
264
        $embeddedEntityClass = EmbeddedEntity::class;
265
        $modelManagerClass = ModelManager::class;
266
267
        $em = $this->createMock(EntityManager::class);
268
269
        /** @var MockObject|ModelManager $modelManager */
270
        $modelManager = $this->getMockBuilder($modelManagerClass)
271
            ->disableOriginalConstructor()
272
            ->setMethods(['getMetadata', 'getEntityManager'])
273
            ->getMock();
274
275
        $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...
276
            ->method('getEntityManager')
277
            ->willReturn($em);
278
279
        $containerEntityMetadata = $this->getMetadataForContainerEntity();
280
        $associatedEntityMetadata = $this->getMetadataForAssociatedEntity();
281
        $embeddedEntityMetadata = $this->getMetadataForEmbeddedEntity();
282
283
        $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...
284
            ->willReturnMap(
285
                [
286
                        [$containerEntityClass, $containerEntityMetadata],
287
                        [$embeddedEntityClass, $embeddedEntityMetadata],
288
                        [$associatedEntityClass, $associatedEntityMetadata],
289
                    ]
290
            );
291
292
        /** @var ClassMetadata $metadata */
293
        list($metadata, $lastPropertyName) = $modelManager
294
            ->getParentMetadataForProperty($containerEntityClass, 'plainField');
295
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'integer');
296
297
        list($metadata, $lastPropertyName) = $modelManager
298
            ->getParentMetadataForProperty($containerEntityClass, 'associatedEntity.plainField');
299
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'string');
300
301
        list($metadata, $lastPropertyName) = $modelManager
302
            ->getParentMetadataForProperty($containerEntityClass, 'embeddedEntity.plainField');
303
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
304
305
        list($metadata, $lastPropertyName) = $modelManager
306
            ->getParentMetadataForProperty($containerEntityClass, 'associatedEntity.embeddedEntity.plainField');
307
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
308
309
        list($metadata, $lastPropertyName) = $modelManager
310
            ->getParentMetadataForProperty(
311
                $containerEntityClass,
312
                'associatedEntity.embeddedEntity.subEmbeddedEntity.plainField'
313
            );
314
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
315
    }
316
317
    public function getMetadataForEmbeddedEntity()
318
    {
319
        $metadata = new ClassMetadata(EmbeddedEntity::class);
320
321
        $metadata->fieldMappings = [
322
            'plainField' => [
323
                'fieldName' => 'plainField',
324
                'columnName' => 'plainField',
325
                'type' => 'boolean',
326
            ],
327
        ];
328
329
        return $metadata;
330
    }
331
332
    public function getMetadataForSubEmbeddedEntity()
333
    {
334
        $metadata = new ClassMetadata(SubEmbeddedEntity::class);
335
336
        $metadata->fieldMappings = [
337
            'plainField' => [
338
                'fieldName' => 'plainField',
339
                'columnName' => 'plainField',
340
                'type' => 'boolean',
341
            ],
342
        ];
343
344
        return $metadata;
345
    }
346
347
    public function getMetadataForAssociatedEntity()
348
    {
349
        $embeddedEntityClass = EmbeddedEntity::class;
350
        $subEmbeddedEntityClass = SubEmbeddedEntity::class;
351
352
        $metadata = new ClassMetadata(AssociatedEntity::class);
353
354
        $metadata->fieldMappings = [
355
            'plainField' => [
356
                'fieldName' => 'plainField',
357
                'columnName' => 'plainField',
358
                'type' => 'string',
359
            ],
360
        ];
361
362
        $metadata->embeddedClasses['embeddedEntity'] = [
363
            'class' => $embeddedEntityClass,
364
            'columnPrefix' => 'embedded_entity_',
365
        ];
366
        $metadata->embeddedClasses['embeddedEntity.subEmbeddedEntity'] = [
367
            'class' => $subEmbeddedEntityClass,
368
            'columnPrefix' => 'embedded_entity_sub_embedded_entity_',
369
            'declaredField' => 'embeddedEntity',
370
            'originalField' => 'subEmbeddedEntity',
371
        ];
372
373
        $metadata->inlineEmbeddable('embeddedEntity', $this->getMetadataForEmbeddedEntity());
374
        $metadata->inlineEmbeddable('embeddedEntity.subEmbeddedEntity', $this->getMetadataForSubEmbeddedEntity());
375
376
        return $metadata;
377
    }
378
379
    public function getMetadataForContainerEntity()
380
    {
381
        $containerEntityClass = ContainerEntity::class;
382
        $associatedEntityClass = AssociatedEntity::class;
383
        $embeddedEntityClass = EmbeddedEntity::class;
384
        $subEmbeddedEntityClass = SubEmbeddedEntity::class;
385
386
        $metadata = new ClassMetadata($containerEntityClass);
387
388
        $metadata->fieldMappings = [
389
            'plainField' => [
390
                'fieldName' => 'plainField',
391
                'columnName' => 'plainField',
392
                'type' => 'integer',
393
            ],
394
        ];
395
396
        $metadata->associationMappings['associatedEntity'] = [
397
            'fieldName' => 'associatedEntity',
398
            'targetEntity' => $associatedEntityClass,
399
            'sourceEntity' => $containerEntityClass,
400
        ];
401
402
        $metadata->embeddedClasses['embeddedEntity'] = [
403
            'class' => $embeddedEntityClass,
404
            'columnPrefix' => 'embeddedEntity',
405
        ];
406
        $metadata->embeddedClasses['embeddedEntity.subEmbeddedEntity'] = [
407
            'class' => $subEmbeddedEntityClass,
408
            'columnPrefix' => 'embedded_entity_sub_embedded_entity_',
409
            'declaredField' => 'embeddedEntity',
410
            'originalField' => 'subEmbeddedEntity',
411
        ];
412
413
        $metadata->inlineEmbeddable('embeddedEntity', $this->getMetadataForEmbeddedEntity());
414
        $metadata->inlineEmbeddable('embeddedEntity.subEmbeddedEntity', $this->getMetadataForSubEmbeddedEntity());
415
416
        return $metadata;
417
    }
418
419
    public function testNonIntegerIdentifierType(): void
420
    {
421
        $uuid = new NonIntegerIdentifierTestClass('efbcfc4b-8c43-4d42-aa4c-d707e55151ac');
422
        $entity = new UuidEntity($uuid);
423
424
        $meta = $this->createMock(ClassMetadata::class);
425
        $meta->expects($this->any())
426
            ->method('getIdentifierValues')
427
            ->willReturn([$entity->getId()]);
428
        $meta->expects($this->any())
429
            ->method('getTypeOfField')
430
            ->willReturn(UuidType::NAME);
431
432
        $mf = $this->createMock(ClassMetadataFactory::class);
433
        $mf->expects($this->any())
434
            ->method('getMetadataFor')
435
            ->willReturn($meta);
436
437
        $platform = $this->createMock(PostgreSqlPlatform::class);
438
        $platform->expects($this->any())
439
            ->method('hasDoctrineTypeMappingFor')
440
            ->with(UuidType::NAME)
441
            ->willReturn(false);
442
        $platform->expects($this->never())
443
            ->method('getDoctrineTypeMapping');
444
445
        $conn = $this->createMock(Connection::class);
446
        $conn->expects($this->any())
447
            ->method('getDatabasePlatform')
448
            ->willReturn($platform);
449
450
        $em = $this->createMock(EntityManager::class);
451
        $em->expects($this->any())
452
            ->method('getMetadataFactory')
453
            ->willReturn($mf);
454
        $em->expects($this->any())
455
            ->method('getConnection')
456
            ->willReturn($conn);
457
458
        $registry = $this->createMock(ManagerRegistry::class);
459
        $registry->expects($this->any())
460
            ->method('getManagerForClass')
461
            ->willReturn($em);
462
463
        $manager = new ModelManager($registry);
464
        $result = $manager->getIdentifierValues($entity);
465
466
        $this->assertSame($entity->getId()->toString(), $result[0]);
467
    }
468
469
    public function testIntegerIdentifierType(): void
470
    {
471
        $id = new ProductId(12345);
472
        $entity = new Product($id, 'Some product');
473
474
        $meta = $this->createMock(ClassMetadata::class);
475
        $meta->expects($this->any())
476
            ->method('getIdentifierValues')
477
            ->willReturn([$entity->getId()]);
478
        $meta->expects($this->any())
479
            ->method('getTypeOfField')
480
            ->willReturn(ProductIdType::NAME);
481
482
        $mf = $this->createMock(ClassMetadataFactory::class);
483
        $mf->expects($this->any())
484
            ->method('getMetadataFor')
485
            ->willReturn($meta);
486
487
        $platform = $this->createMock(PostgreSqlPlatform::class);
488
        $platform->expects($this->any())
489
            ->method('hasDoctrineTypeMappingFor')
490
            ->with(ProductIdType::NAME)
491
            ->willReturn(false);
492
        $platform->expects($this->never())
493
            ->method('getDoctrineTypeMapping');
494
495
        $conn = $this->createMock(Connection::class);
496
        $conn->expects($this->any())
497
            ->method('getDatabasePlatform')
498
            ->willReturn($platform);
499
500
        $em = $this->createMock(EntityManager::class);
501
        $em->expects($this->any())
502
            ->method('getMetadataFactory')
503
            ->willReturn($mf);
504
        $em->expects($this->any())
505
            ->method('getConnection')
506
            ->willReturn($conn);
507
508
        $registry = $this->createMock(ManagerRegistry::class);
509
        $registry->expects($this->any())
510
            ->method('getManagerForClass')
511
            ->willReturn($em);
512
513
        $manager = new ModelManager($registry);
514
        $result = $manager->getIdentifierValues($entity);
515
516
        $this->assertSame((string) $entity->getId()->getId(), $result[0]);
517
    }
518
519
    public function testAssociationIdentifierType(): void
520
    {
521
        $entity = new ContainerEntity(new AssociatedEntity(42, new EmbeddedEntity()), new EmbeddedEntity());
522
523
        $meta = $this->createMock(ClassMetadata::class);
524
        $meta->expects($this->any())
525
            ->method('getIdentifierValues')
526
            ->willReturn([$entity->getAssociatedEntity()->getPlainField()]);
527
        $meta->expects($this->any())
528
            ->method('getTypeOfField')
529
            ->willReturn(null);
530
531
        $mf = $this->createMock(ClassMetadataFactory::class);
532
        $mf->expects($this->any())
533
            ->method('getMetadataFor')
534
            ->willReturn($meta);
535
536
        $platform = $this->createMock(PostgreSqlPlatform::class);
537
        $platform->expects($this->never())
538
            ->method('hasDoctrineTypeMappingFor');
539
540
        $conn = $this->createMock(Connection::class);
541
        $conn->expects($this->any())
542
            ->method('getDatabasePlatform')
543
            ->willReturn($platform);
544
545
        $em = $this->createMock(EntityManager::class);
546
        $em->expects($this->any())
547
            ->method('getMetadataFactory')
548
            ->willReturn($mf);
549
        $em->expects($this->any())
550
            ->method('getConnection')
551
            ->willReturn($conn);
552
553
        $registry = $this->createMock(ManagerRegistry::class);
554
        $registry->expects($this->any())
555
            ->method('getManagerForClass')
556
            ->willReturn($em);
557
558
        $manager = new ModelManager($registry);
559
        $result = $manager->getIdentifierValues($entity);
560
561
        $this->assertSame(42, $result[0]);
562
    }
563
564
    /**
565
     * [sortBy, sortOrder, isAddOrderBy].
566
     */
567
    public function getSortableInDataSourceIteratorDataProvider(): array
568
    {
569
        return [
570
            [null, null, false],
571
            ['', 'ASC', false],
572
            ['field', 'ASC', true],
573
            ['field', null, true],
574
        ];
575
    }
576
577
    /**
578
     * @dataProvider getSortableInDataSourceIteratorDataProvider
579
     *
580
     * @param string|null $sortBy
581
     * @param string|null $sortOrder
582
     * @param bool        $isAddOrderBy
583
     */
584
    public function testSortableInDataSourceIterator($sortBy, $sortOrder, $isAddOrderBy): void
585
    {
586
        $datagrid = $this->getMockForAbstractClass(DatagridInterface::class);
587
        $configuration = $this->getMockBuilder(Configuration::class)->getMock();
588
        $configuration->expects($this->any())
589
            ->method('getDefaultQueryHints')
590
            ->willReturn([]);
591
592
        $em = $this->getMockBuilder(EntityManager::class)
593
            ->disableOriginalConstructor()
594
            ->getMock();
595
596
        $em->expects($this->any())
597
            ->method('getConfiguration')
598
            ->willReturn($configuration);
599
600
        $queryBuilder = $this->getMockBuilder(QueryBuilder::class)
601
            ->setConstructorArgs([$em])
602
            ->getMock();
603
        $query = new Query($em);
604
605
        $proxyQuery = $this->getMockBuilder(ProxyQuery::class)
606
            ->setConstructorArgs([$queryBuilder])
607
            ->setMethods(['getSortBy', 'getSortOrder', 'getRootAliases'])
608
            ->getMock();
609
610
        $proxyQuery->expects($this->any())
611
            ->method('getSortOrder')
612
            ->willReturn($sortOrder);
613
614
        $proxyQuery->expects($this->any())
615
            ->method('getSortBy')
616
            ->willReturn($sortBy);
617
618
        $queryBuilder->expects($isAddOrderBy ? $this->atLeastOnce() : $this->never())
619
            ->method('addOrderBy');
620
621
        $proxyQuery->expects($this->any())
622
            ->method('getRootAliases')
623
            ->willReturn(['a']);
624
625
        $queryBuilder->expects($this->any())
626
            ->method('getQuery')
627
            ->willReturn($query);
628
629
        $datagrid->expects($this->any())
630
            ->method('getQuery')
631
            ->willReturn($proxyQuery);
632
633
        $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock();
634
        $manager = new ModelManager($registry);
635
        $manager->getDataSourceIterator($datagrid, []);
636
637
        if ($isAddOrderBy) {
638
            $this->assertArrayHasKey($key = 'doctrine.customTreeWalkers', $hints = $query->getHints());
639
            $this->assertContains(OrderByToSelectWalker::class, $hints[$key]);
640
        }
641
    }
642
643
    public function testModelReverseTransform(): void
644
    {
645
        $class = SimpleEntity::class;
646
647
        $metadataFactory = $this->createMock(ClassMetadataFactory::class);
648
        $modelManager = $this->createMock(ObjectManager::class);
649
        $registry = $this->createMock(ManagerRegistry::class);
650
651
        $classMetadata = new ClassMetadata($class);
652
        $classMetadata->reflClass = new \ReflectionClass($class);
653
654
        $modelManager->expects($this->once())
655
            ->method('getMetadataFactory')
656
            ->willReturn($metadataFactory);
657
        $metadataFactory->expects($this->once())
658
            ->method('getMetadataFor')
659
            ->with($class)
660
            ->willReturn($classMetadata);
661
        $registry->expects($this->once())
662
            ->method('getManagerForClass')
663
            ->with($class)
664
            ->willReturn($modelManager);
665
666
        $manager = new ModelManager($registry);
667
        $this->assertInstanceOf($class, $object = $manager->modelReverseTransform(
668
            $class,
669
            [
670
                'schmeckles' => 42,
671
                'multi_word_property' => 'hello',
672
            ]
673
        ));
674
        $this->assertSame(42, $object->getSchmeckles());
675
        $this->assertSame('hello', $object->getMultiWordProperty());
676
    }
677
678
    public function testCollections(): void
679
    {
680
        $registry = $this->createMock(ManagerRegistry::class);
681
        $model = new ModelManager($registry);
682
683
        $collection = $model->getModelCollectionInstance('whyDoWeEvenHaveThisParameter');
684
        $this->assertInstanceOf(ArrayCollection::class, $collection);
685
686
        $item1 = 'item1';
687
        $item2 = 'item2';
688
        $model->collectionAddElement($collection, $item1);
0 ignored issues
show
Bug introduced by
It seems like $collection defined by $model->getModelCollecti...EvenHaveThisParameter') on line 683 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...
689
        $model->collectionAddElement($collection, $item2);
0 ignored issues
show
Documentation introduced by
$item2 is of type string, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
690
691
        $this->assertTrue($model->collectionHasElement($collection, $item1));
692
693
        $model->collectionRemoveElement($collection, $item1);
694
695
        $this->assertFalse($model->collectionHasElement($collection, $item1));
696
697
        $model->collectionClear($collection);
698
699
        $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...
700
    }
701
702
    public function testModelTransform(): void
703
    {
704
        $registry = $this->createMock(ManagerRegistry::class);
705
        $model = new ModelManager($registry);
706
707
        $result = $model->modelTransform('thisIsNotUsed', 'doWeNeedThisMethod');
0 ignored issues
show
Documentation introduced by
'doWeNeedThisMethod' is of type string, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
708
709
        $this->assertSame('doWeNeedThisMethod', $result);
710
    }
711
712
    public function testGetPaginationParameters(): void
713
    {
714
        $datagrid = $this->createMock(DatagridInterface::class);
715
        $field = $this->createMock(FieldDescriptionInterface::class);
716
        $registry = $this->createMock(ManagerRegistry::class);
717
718
        $datagrid->expects($this->once())
719
            ->method('getValues')
720
            ->willReturn(['_sort_by' => $field]);
721
722
        $field->expects($this->once())
723
            ->method('getName')
724
            ->willReturn($name = 'test');
725
726
        $model = new ModelManager($registry);
727
728
        $result = $model->getPaginationParameters($datagrid, $page = 5);
729
730
        $this->assertSame($page, $result['filter']['_page']);
731
        $this->assertSame($name, $result['filter']['_sort_by']);
732
    }
733
734
    public function testGetModelInstanceException(): void
735
    {
736
        $registry = $this->createMock(ManagerRegistry::class);
737
738
        $model = new ModelManager($registry);
739
740
        $this->expectException(\RuntimeException::class);
741
742
        $model->getModelInstance(AbstractEntity::class);
743
    }
744
745
    public function testGetModelInstanceForProtectedEntity(): void
746
    {
747
        $registry = $this->createMock(ManagerRegistry::class);
748
749
        $model = new ModelManager($registry);
750
751
        $this->assertInstanceOf(ProtectedEntity::class, $model->getModelInstance(ProtectedEntity::class));
752
    }
753
754
    public function testGetEntityManagerException(): void
755
    {
756
        $registry = $this->createMock(ManagerRegistry::class);
757
758
        $model = new ModelManager($registry);
759
760
        $this->expectException(\RuntimeException::class);
761
762
        $model->getEntityManager(VersionedEntity::class);
763
    }
764
765
    public function testGetNewFieldDescriptionInstanceException(): void
766
    {
767
        $registry = $this->createMock(ManagerRegistry::class);
768
769
        $model = new ModelManager($registry);
770
771
        $this->expectException(\RuntimeException::class);
772
773
        $model->getNewFieldDescriptionInstance(VersionedEntity::class, [], []);
0 ignored issues
show
Documentation introduced by
array() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
774
    }
775
776
    /**
777
     * @dataProvider createUpdateRemoveData
778
     */
779
    public function testCreate($exception): void
780
    {
781
        $registry = $this->createMock(ManagerRegistry::class);
782
783
        $entityManger = $this->createMock(EntityManager::class);
784
785
        $registry->expects($this->once())
786
            ->method('getManagerForClass')
787
            ->willReturn($entityManger);
788
789
        $entityManger->expects($this->once())
790
            ->method('persist');
791
792
        $entityManger->expects($this->once())
793
            ->method('flush')
794
            ->willThrowException($exception);
795
796
        $model = new ModelManager($registry);
797
798
        $this->expectException(ModelManagerException::class);
799
800
        $model->create(new VersionedEntity());
801
    }
802
803
    public function createUpdateRemoveData(): array
804
    {
805
        return [
806
            'PDOException' => [
807
                new \PDOException(),
808
            ],
809
            'DBALException' => [
810
                new DBALException(),
811
            ],
812
        ];
813
    }
814
815
    /**
816
     * @dataProvider createUpdateRemoveData
817
     */
818
    public function testUpdate($exception): void
819
    {
820
        $registry = $this->createMock(ManagerRegistry::class);
821
822
        $entityManger = $this->createMock(EntityManager::class);
823
824
        $registry->expects($this->once())
825
            ->method('getManagerForClass')
826
            ->willReturn($entityManger);
827
828
        $entityManger->expects($this->once())
829
            ->method('persist');
830
831
        $entityManger->expects($this->once())
832
            ->method('flush')
833
            ->willThrowException($exception);
834
835
        $model = new ModelManager($registry);
836
837
        $this->expectException(ModelManagerException::class);
838
839
        $model->update(new VersionedEntity());
840
    }
841
842
    /**
843
     * @dataProvider createUpdateRemoveData
844
     */
845
    public function testRemove($exception): void
846
    {
847
        $registry = $this->createMock(ManagerRegistry::class);
848
849
        $entityManger = $this->createMock(EntityManager::class);
850
851
        $registry->expects($this->once())
852
            ->method('getManagerForClass')
853
            ->willReturn($entityManger);
854
855
        $entityManger->expects($this->once())
856
            ->method('remove');
857
858
        $entityManger->expects($this->once())
859
            ->method('flush')
860
            ->willThrowException($exception);
861
862
        $model = new ModelManager($registry);
863
864
        $this->expectException(ModelManagerException::class);
865
866
        $model->delete(new VersionedEntity());
867
    }
868
869
    public function testFindBadId(): void
870
    {
871
        $registry = $this->createMock(ManagerRegistry::class);
872
873
        $model = new ModelManager($registry);
874
875
        $this->assertNull($model->find('notImportant', null));
876
    }
877
878
    /**
879
     * @dataProvider getWrongEntities
880
     *
881
     * @param mixed $entity
882
     */
883
    public function testNormalizedIdentifierException($entity): void
884
    {
885
        $registry = $this->createMock(ManagerRegistry::class);
886
887
        $model = new ModelManager($registry);
888
889
        $this->expectException(\RuntimeException::class);
890
891
        $model->getNormalizedIdentifier($entity);
892
    }
893
894
    public function getWrongEntities(): iterable
895
    {
896
        yield [0];
897
        yield [1];
898
        yield [false];
899
        yield [true];
900
        yield [[]];
901
        yield [''];
902
        yield ['sonata-project'];
903
    }
904
905
    public function testGetUrlsafeIdentifierNull(): void
906
    {
907
        $registry = $this->createMock(ManagerRegistry::class);
908
909
        $model = new ModelManager($registry);
910
911
        $this->assertNull($model->getNormalizedIdentifier(null));
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
912
    }
913
914
    private function getMetadata($class, $isVersioned)
915
    {
916
        $metadata = new ClassMetadata($class);
917
918
        $metadata->isVersioned = $isVersioned;
919
920
        if ($isVersioned) {
921
            $versionField = 'version';
922
            $metadata->versionField = $versionField;
923
            $metadata->reflFields[$versionField] = new \ReflectionProperty($class, $versionField);
924
        }
925
926
        return $metadata;
927
    }
928
}
929