Completed
Push — master ( 23a060...e747f7 )
by Luís
45s queued 37s
created

testSchemaHasProperIndexesFromUniqueConstraintAnnotation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 11
nc 1
nop 0
dl 0
loc 17
rs 9.9
c 0
b 0
f 0
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 count;
30
use function current;
31
32
class SchemaToolTest extends OrmTestCase
33
{
34
    public function testAddUniqueIndexForUniqueFieldAnnotation() : void
35
    {
36
        $em         = $this->getTestEntityManager();
37
        $schemaTool = new SchemaTool($em);
38
39
        $classes = [
40
            $em->getClassMetadata(CmsAddress::class),
41
            $em->getClassMetadata(CmsArticle::class),
42
            $em->getClassMetadata(CmsComment::class),
43
            $em->getClassMetadata(CmsEmployee::class),
44
            $em->getClassMetadata(CmsGroup::class),
45
            $em->getClassMetadata(CmsPhonenumber::class),
46
            $em->getClassMetadata(CmsUser::class),
47
        ];
48
49
        $schema = $schemaTool->getSchemaFromMetadata($classes);
50
51
        self::assertTrue($schema->hasTable('cms_users'), 'Table cms_users should exist.');
52
        self::assertTrue($schema->getTable('cms_users')->columnsAreIndexed(['username']), 'username column should be indexed.');
53
    }
54
55
    public function testAnnotationOptionsAttribute() : void
56
    {
57
        $em         = $this->getTestEntityManager();
58
        $schemaTool = new SchemaTool($em);
59
60
        $classes = [
61
            $em->getClassMetadata(TestEntityWithAnnotationOptionsAttribute::class),
62
        ];
63
64
        $schema = $schemaTool->getSchemaFromMetadata($classes);
65
66
        $expected = ['foo' => 'bar', 'baz' => ['key' => 'val']];
67
68
        self::assertEquals($expected, $schema->getTable('TestEntityWithAnnotationOptionsAttribute')->getOptions(), 'options annotation are passed to the tables options');
69
        self::assertEquals($expected, $schema->getTable('TestEntityWithAnnotationOptionsAttribute')->getColumn('test')->getCustomSchemaOptions(), 'options annotation are passed to the columns customSchemaOptions');
70
    }
71
72
    /**
73
     * @group DDC-200
74
     */
75
    public function testPassColumnDefinitionToJoinColumn() : void
76
    {
77
        $customColumnDef = 'MEDIUMINT(6) UNSIGNED NOT NULL';
78
79
        $em         = $this->getTestEntityManager();
80
        $schemaTool = new SchemaTool($em);
81
82
        $avatar     = $em->getClassMetadata(ForumAvatar::class);
83
        $idProperty = $avatar->getProperty('id');
0 ignored issues
show
Bug introduced by
The method getProperty() does not exist on Doctrine\Common\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

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

227
        $metadata->/** @scrutinizer ignore-call */ 
228
                   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...
228
229
        $discriminatorColumn = new DiscriminatorColumnMetadata();
230
231
        $discriminatorColumn->setColumnName('discriminator');
232
        $discriminatorColumn->setType(Type::getType('string'));
233
234
        $metadata->setDiscriminatorColumn($discriminatorColumn);
0 ignored issues
show
Bug introduced by
The method setDiscriminatorColumn() does not exist on Doctrine\Common\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

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

315
    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...
316
    {
317
        $this->tableCalls++;
318
    }
319
320
    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

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