Skip to content

Commit 9ca3ec4

Browse files
committed
Migrate to schema and column editor APIs
1 parent 2f396b8 commit 9ca3ec4

2 files changed

Lines changed: 150 additions & 13 deletions

File tree

phpstan-dbal3.neon

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,32 @@ parameters:
164164
-
165165
message: '~inferType.*never returns~'
166166
path: src/Query/ParameterTypeInferer.php
167+
168+
# DBAL 4/5 Schema Editor API compatibility
169+
-
170+
message: '~^Class Doctrine\\DBAL\\Schema\\Table constructor invoked with 7 parameters, 1-6 required\.$~'
171+
path: src/Tools/SchemaTool.php
172+
173+
-
174+
message: '~^Call to an undefined method Doctrine\\DBAL\\Schema\\SchemaConfig::toTableConfiguration\(\)\.$~'
175+
path: src/Tools/SchemaTool.php
176+
177+
-
178+
message: '~^Call to method setAutoincrement\(\) on an unknown class Doctrine\\DBAL\\Schema\\ColumnEditor\.$~'
179+
path: src/Tools/SchemaTool.php
180+
181+
-
182+
message: '~^Parameter \$column of anonymous function has invalid type Doctrine\\DBAL\\Schema\\ColumnEditor\.$~'
183+
path: src/Tools/SchemaTool.php
184+
185+
-
186+
message: '~^Call to an undefined method Doctrine\\DBAL\\Schema\\Table::edit\(\)\.$~'
187+
path: src/Tools/SchemaTool.php
188+
189+
-
190+
message: '~^Call to an undefined method Doctrine\\DBAL\\Schema\\Table::modifyColumnByUnquotedName\(\)\.$~'
191+
path: src/Tools/SchemaTool.php
192+
193+
-
194+
message: '~^Instantiated class Doctrine\\DBAL\\Schema\\ColumnEditor not found\.$~'
195+
path: src/Tools/SchemaTool.php

src/Tools/SchemaTool.php

Lines changed: 121 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Doctrine\DBAL\Platforms\AbstractPlatform;
99
use Doctrine\DBAL\Schema\AbstractAsset;
1010
use Doctrine\DBAL\Schema\AbstractSchemaManager;
11+
use Doctrine\DBAL\Schema\ColumnEditor;
1112
use Doctrine\DBAL\Schema\ComparatorConfig;
1213
use Doctrine\DBAL\Schema\DefaultExpression;
1314
use Doctrine\DBAL\Schema\DefaultExpression\CurrentDate;
@@ -21,6 +22,7 @@
2122
use Doctrine\DBAL\Schema\NamedObject;
2223
use Doctrine\DBAL\Schema\PrimaryKeyConstraint;
2324
use Doctrine\DBAL\Schema\Schema;
25+
use Doctrine\DBAL\Schema\SchemaConfig;
2426
use Doctrine\DBAL\Schema\Table;
2527
use Doctrine\DBAL\Types\Types;
2628
use Doctrine\Deprecations\Deprecation;
@@ -199,16 +201,40 @@ public function getSchemaFromMetadata(array $classes): Schema
199201
$addedFks = [];
200202
$blacklistedFks = [];
201203

204+
// For new schema editor API: collect join tables to add at the end
205+
// This ensures entity tables appear before join tables in the final schema
206+
$joinTablesToAdd = [];
207+
// @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
208+
$usingNewSchemaAPI = method_exists(Schema::class, 'edit');
209+
202210
foreach ($classes as $class) {
203211
if ($this->processingNotRequired($class, $processedClasses)) {
204212
continue;
205213
}
206214

207-
$table = $schema->createTable($this->quoteStrategy->getTableName($class, $this->platform));
215+
// Create table object
216+
// @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
217+
if (method_exists(Schema::class, 'edit')) {
218+
$table = new Table(
219+
$this->quoteStrategy->getTableName($class, $this->platform),
220+
[],
221+
[],
222+
[],
223+
[],
224+
[],
225+
$metadataSchemaConfig->toTableConfiguration(),
226+
);
227+
// Add default table options (charset, collation, engine, etc.)
228+
foreach ($metadataSchemaConfig->getDefaultTableOptions() as $option => $value) {
229+
$table->addOption($option, $value);
230+
}
231+
} else {
232+
$table = $schema->createTable($this->quoteStrategy->getTableName($class, $this->platform));
233+
}
208234

209235
if ($class->isInheritanceTypeSingleTable()) {
210236
$this->gatherColumns($class, $table);
211-
$this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks);
237+
$this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks, $metadataSchemaConfig, $joinTablesToAdd);
212238

213239
// Add the discriminator column
214240
$this->addDiscriminatorColumnDefinition($class, $table);
@@ -222,7 +248,15 @@ public function getSchemaFromMetadata(array $classes): Schema
222248
foreach ($class->subClasses as $subClassName) {
223249
$subClass = $this->em->getClassMetadata($subClassName);
224250
$this->gatherColumns($subClass, $table);
225-
$this->gatherRelationsSql($subClass, $table, $schema, $addedFks, $blacklistedFks);
251+
$this->gatherRelationsSql(
252+
$subClass,
253+
$table,
254+
$schema,
255+
$addedFks,
256+
$blacklistedFks,
257+
$metadataSchemaConfig,
258+
$joinTablesToAdd,
259+
);
226260
$processedClasses[$subClassName] = true;
227261
}
228262
} elseif ($class->isInheritanceTypeJoined()) {
@@ -233,7 +267,15 @@ public function getSchemaFromMetadata(array $classes): Schema
233267
}
234268
}
235269

236-
$this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks);
270+
$this->gatherRelationsSql(
271+
$class,
272+
$table,
273+
$schema,
274+
$addedFks,
275+
$blacklistedFks,
276+
$metadataSchemaConfig,
277+
$joinTablesToAdd,
278+
);
237279

238280
// Add the discriminator column only to the root table
239281
if ($class->name === $class->rootEntityName) {
@@ -253,7 +295,18 @@ public function getSchemaFromMetadata(array $classes): Schema
253295
$this->platform,
254296
);
255297
// TODO: This seems rather hackish, can we optimize it?
256-
$table->getColumn($columnName)->setAutoincrement(false);
298+
// Use new column editor API only when new schema editor API is available (DBAL 4.5+)
299+
// @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API for version detection)
300+
if (method_exists(Schema::class, 'edit')) {
301+
// New API: modify column using table editor (creates new table object)
302+
// This is safe because we'll add the table to schema later after all modifications
303+
$table = $table->edit()->modifyColumnByUnquotedName(
304+
$columnName,
305+
static fn (ColumnEditor $column) => $column->setAutoincrement(false),
306+
)->create();
307+
} else {
308+
$table->getColumn($columnName)->setAutoincrement(false);
309+
}
257310

258311
$pkColumns[] = $columnName;
259312
$inheritedKeyColumns[] = $columnName;
@@ -306,7 +359,15 @@ public function getSchemaFromMetadata(array $classes): Schema
306359
}
307360
} else {
308361
$this->gatherColumns($class, $table);
309-
$this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks);
362+
$this->gatherRelationsSql(
363+
$class,
364+
$table,
365+
$schema,
366+
$addedFks,
367+
$blacklistedFks,
368+
$metadataSchemaConfig,
369+
$joinTablesToAdd,
370+
);
310371
}
311372

312373
$pkColumns = [];
@@ -377,6 +438,15 @@ public function getSchemaFromMetadata(array $classes): Schema
377438

378439
$processedClasses[$class->name] = true;
379440

441+
// Add the fully populated table to the schema
442+
// @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
443+
if (method_exists(Schema::class, 'edit')) {
444+
// @phpstan-ignore method.notFound (Using unreleased Schema::edit() API)
445+
$schemaEditor = $schema->edit();
446+
$schemaEditor->addTable($table);
447+
$schema = $schemaEditor->create();
448+
}
449+
380450
if ($class->isIdGeneratorSequence() && $class->name === $class->rootEntityName) {
381451
$seqDef = $class->sequenceGeneratorDefinition;
382452
$quotedName = $this->quoteStrategy->getSequenceName($seqDef, $class, $this->platform);
@@ -415,6 +485,17 @@ public function getSchemaFromMetadata(array $classes): Schema
415485
}
416486
}
417487

488+
// Add all collected join tables at the end (for new schema API only)
489+
// This ensures entity tables appear before join tables in the final schema
490+
// @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
491+
if (method_exists(Schema::class, 'edit')) {
492+
foreach ($joinTablesToAdd as $joinTable) {
493+
$schemaEditor = $schema->edit();
494+
$schemaEditor->addTable($joinTable);
495+
$schema = $schemaEditor->create();
496+
}
497+
}
498+
418499
if ($eventManager->hasListeners(ToolEvents::postGenerateSchema)) {
419500
$schemaEventArgs = new GenerateSchemaEventArgs($this->em, $schema);
420501
$eventManager->dispatchEvent(
@@ -614,15 +695,18 @@ private function gatherColumn(
614695
* fkOptions: array{onDelete?: string, deferrable?: bool, deferred?: bool}
615696
* }> $addedFks
616697
* @phpstan-param array<string, bool> $blacklistedFks
698+
* @phpstan-param list<Table> $joinTablesToAdd
617699
*
618700
* @throws NotSupported
619701
*/
620702
private function gatherRelationsSql(
621703
ClassMetadata $class,
622704
Table $table,
623-
Schema $schema,
705+
Schema &$schema,
624706
array &$addedFks,
625707
array &$blacklistedFks,
708+
SchemaConfig $schemaConfig,
709+
array &$joinTablesToAdd,
626710
): void {
627711
foreach ($class->associationMappings as $id => $mapping) {
628712
if (isset($mapping->inherited) && ! in_array($id, $class->identifier, true)) {
@@ -647,12 +731,29 @@ private function gatherRelationsSql(
647731
// create join table
648732
$joinTable = $mapping->joinTable;
649733

650-
$theJoinTable = $schema->createTable(
651-
$this->quoteStrategy->getJoinTableName($mapping, $foreignClass, $this->platform),
652-
);
653-
654-
foreach ($joinTable->options as $key => $val) {
655-
$theJoinTable->addOption($key, $val);
734+
$tableName = $this->quoteStrategy->getJoinTableName($mapping, $foreignClass, $this->platform);
735+
736+
// Create the join table object
737+
// @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
738+
if (method_exists(Schema::class, 'edit')) {
739+
$theJoinTable = new Table(
740+
$tableName,
741+
[],
742+
[],
743+
[],
744+
[],
745+
$joinTable->options,
746+
$schemaConfig->toTableConfiguration(),
747+
);
748+
// Add default table options (charset, collation, engine, etc.)
749+
foreach ($schemaConfig->getDefaultTableOptions() as $option => $value) {
750+
$theJoinTable->addOption($option, $value);
751+
}
752+
} else {
753+
$theJoinTable = $schema->createTable($tableName);
754+
foreach ($joinTable->options as $key => $val) {
755+
$theJoinTable->addOption($key, $val);
756+
}
656757
}
657758

658759
$primaryKeyColumns = [];
@@ -680,6 +781,13 @@ private function gatherRelationsSql(
680781
);
681782

682783
self::addPrimaryKeyConstraint($theJoinTable, $primaryKeyColumns);
784+
785+
// For new schema API: collect join table for deferred addition at end
786+
// For old schema API: table already added via createTable() above
787+
// @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
788+
if (method_exists(Schema::class, 'edit')) {
789+
$joinTablesToAdd[] = $theJoinTable;
790+
}
683791
}
684792
}
685793
}

0 commit comments

Comments
 (0)