Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions phpstan-dbal3.neon
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,36 @@ parameters:
-
message: '~inferType.*never returns~'
path: src/Query/ParameterTypeInferer.php

# DBAL 4/5 Schema Editor API compatibility
-
message: '~^Class Doctrine\\DBAL\\Schema\\Table constructor invoked with 7 parameters, 1-6 required\.$~'
path: src/Tools/SchemaTool.php

-
message: '~^Call to an undefined method Doctrine\\DBAL\\Schema\\SchemaConfig::toTableConfiguration\(\)\.$~'
path: src/Tools/SchemaTool.php

-
message: '~^Call to method setAutoincrement\(\) on an unknown class Doctrine\\DBAL\\Schema\\ColumnEditor\.$~'
path: src/Tools/SchemaTool.php

-
message: '~^Parameter \$column of anonymous function has invalid type Doctrine\\DBAL\\Schema\\ColumnEditor\.$~'
path: src/Tools/SchemaTool.php

-
message: '~^Call to an undefined method Doctrine\\DBAL\\Schema\\Table::edit\(\)\.$~'
path: src/Tools/SchemaTool.php

-
message: '~^Call to an undefined method Doctrine\\DBAL\\Schema\\Table::modifyColumnByUnquotedName\(\)\.$~'
path: src/Tools/SchemaTool.php

-
message: '~^Instantiated class Doctrine\\DBAL\\Schema\\ColumnEditor not found\.$~'
path: src/Tools/SchemaTool.php

-
message: '~^Unknown parameter \$configuration in call to Doctrine\\DBAL\\Schema\\Table constructor\.$~'
path: src/Tools/SchemaTool.php
138 changes: 121 additions & 17 deletions src/Tools/SchemaTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractAsset;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\ColumnEditor;
use Doctrine\DBAL\Schema\ComparatorConfig;
use Doctrine\DBAL\Schema\DefaultExpression;
use Doctrine\DBAL\Schema\DefaultExpression\CurrentDate;
Expand All @@ -21,6 +22,7 @@
use Doctrine\DBAL\Schema\NamedObject;
use Doctrine\DBAL\Schema\PrimaryKeyConstraint;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaConfig;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\Types;
use Doctrine\Deprecations\Deprecation;
Expand Down Expand Up @@ -204,11 +206,36 @@ public function getSchemaFromMetadata(array $classes): Schema
continue;
}

$table = $schema->createTable($this->quoteStrategy->getTableName($class, $this->platform));
$tableName = $this->quoteStrategy->getTableName($class, $this->platform);

// @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
if (method_exists(Schema::class, 'edit')) {
$table = new Table(
name: $tableName,
configuration: $metadataSchemaConfig->toTableConfiguration(),
);
// Add default table options (charset, collation, engine, etc.)
foreach ($metadataSchemaConfig->getDefaultTableOptions() as $option => $value) {
$table->addOption($option, $value);
}
} else {
$table = $schema->createTable($tableName);
}

if ($class->isInheritanceTypeSingleTable()) {
// For new schema API: collect join tables to add after this entity table
$joinTablesToAdd = [];

$this->gatherColumns($class, $table);
$this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks);
$this->gatherRelationsSql(
$class,
$table,
$schema,
$addedFks,
$blacklistedFks,
$metadataSchemaConfig,
$joinTablesToAdd,
);

// Add the discriminator column
$this->addDiscriminatorColumnDefinition($class, $table);
Expand All @@ -222,18 +249,37 @@ public function getSchemaFromMetadata(array $classes): Schema
foreach ($class->subClasses as $subClassName) {
$subClass = $this->em->getClassMetadata($subClassName);
$this->gatherColumns($subClass, $table);
$this->gatherRelationsSql($subClass, $table, $schema, $addedFks, $blacklistedFks);
$this->gatherRelationsSql(
$subClass,
$table,
$schema,
$addedFks,
$blacklistedFks,
$metadataSchemaConfig,
$joinTablesToAdd,
);
$processedClasses[$subClassName] = true;
}
} elseif ($class->isInheritanceTypeJoined()) {
// For new schema API: collect join tables to add after this entity table
$joinTablesToAdd = [];

// Add all non-inherited fields as columns
foreach ($class->fieldMappings as $fieldName => $mapping) {
if (! isset($mapping->inherited)) {
$this->gatherColumn($class, $mapping, $table);
}
}

$this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks);
$this->gatherRelationsSql(
$class,
$table,
$schema,
$addedFks,
$blacklistedFks,
$metadataSchemaConfig,
$joinTablesToAdd,
);

// Add the discriminator column only to the root table
if ($class->name === $class->rootEntityName) {
Expand All @@ -253,7 +299,17 @@ public function getSchemaFromMetadata(array $classes): Schema
$this->platform,
);
// TODO: This seems rather hackish, can we optimize it?
$table->getColumn($columnName)->setAutoincrement(false);
// @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API for version detection)
if (method_exists(Schema::class, 'edit')) {
// New API: modify column using table editor (creates new table object)
// This is safe because we'll add the table to schema later after all modifications
$table = $table->edit()->modifyColumnByUnquotedName(
$columnName,
static fn (ColumnEditor $column) => $column->setAutoincrement(false),
)->create();
} else {
$table->getColumn($columnName)->setAutoincrement(false);
}

$pkColumns[] = $columnName;
$inheritedKeyColumns[] = $columnName;
Expand Down Expand Up @@ -305,8 +361,19 @@ public function getSchemaFromMetadata(array $classes): Schema
}
}
} else {
// For new schema API: collect join tables to add after this entity table
$joinTablesToAdd = [];

$this->gatherColumns($class, $table);
$this->gatherRelationsSql($class, $table, $schema, $addedFks, $blacklistedFks);
$this->gatherRelationsSql(
$class,
$table,
$schema,
$addedFks,
$blacklistedFks,
$metadataSchemaConfig,
$joinTablesToAdd,
);
}

$pkColumns = [];
Expand Down Expand Up @@ -377,6 +444,22 @@ public function getSchemaFromMetadata(array $classes): Schema

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

// Add the fully populated table to the schema
// @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
if (method_exists(Schema::class, 'edit')) {
// @phpstan-ignore method.notFound (Using unreleased Schema::edit() API)
$schemaEditor = $schema->edit();
$schemaEditor->addTable($table);

// Add any join tables collected during relation processing
// This ensures join tables appear right after their owning entity table
foreach ($joinTablesToAdd as $joinTable) {
$schemaEditor->addTable($joinTable);
}

$schema = $schemaEditor->create();
}

if ($class->isIdGeneratorSequence() && $class->name === $class->rootEntityName) {
$seqDef = $class->sequenceGeneratorDefinition;
$quotedName = $this->quoteStrategy->getSequenceName($seqDef, $class, $this->platform);
Expand Down Expand Up @@ -405,12 +488,12 @@ public function getSchemaFromMetadata(array $classes): Schema
$newTable = $tableEventArgs->getClassTable();

// @phpstan-ignore method.notFound (Using unreleased Schema::edit() API)
$schemaEditor = $schema->edit();
$schemaEditor->dropTable($originalTableName);
$schemaEditor->addTable($newTable);
$schema = $schema->edit()
->dropTable($originalTableName)
->addTable($newTable)
->create();

$schema = $schemaEditor->create();
$table = $newTable;
$table = $newTable;
}
}
}
Expand Down Expand Up @@ -614,15 +697,18 @@ private function gatherColumn(
* fkOptions: array{onDelete?: string, deferrable?: bool, deferred?: bool}
* }> $addedFks
* @phpstan-param array<string, bool> $blacklistedFks
* @phpstan-param list<Table> $joinTablesToAdd
*
* @throws NotSupported
*/
private function gatherRelationsSql(
ClassMetadata $class,
Table $table,
Schema $schema,
Schema &$schema,
array &$addedFks,
array &$blacklistedFks,
SchemaConfig $schemaConfig,
array &$joinTablesToAdd,
): void {
foreach ($class->associationMappings as $id => $mapping) {
if (isset($mapping->inherited) && ! in_array($id, $class->identifier, true)) {
Expand All @@ -647,12 +733,25 @@ private function gatherRelationsSql(
// create join table
$joinTable = $mapping->joinTable;

$theJoinTable = $schema->createTable(
$this->quoteStrategy->getJoinTableName($mapping, $foreignClass, $this->platform),
);
$tableName = $this->quoteStrategy->getJoinTableName($mapping, $foreignClass, $this->platform);

foreach ($joinTable->options as $key => $val) {
$theJoinTable->addOption($key, $val);
// Create the join table object
// @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
if (method_exists(Schema::class, 'edit')) {
$theJoinTable = new Table(
name: $tableName,
options: $joinTable->options,
configuration: $schemaConfig->toTableConfiguration(),
);
// Add default table options (charset, collation, engine, etc.)
foreach ($schemaConfig->getDefaultTableOptions() as $option => $value) {
$theJoinTable->addOption($option, $value);
}
} else {
$theJoinTable = $schema->createTable($tableName);
foreach ($joinTable->options as $key => $val) {
$theJoinTable->addOption($key, $val);
}
}

$primaryKeyColumns = [];
Expand Down Expand Up @@ -680,6 +779,11 @@ private function gatherRelationsSql(
);

self::addPrimaryKeyConstraint($theJoinTable, $primaryKeyColumns);

// @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
if (method_exists(Schema::class, 'edit')) {
$joinTablesToAdd[] = $theJoinTable;
}
}
}
}
Expand Down
Loading