SchemaToolTest   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 271
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 11
eloc 149
c 3
b 0
f 0
dl 0
loc 271
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A testSchemaHasProperIndexesFromUniqueConstraintAnnotation() 0 17 1
A testAddUniqueIndexForUniqueFieldAnnotation() 0 19 1
A testDerivedCompositeKey() 0 46 2
A testPostGenerateEvents() 0 25 1
A testRemoveUniqueIndexOverruledByPrimaryKey() 0 17 1
A testNullDefaultNotAddedToCustomSchemaOptions() 0 11 1
A testSetDiscriminatorColumnWithoutLength() 0 24 1
A testPassColumnOptionsToJoinColumn() 0 32 1
A testPassColumnDefinitionToJoinColumn() 0 22 1
A testAnnotationOptionsAttribute() 0 24 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Tests\ORM\Tools;
6
7
use Doctrine\DBAL\Types\Type;
8
use Doctrine\ORM\Annotation as ORM;
9
use Doctrine\ORM\Mapping\DiscriminatorColumnMetadata;
10
use Doctrine\ORM\Mapping\InheritanceType;
11
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
12
use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs;
13
use Doctrine\ORM\Tools\SchemaTool;
14
use Doctrine\ORM\Tools\ToolEvents;
15
use Doctrine\Tests\Models\CMS\CmsAddress;
16
use Doctrine\Tests\Models\CMS\CmsArticle;
17
use Doctrine\Tests\Models\CMS\CmsComment;
18
use Doctrine\Tests\Models\CMS\CmsEmployee;
19
use Doctrine\Tests\Models\CMS\CmsGroup;
20
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
21
use Doctrine\Tests\Models\CMS\CmsUser;
22
use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedDerivedChildClass;
23
use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedDerivedIdentityClass;
24
use Doctrine\Tests\Models\CompositeKeyInheritance\JoinedDerivedRootClass;
25
use Doctrine\Tests\Models\Forum\ForumAvatar;
26
use Doctrine\Tests\Models\Forum\ForumUser;
27
use Doctrine\Tests\Models\NullDefault\NullDefaultColumn;
28
use Doctrine\Tests\OrmTestCase;
29
use function array_merge;
30
use function count;
31
use function current;
32
33
class SchemaToolTest extends OrmTestCase
34
{
35
    public function testAddUniqueIndexForUniqueFieldAnnotation() : void
36
    {
37
        $em         = $this->getTestEntityManager();
38
        $schemaTool = new SchemaTool($em);
39
40
        $classes = [
41
            $em->getClassMetadata(CmsAddress::class),
42
            $em->getClassMetadata(CmsArticle::class),
43
            $em->getClassMetadata(CmsComment::class),
44
            $em->getClassMetadata(CmsEmployee::class),
45
            $em->getClassMetadata(CmsGroup::class),
46
            $em->getClassMetadata(CmsPhonenumber::class),
47
            $em->getClassMetadata(CmsUser::class),
48
        ];
49
50
        $schema = $schemaTool->getSchemaFromMetadata($classes);
51
52
        self::assertTrue($schema->hasTable('cms_users'), 'Table cms_users should exist.');
53
        self::assertTrue($schema->getTable('cms_users')->columnsAreIndexed(['username']), 'username column should be indexed.');
54
    }
55
56
    public function testAnnotationOptionsAttribute() : void
57
    {
58
        $em         = $this->getTestEntityManager();
59
        $schemaTool = new SchemaTool($em);
60
61
        $classes = [
62
            $em->getClassMetadata(TestEntityWithAnnotationOptionsAttribute::class),
63
        ];
64
65
        $schema = $schemaTool->getSchemaFromMetadata($classes);
66
67
        $expected = ['foo' => 'bar', 'baz' => ['key' => 'val']];
68
        $table    = $schema->getTable('TestEntityWithAnnotationOptionsAttribute');
69
70
        self::assertEquals(
71
            array_merge($expected, ['create_options' => []]),
72
            $table->getOptions(),
73
            'options annotation are passed to the tables options'
74
        );
75
76
        self::assertEquals(
77
            $expected,
78
            $table->getColumn('test')->getCustomSchemaOptions(),
79
            'options annotation are passed to the columns customSchemaOptions'
80
        );
81
    }
82
83
    /**
84
     * @group DDC-200
85
     */
86
    public function testPassColumnDefinitionToJoinColumn() : void
87
    {
88
        $customColumnDef = 'MEDIUMINT(6) UNSIGNED NOT NULL';
89
90
        $em         = $this->getTestEntityManager();
91
        $schemaTool = new SchemaTool($em);
92
93
        $avatar     = $em->getClassMetadata(ForumAvatar::class);
94
        $idProperty = $avatar->getProperty('id');
0 ignored issues
show
Bug introduced by
The method getProperty() does not exist on Doctrine\Persistence\Mapping\ClassMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

94
        /** @scrutinizer ignore-call */ 
95
        $idProperty = $avatar->getProperty('id');

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...
95
96
        $idProperty->setColumnDefinition($customColumnDef);
97
98
        $user    = $em->getClassMetadata(ForumUser::class);
99
        $classes = [$avatar, $user];
100
        $schema  = $schemaTool->getSchemaFromMetadata($classes);
101
102
        self::assertTrue($schema->hasTable('forum_users'));
103
104
        $table = $schema->getTable('forum_users');
105
106
        self::assertTrue($table->hasColumn('avatar_id'));
107
        self::assertEquals($customColumnDef, $table->getColumn('avatar_id')->getColumnDefinition());
108
    }
109
110
    /**
111
     * @group 6830
112
     */
113
    public function testPassColumnOptionsToJoinColumn() : void
114
    {
115
        $em       = $this->getTestEntityManager();
116
        $category = $em->getClassMetadata(GH6830Category::class);
117
        $board    = $em->getClassMetadata(GH6830Board::class);
118
119
        $schemaTool = new SchemaTool($em);
120
        $schema     = $schemaTool->getSchemaFromMetadata([$category, $board]);
121
122
        self::assertTrue($schema->hasTable('GH6830Category'));
123
        self::assertTrue($schema->hasTable('GH6830Board'));
124
125
        $tableCategory = $schema->getTable('GH6830Category');
126
        $tableBoard    = $schema->getTable('GH6830Board');
127
128
        self::assertTrue($tableBoard->hasColumn('category_id'));
129
130
        self::assertSame(
131
            $tableCategory->getColumn('id')->getFixed(),
132
            $tableBoard->getColumn('category_id')->getFixed(),
133
            'Foreign key/join column should have the same value of option `fixed` as the referenced column'
134
        );
135
136
        self::assertEquals(
137
            $tableCategory->getColumn('id')->getCustomSchemaOptions(),
138
            $tableBoard->getColumn('category_id')->getCustomSchemaOptions(),
139
            'Foreign key/join column should have the same custom options as the referenced column'
140
        );
141
142
        self::assertEquals(
143
            ['collation' => 'latin1_bin', 'foo' => 'bar'],
144
            $tableBoard->getColumn('category_id')->getCustomSchemaOptions()
145
        );
146
    }
147
148
    /**
149
     * @group DDC-283
150
     */
151
    public function testPostGenerateEvents() : void
152
    {
153
        $listener = new GenerateSchemaEventListener();
154
155
        $em = $this->getTestEntityManager();
156
        $em->getEventManager()->addEventListener(
157
            [ToolEvents::postGenerateSchemaTable, ToolEvents::postGenerateSchema],
158
            $listener
159
        );
160
        $schemaTool = new SchemaTool($em);
161
162
        $classes = [
163
            $em->getClassMetadata(CmsAddress::class),
164
            $em->getClassMetadata(CmsArticle::class),
165
            $em->getClassMetadata(CmsComment::class),
166
            $em->getClassMetadata(CmsEmployee::class),
167
            $em->getClassMetadata(CmsGroup::class),
168
            $em->getClassMetadata(CmsPhonenumber::class),
169
            $em->getClassMetadata(CmsUser::class),
170
        ];
171
172
        $schema = $schemaTool->getSchemaFromMetadata($classes);
0 ignored issues
show
Unused Code introduced by
The assignment to $schema is dead and can be removed.
Loading history...
173
174
        self::assertEquals(count($classes), $listener->tableCalls);
175
        self::assertTrue($listener->schemaCalled);
176
    }
177
178
    public function testNullDefaultNotAddedToCustomSchemaOptions() : void
179
    {
180
        $em         = $this->getTestEntityManager();
181
        $schemaTool = new SchemaTool($em);
182
183
        $customSchemaOptions = $schemaTool->getSchemaFromMetadata([$em->getClassMetadata(NullDefaultColumn::class)])
184
            ->getTable('NullDefaultColumn')
185
            ->getColumn('nullDefault')
186
            ->getCustomSchemaOptions();
187
188
        self::assertSame([], $customSchemaOptions);
189
    }
190
191
    /**
192
     * @group DDC-3671
193
     */
194
    public function testSchemaHasProperIndexesFromUniqueConstraintAnnotation() : void
195
    {
196
        $em         = $this->getTestEntityManager();
197
        $schemaTool = new SchemaTool($em);
198
        $classes    = [
199
            $em->getClassMetadata(UniqueConstraintAnnotationModel::class),
200
        ];
201
202
        $schema = $schemaTool->getSchemaFromMetadata($classes);
203
204
        self::assertTrue($schema->hasTable('unique_constraint_annotation_table'));
205
        $table = $schema->getTable('unique_constraint_annotation_table');
206
207
        self::assertCount(1, $table->getIndexes());
208
        self::assertCount(1, $table->getUniqueConstraints());
209
        self::assertTrue($table->hasIndex('primary'));
210
        self::assertTrue($table->hasUniqueConstraint('uniq_hash'));
211
    }
212
213
    public function testRemoveUniqueIndexOverruledByPrimaryKey() : void
214
    {
215
        $em         = $this->getTestEntityManager();
216
        $schemaTool = new SchemaTool($em);
217
        $classes    = [
218
            $em->getClassMetadata(FirstEntity::class),
219
            $em->getClassMetadata(SecondEntity::class),
220
        ];
221
222
        $schema = $schemaTool->getSchemaFromMetadata($classes);
223
224
        self::assertTrue($schema->hasTable('first_entity'), 'Table first_entity should exist.');
225
226
        $indexes = $schema->getTable('first_entity')->getIndexes();
227
228
        self::assertCount(1, $indexes, 'there should be only one index');
229
        self::assertTrue(current($indexes)->isPrimary(), 'index should be primary');
230
    }
231
232
    public function testSetDiscriminatorColumnWithoutLength() : void
233
    {
234
        $em         = $this->getTestEntityManager();
235
        $schemaTool = new SchemaTool($em);
236
        $metadata   = $em->getClassMetadata(FirstEntity::class);
237
238
        $metadata->setInheritanceType(InheritanceType::SINGLE_TABLE);
0 ignored issues
show
Bug introduced by
The method setInheritanceType() does not exist on Doctrine\Persistence\Mapping\ClassMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

238
        $metadata->/** @scrutinizer ignore-call */ 
239
                   setInheritanceType(InheritanceType::SINGLE_TABLE);

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...
239
240
        $discriminatorColumn = new DiscriminatorColumnMetadata();
241
242
        $discriminatorColumn->setColumnName('discriminator');
243
        $discriminatorColumn->setType(Type::getType('string'));
244
245
        $metadata->setDiscriminatorColumn($discriminatorColumn);
0 ignored issues
show
Bug introduced by
The method setDiscriminatorColumn() does not exist on Doctrine\Persistence\Mapping\ClassMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

245
        $metadata->/** @scrutinizer ignore-call */ 
246
                   setDiscriminatorColumn($discriminatorColumn);

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...
246
247
        $schema = $schemaTool->getSchemaFromMetadata([$metadata]);
248
249
        self::assertTrue($schema->hasTable('first_entity'));
250
        $table = $schema->getTable('first_entity');
251
252
        self::assertTrue($table->hasColumn('discriminator'));
253
        $column = $table->getColumn('discriminator');
254
255
        self::assertEquals(255, $column->getLength());
256
    }
257
258
    public function testDerivedCompositeKey() : void
259
    {
260
        self::markTestIncomplete(
261
            '@guilhermeblanco, in #6767 this test was added and I got confused while rebasing SchemaTool... '
262
            . 'so this one does not work atm'
263
        );
264
265
        $em         = $this->getTestEntityManager();
266
        $schemaTool = new SchemaTool($em);
267
268
        $schema = $schemaTool->getSchemaFromMetadata(
269
            [
270
                $em->getClassMetadata(JoinedDerivedIdentityClass::class),
271
                $em->getClassMetadata(JoinedDerivedRootClass::class),
272
                $em->getClassMetadata(JoinedDerivedChildClass::class),
273
            ]
274
        );
275
276
        self::assertTrue($schema->hasTable('joined_derived_identity'));
277
        self::assertTrue($schema->hasTable('joined_derived_root'));
278
        self::assertTrue($schema->hasTable('joined_derived_child'));
279
280
        $rootTable = $schema->getTable('joined_derived_root');
281
        self::assertNotNull($rootTable->getPrimaryKey());
282
        self::assertSame(['keyPart1_id', 'keyPart2'], $rootTable->getPrimaryKey()->getColumns());
283
284
        $childTable = $schema->getTable('joined_derived_child');
285
        self::assertNotNull($childTable->getPrimaryKey());
286
        self::assertSame(['keyPart1_id', 'keyPart2'], $childTable->getPrimaryKey()->getColumns());
287
288
        $childTableForeignKeys = $childTable->getForeignKeys();
289
290
        self::assertCount(2, $childTableForeignKeys);
291
292
        $expectedColumns = [
293
            'joined_derived_identity' => [['keyPart1_id'], ['id']],
294
            'joined_derived_root'     => [['keyPart1_id', 'keyPart2'], ['keyPart1_id', 'keyPart2']],
295
        ];
296
297
        foreach ($childTableForeignKeys as $foreignKey) {
298
            self::assertArrayHasKey($foreignKey->getForeignTableName(), $expectedColumns);
299
300
            [$localColumns, $foreignColumns] = $expectedColumns[$foreignKey->getForeignTableName()];
301
302
            self::assertSame($localColumns, $foreignKey->getLocalColumns());
303
            self::assertSame($foreignColumns, $foreignKey->getForeignColumns());
304
        }
305
    }
306
}
307
308
/**
309
 * @ORM\Entity
310
 * @ORM\Table(options={"foo": "bar", "baz": {"key": "val"}})
311
 */
312
class TestEntityWithAnnotationOptionsAttribute
313
{
314
    /** @ORM\Id @ORM\Column */
315
    private $id;
0 ignored issues
show
introduced by
The private property $id is not used, and could be removed.
Loading history...
316
317
    /** @ORM\Column(type="string", options={"foo": "bar", "baz": {"key": "val"}}) */
318
    private $test;
0 ignored issues
show
introduced by
The private property $test is not used, and could be removed.
Loading history...
319
}
320
321
class GenerateSchemaEventListener
322
{
323
    public $tableCalls   = 0;
324
    public $schemaCalled = false;
325
326
    public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $eventArgs)
0 ignored issues
show
Unused Code introduced by
The parameter $eventArgs is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

326
    public function postGenerateSchemaTable(/** @scrutinizer ignore-unused */ GenerateSchemaTableEventArgs $eventArgs)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
327
    {
328
        $this->tableCalls++;
329
    }
330
331
    public function postGenerateSchema(GenerateSchemaEventArgs $eventArgs)
0 ignored issues
show
Unused Code introduced by
The parameter $eventArgs is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

331
    public function postGenerateSchema(/** @scrutinizer ignore-unused */ GenerateSchemaEventArgs $eventArgs)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
332
    {
333
        $this->schemaCalled = true;
334
    }
335
}
336
337
/**
338
 * @ORM\Entity
339
 * @ORM\Table(
340
 *     name="unique_constraint_annotation_table",
341
 *     uniqueConstraints={
342
 *         @ORM\UniqueConstraint(name="uniq_hash", columns={"hash"})
343
 *     }
344
 * )
345
 */
346
class UniqueConstraintAnnotationModel
347
{
348
    /** @ORM\Id @ORM\Column */
349
    private $id;
350
351
    /** @ORM\Column(name="hash", type="string", length=8, nullable=false, unique=true) */
352
    private $hash;
0 ignored issues
show
introduced by
The private property $hash is not used, and could be removed.
Loading history...
353
}
354
355
/**
356
 * @ORM\Entity
357
 * @ORM\Table(name="first_entity")
358
 */
359
class FirstEntity
360
{
361
    /**
362
     * @ORM\Id
363
     * @ORM\Column(name="id")
364
     */
365
    public $id;
366
367
    /**
368
     * @ORM\OneToOne(targetEntity=SecondEntity::class)
369
     * @ORM\JoinColumn(name="id", referencedColumnName="first_entity_id")
370
     */
371
    public $secondEntity;
372
373
    /** @ORM\Column(name="name") */
374
    public $name;
375
}
376
377
/**
378
 * @ORM\Entity
379
 * @ORM\Table(name="second_entity")
380
 */
381
class SecondEntity
382
{
383
    /**
384
     * @ORM\Id
385
     * @ORM\Column(name="first_entity_id")
386
     */
387
    public $fist_entity_id;
388
389
    /** @ORM\Column(name="name") */
390
    public $name;
391
}
392
393
/**
394
 * @ORM\Entity
395
 */
396
class GH6830Board
397
{
398
    /**
399
     * @ORM\Id
400
     * @ORM\Column(type="integer")
401
     */
402
    public $id;
403
404
    /**
405
     * @ORM\ManyToOne(targetEntity=GH6830Category::class, inversedBy="boards")
406
     * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
407
     */
408
    public $category;
409
}
410
411
/**
412
 * @ORM\Entity
413
 */
414
class GH6830Category
415
{
416
    /**
417
     * @ORM\Id
418
     * @ORM\Column(type="string", length=8, options={"fixed":true, "collation":"latin1_bin", "foo":"bar"})
419
     *
420
     * @var string
421
     */
422
    public $id;
423
424
    /** @ORM\OneToMany(targetEntity=GH6830Board::class, mappedBy="category") */
425
    public $boards;
426
}
427