Failed Conditions
Push — master ( d60694...8601d9 )
by Guilherme
09:39
created

Tests/ORM/Mapping/ClassMetadataFactoryTest.php (1 issue)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Tests\ORM\Mapping;
6
7
use Doctrine\Common\EventManager;
8
use Doctrine\DBAL\Connection;
9
use Doctrine\DBAL\Platforms\AbstractPlatform;
10
use Doctrine\DBAL\Types\Type;
11
use Doctrine\ORM\Configuration;
12
use Doctrine\ORM\EntityManagerInterface;
13
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
14
use Doctrine\ORM\Events;
15
use Doctrine\ORM\Exception\ORMException;
0 ignored issues
show
Type Doctrine\ORM\Exception\ORMException is not used in this file.
Loading history...
16
use Doctrine\ORM\Mapping;
17
use Doctrine\ORM\Mapping\ClassMetadata;
18
use Doctrine\ORM\Mapping\ClassMetadataFactory;
19
use Doctrine\ORM\Mapping\Driver\MappingDriver;
20
use Doctrine\ORM\Mapping\MappingException;
21
use Doctrine\ORM\Reflection\ReflectionService;
22
use Doctrine\ORM\Sequencing\Generator;
23
use Doctrine\Tests\Mocks\ConnectionMock;
24
use Doctrine\Tests\Mocks\DriverMock;
25
use Doctrine\Tests\Mocks\EntityManagerMock;
26
use Doctrine\Tests\Mocks\MetadataDriverMock;
27
use Doctrine\Tests\Models\CMS\CmsArticle;
28
use Doctrine\Tests\Models\CMS\CmsUser;
29
use Doctrine\Tests\Models\DDC4006\DDC4006User;
30
use Doctrine\Tests\Models\JoinedInheritanceType\AnotherChildClass;
31
use Doctrine\Tests\Models\JoinedInheritanceType\ChildClass;
32
use Doctrine\Tests\Models\JoinedInheritanceType\RootClass;
33
use Doctrine\Tests\Models\Quote;
34
use Doctrine\Tests\OrmTestCase;
35
use DoctrineGlobalArticle;
36
use Exception;
37
use InvalidArgumentException;
38
use stdClass;
39
use function reset;
40
use function sprintf;
41
42
class ClassMetadataFactoryTest extends OrmTestCase
43
{
44
    public function testGetMetadataForSingleClass() : void
45
    {
46
        $mockDriver    = new MetadataDriverMock();
47
        $entityManager = $this->createEntityManager($mockDriver);
48
49
        $conn         = $entityManager->getConnection();
50
        $mockPlatform = $conn->getDatabasePlatform();
51
        $mockPlatform->setPrefersSequences(true);
52
        $mockPlatform->setPrefersIdentityColumns(false);
53
54
        $cm1 = $this->createValidClassMetadata();
55
56
        // SUT
57
        $cmf = new ClassMetadataFactory();
58
        $cmf->setEntityManager($entityManager);
59
        $cmf->setMetadataFor($cm1->getClassName(), $cm1);
60
61
        // Prechecks
62
        self::assertCount(0, $cm1->getAncestorsIterator());
63
        self::assertEquals(Mapping\InheritanceType::NONE, $cm1->inheritanceType);
64
        self::assertEquals(Mapping\GeneratorType::SEQUENCE, $cm1->getProperty('id')->getValueGenerator()->getType());
65
        self::assertTrue($cm1->hasField('name'));
66
        self::assertCount(4, $cm1->getPropertiesIterator()); // 2 fields + 2 associations
67
        self::assertEquals('group', $cm1->table->getName());
68
69
        // Go
70
        $cmMap1 = $cmf->getMetadataFor($cm1->getClassName());
71
72
        self::assertSame($cm1, $cmMap1);
73
        self::assertEquals('group', $cmMap1->table->getName());
74
        self::assertCount(0, $cmMap1->getAncestorsIterator());
75
        self::assertTrue($cmMap1->hasField('name'));
76
    }
77
78
    public function testHasGetMetadataNamespaceSeparatorIsNotNormalized() : void
79
    {
80
        require_once __DIR__ . '/../../Models/Global/GlobalNamespaceModel.php';
81
82
        $metadataDriver = $this->createAnnotationDriver([__DIR__ . '/../../Models/Global/']);
83
84
        $entityManager = $this->createEntityManager($metadataDriver);
85
86
        $mf = $entityManager->getMetadataFactory();
87
88
        self::assertSame(
89
            $mf->getMetadataFor(DoctrineGlobalArticle::class),
90
            $mf->getMetadataFor('\\' . DoctrineGlobalArticle::class)
91
        );
92
        self::assertTrue($mf->hasMetadataFor(DoctrineGlobalArticle::class));
93
        self::assertTrue($mf->hasMetadataFor('\\' . DoctrineGlobalArticle::class));
94
    }
95
96
    /**
97
     * @group DDC-1512
98
     */
99
    public function testIsTransient() : void
100
    {
101
        $cmf    = new ClassMetadataFactory();
102
        $driver = $this->createMock(MappingDriver::class);
103
        $driver->expects($this->at(0))
104
               ->method('isTransient')
105
               ->with($this->equalTo(CmsUser::class))
106
               ->will($this->returnValue(true));
107
        $driver->expects($this->at(1))
108
               ->method('isTransient')
109
               ->with($this->equalTo(CmsArticle::class))
110
               ->will($this->returnValue(false));
111
112
        $em = $this->createEntityManager($driver);
113
114
        self::assertTrue($em->getMetadataFactory()->isTransient(CmsUser::class));
115
        self::assertFalse($em->getMetadataFactory()->isTransient(CmsArticle::class));
116
    }
117
118
    /**
119
     * @dataProvider classesInInheritanceWithNoMapProvider()
120
     */
121
    public function testNoDefaultDiscriminatorMapIsAssumed(string $rootClassName, string $targetClassName) : void
122
    {
123
        $cmf    = new ClassMetadataFactory();
124
        $driver = $this->createAnnotationDriver([__DIR__ . '/../../Models/JoinedInheritanceType/']);
125
        $em     = $this->createEntityManager($driver);
126
        $cmf->setEntityManager($em);
127
128
        $this->expectException(MappingException::class);
129
        $this->expectExceptionMessage(
130
            sprintf("Entity class '%s' is using inheritance but no discriminator map was defined.", $rootClassName)
131
        );
132
133
        $cmf->getMetadataFor($targetClassName);
134
    }
135
136
    /**
137
     * @return string[]
138
     */
139
    public function classesInInheritanceWithNoMapProvider() : iterable
140
    {
141
        yield 'root entity' => [RootClass::class, RootClass::class];
142
        yield 'child entity' => [RootClass::class, ChildClass::class];
143
        yield 'another child entity' => [RootClass::class, AnotherChildClass::class];
144
    }
145
146
    public function testGetAllMetadataWorksWithBadConnection() : void
147
    {
148
        // DDC-3551
149
        $conn       = $this->createMock(Connection::class);
150
        $mockDriver = new MetadataDriverMock();
151
        $conn->expects($this->any())
152
            ->method('getEventManager')
153
            ->willReturn(new EventManager());
154
        $em = $this->createEntityManager($mockDriver, $conn);
155
156
        $conn->expects($this->any())
157
            ->method('getDatabasePlatform')
158
            ->will($this->throwException(new Exception('Exception thrown in test when calling getDatabasePlatform')));
159
160
        $cmf = new ClassMetadataFactory();
161
        $cmf->setEntityManager($em);
162
163
        // getting all the metadata should work, even if get DatabasePlatform blows up
164
        $metadata = $cmf->getAllMetadata();
165
        // this will just be an empty array - there was no error
166
        self::assertEquals([], $metadata);
167
    }
168
169
    protected function createEntityManager($metadataDriver, $conn = null)
170
    {
171
        $driverMock = new DriverMock();
172
        $config     = new Configuration();
173
174
        $config->setProxyDir(__DIR__ . '/../../Proxies');
175
        $config->setProxyNamespace('Doctrine\Tests\Proxies');
176
177
        if (! $conn) {
178
            $conn = new ConnectionMock([], $driverMock, $config, new EventManager());
179
        }
180
        $eventManager = $conn->getEventManager();
181
182
        $config->setMetadataDriverImpl($metadataDriver);
183
184
        return EntityManagerMock::create($conn, $config, $eventManager);
185
    }
186
187
    /**
188
     * @return ClassMetadataFactoryTestSubject
189
     */
190
    protected function createTestFactory()
191
    {
192
        $mockDriver    = new MetadataDriverMock();
193
        $entityManager = $this->createEntityManager($mockDriver);
194
        $cmf           = new ClassMetadataFactoryTestSubject();
195
        $cmf->setEntityManager($entityManager);
196
197
        return $cmf;
198
    }
199
200
    /**
201
     * @return ClassMetadata
202
     */
203
    protected function createValidClassMetadata()
204
    {
205
        // Self-made metadata
206
        $metadataBuildingContext = new Mapping\ClassMetadataBuildingContext(
207
            $this->createMock(ClassMetadataFactory::class),
208
            $this->createMock(ReflectionService::class),
209
            $this->createMock(AbstractPlatform::class)
210
        );
211
212
        $cm1 = new ClassMetadata(TestEntity1::class, null, $metadataBuildingContext);
213
214
        $tableMetadata = new Mapping\TableMetadata();
215
        $tableMetadata->setName('group');
216
217
        $cm1->setTable($tableMetadata);
218
219
        // Add a mapped field
220
        $fieldMetadata = new Mapping\FieldMetadata('id');
221
222
        $fieldMetadata->setType(Type::getType('integer'));
223
        $fieldMetadata->setPrimaryKey(true);
224
        $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata(
225
            Mapping\GeneratorType::SEQUENCE,
226
            new Generator\SequenceGenerator("group_id_seq", 1)
227
        ));
228
229
        $cm1->addProperty($fieldMetadata);
230
231
        // Add a mapped field
232
        $fieldMetadata = new Mapping\FieldMetadata('name');
233
234
        $fieldMetadata->setType(Type::getType('string'));
235
236
        $cm1->addProperty($fieldMetadata);
237
238
        // and a mapped association
239
        $association = new Mapping\OneToOneAssociationMetadata('other');
240
241
        $association->setTargetEntity(TestEntity1::class);
242
        $association->setMappedBy('this');
243
244
        $cm1->addProperty($association);
245
246
        // and an association on the owning side
247
        $joinColumns = [];
248
249
        $joinColumn = new Mapping\JoinColumnMetadata();
250
251
        $joinColumn->setColumnName('other_id');
252
        $joinColumn->setReferencedColumnName('id');
253
254
        $joinColumns[] = $joinColumn;
255
256
        $association = new Mapping\OneToOneAssociationMetadata('association');
257
258
        $association->setJoinColumns($joinColumns);
259
        $association->setTargetEntity(TestEntity1::class);
260
261
        $cm1->addProperty($association);
262
263
        return $cm1;
264
    }
265
266
    /**
267
     * @group DDC-1845
268
     */
269
    public function testQuoteMetadata() : void
270
    {
271
        $cmf    = new ClassMetadataFactory();
272
        $driver = $this->createAnnotationDriver([__DIR__ . '/../../Models/Quote/']);
273
        $em     = $this->createEntityManager($driver);
274
        $cmf->setEntityManager($em);
275
276
        $userMetadata    = $cmf->getMetadataFor(Quote\User::class);
277
        $phoneMetadata   = $cmf->getMetadataFor(Quote\Phone::class);
278
        $groupMetadata   = $cmf->getMetadataFor(Quote\Group::class);
279
        $addressMetadata = $cmf->getMetadataFor(Quote\Address::class);
280
281
        // Phone Class Metadata
282
        self::assertNotNull($phoneMetadata->getProperty('number'));
283
        self::assertEquals('phone-number', $phoneMetadata->getProperty('number')->getColumnName());
284
285
        $user                = $phoneMetadata->getProperty('user');
286
        $userJoinColumns     = $user->getJoinColumns();
287
        $phoneUserJoinColumn = reset($userJoinColumns);
288
289
        self::assertEquals('user-id', $phoneUserJoinColumn->getColumnName());
290
        self::assertEquals('user-id', $phoneUserJoinColumn->getReferencedColumnName());
291
292
        // Address Class Metadata
293
        self::assertNotNull($addressMetadata->getProperty('id'));
294
        self::assertNotNull($addressMetadata->getProperty('zip'));
295
        self::assertEquals('address-id', $addressMetadata->getProperty('id')->getColumnName());
296
        self::assertEquals('address-zip', $addressMetadata->getProperty('zip')->getColumnName());
297
298
        // User Class Metadata
299
        self::assertNotNull($userMetadata->getProperty('id'));
300
        self::assertNotNull($userMetadata->getProperty('name'));
301
        self::assertEquals('user-id', $userMetadata->getProperty('id')->getColumnName());
302
        self::assertEquals('user-name', $userMetadata->getProperty('name')->getColumnName());
303
304
        $group               = $groupMetadata->getProperty('parent');
305
        $groupJoinColumns    = $group->getJoinColumns();
306
        $groupUserJoinColumn = reset($groupJoinColumns);
307
308
        self::assertEquals('parent-id', $groupUserJoinColumn->getColumnName());
309
        self::assertEquals('group-id', $groupUserJoinColumn->getReferencedColumnName());
310
311
        $user                  = $addressMetadata->getProperty('user');
312
        $userJoinColumns       = $user->getJoinColumns();
313
        $addressUserJoinColumn = reset($userJoinColumns);
314
315
        self::assertEquals('user-id', $addressUserJoinColumn->getColumnName());
316
        self::assertEquals('user-id', $addressUserJoinColumn->getReferencedColumnName());
317
318
        $groups                       = $userMetadata->getProperty('groups');
319
        $groupsJoinTable              = $groups->getJoinTable();
320
        $userGroupsJoinColumns        = $groupsJoinTable->getJoinColumns();
321
        $userGroupsJoinColumn         = reset($userGroupsJoinColumns);
322
        $userGroupsInverseJoinColumns = $groupsJoinTable->getInverseJoinColumns();
323
        $userGroupsInverseJoinColumn  = reset($userGroupsInverseJoinColumns);
324
325
        self::assertEquals('quote-users-groups', $groupsJoinTable->getName());
326
        self::assertEquals('user-id', $userGroupsJoinColumn->getColumnName());
327
        self::assertEquals('user-id', $userGroupsJoinColumn->getReferencedColumnName());
328
        self::assertEquals('group-id', $userGroupsInverseJoinColumn->getColumnName());
329
        self::assertEquals('group-id', $userGroupsInverseJoinColumn->getReferencedColumnName());
330
    }
331
332
    /**
333
     * @group DDC-3385
334
     * @group 1181
335
     * @group 385
336
     */
337
    public function testFallbackLoadingCausesEventTriggeringThatCanModifyFetchedMetadata() : void
338
    {
339
        $test = $this;
340
341
        /** @var ClassMetadata $metadata */
342
        $metadata     = $this->createMock(ClassMetadata::class);
343
        $cmf          = new ClassMetadataFactory();
344
        $mockDriver   = new MetadataDriverMock();
345
        $em           = $this->createEntityManager($mockDriver);
346
        $listener     = $this->getMockBuilder(stdClass::class)->setMethods(['onClassMetadataNotFound'])->getMock();
347
        $eventManager = $em->getEventManager();
348
349
        $cmf->setEntityManager($em);
350
351
        $listener
352
            ->expects($this->any())
353
            ->method('onClassMetadataNotFound')
354
            ->will($this->returnCallback(static function (OnClassMetadataNotFoundEventArgs $args) use ($metadata, $em, $test) {
355
                $test->assertNull($args->getFoundMetadata());
356
                $test->assertSame('Foo', $args->getClassName());
357
                $test->assertSame($em, $args->getObjectManager());
358
359
                $args->setFoundMetadata($metadata);
360
            }));
361
362
        $eventManager->addEventListener([Events::onClassMetadataNotFound], $listener);
363
364
        self::assertSame($metadata, $cmf->getMetadataFor('Foo'));
365
    }
366
367
    /**
368
     * @group DDC-3427
369
     */
370
    public function testAcceptsEntityManagerInterfaceInstances() : void
371
    {
372
        $classMetadataFactory = new ClassMetadataFactory();
373
374
        /** @var EntityManagerInterface $entityManager */
375
        $entityManager = $this->createMock(EntityManagerInterface::class);
376
377
        $classMetadataFactory->setEntityManager($entityManager);
378
379
        // not really the cleanest way to check it, but we won't add a getter to the CMF just for the sake of testing.
380
        self::assertAttributeSame($entityManager, 'em', $classMetadataFactory);
381
    }
382
383
    /**
384
     * @group embedded
385
     * @group DDC-3305
386
     */
387
    public function testRejectsEmbeddableWithoutValidClassName() : void
388
    {
389
        $metadata = $this->createValidClassMetadata();
390
391
        $metadata->mapEmbedded(
392
            [
393
                'fieldName'    => 'embedded',
394
                'class'        => '',
395
                'columnPrefix' => false,
396
            ]
397
        );
398
399
        $cmf = $this->createTestFactory();
400
401
        $cmf->setMetadataForClass($metadata->getClassName(), $metadata);
402
403
        $this->expectException(MappingException::class);
404
        $this->expectExceptionMessage('The embed mapping \'embedded\' misses the \'class\' attribute.');
405
406
        $cmf->getMetadataFor($metadata->getClassName());
407
    }
408
409
    /**
410
     * @group embedded
411
     * @group DDC-4006
412
     */
413
    public function testInheritsIdGeneratorMappingFromEmbeddable() : void
414
    {
415
        $cmf    = new ClassMetadataFactory();
416
        $driver = $this->createAnnotationDriver([__DIR__ . '/../../Models/DDC4006/']);
417
        $em     = $this->createEntityManager($driver);
418
        $cmf->setEntityManager($em);
419
420
        $userMetadata = $cmf->getMetadataFor(DDC4006User::class);
421
422
        self::assertTrue($userMetadata->isIdGeneratorIdentity());
423
    }
424
}
425
426
/* Test subject class with overridden factory method for mocking purposes */
427
class ClassMetadataFactoryTestSubject extends ClassMetadataFactory
428
{
429
    private $mockMetadata     = [];
430
    private $requestedClasses = [];
431
432
    protected function newClassMetadataInstance(
433
        string $className,
434
        ?Mapping\ClassMetadata $parent,
435
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
436
    ) : ClassMetadata {
437
        $this->requestedClasses[] = $className;
438
439
        if (! isset($this->mockMetadata[$className])) {
440
            throw new InvalidArgumentException(sprintf('No mock metadata found for class %s.', $className));
441
        }
442
443
        return $this->mockMetadata[$className];
444
    }
445
446
    public function setMetadataForClass($className, $metadata)
447
    {
448
        $this->mockMetadata[$className] = $metadata;
449
    }
450
451
    public function getRequestedClasses()
452
    {
453
        return $this->requestedClasses;
454
    }
455
}
456
457
class TestEntity1
458
{
459
    private $id;
460
    private $name;
461
    private $other;
462
    private $association;
463
    private $embedded;
464
}
465
466
class CustomIdGenerator implements Generator\Generator
467
{
468
    /**
469
     * {@inheritdoc}
470
     */
471
    public function generate(EntityManagerInterface $em, ?object $entity)
472
    {
473
    }
474
475
    /**
476
     * {@inheritdoc}
477
     */
478
    public function isPostInsertGenerator() : bool
479
    {
480
        return false;
481
    }
482
}
483