Passed
Pull Request — master (#7725)
by Guilherme
09:09
created

testNullDefaultNotAddedToCustomSchemaOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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

85
        /** @scrutinizer ignore-call */ 
86
        $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...
86
87
        $idProperty->setColumnDefinition($customColumnDef);
88
89
        $user    = $em->getClassMetadata(ForumUser::class);
90
        $classes = [$avatar, $user];
91
        $schema  = $schemaTool->getSchemaFromMetadata($classes);
92
93
        self::assertTrue($schema->hasTable('forum_users'));
94
95
        $table = $schema->getTable('forum_users');
96
97
        self::assertTrue($table->hasColumn('avatar_id'));
98
        self::assertEquals($customColumnDef, $table->getColumn('avatar_id')->getColumnDefinition());
99
    }
100
101
    /**
102
     * @group 6830
103
     */
104
    public function testPassColumnOptionsToJoinColumn() : void
105
    {
106
        $em = $this->getTestEntityManager();
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
107
        $category = $em->getClassMetadata(GH6830Category::class);
108
        $board = $em->getClassMetadata(GH6830Board::class);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
109
110
        $schemaTool = new SchemaTool($em);
111
        $schema = $schemaTool->getSchemaFromMetadata([$category, $board]);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
112
113
        self::assertTrue($schema->hasTable('GH6830Category'));
114
        self::assertTrue($schema->hasTable('GH6830Board'));
115
116
        $tableCategory = $schema->getTable('GH6830Category');
117
        $tableBoard = $schema->getTable('GH6830Board');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

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

229
        $metadata->/** @scrutinizer ignore-call */ 
230
                   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...
230
231
        $discriminatorColumn = new DiscriminatorColumnMetadata();
232
233
        $discriminatorColumn->setColumnName('discriminator');
234
        $discriminatorColumn->setType(Type::getType('string'));
235
236
        $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

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

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

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