88use Doctrine \DBAL \Platforms \AbstractPlatform ;
99use Doctrine \DBAL \Schema \AbstractAsset ;
1010use Doctrine \DBAL \Schema \AbstractSchemaManager ;
11+ use Doctrine \DBAL \Schema \ColumnEditor ;
1112use Doctrine \DBAL \Schema \ComparatorConfig ;
1213use Doctrine \DBAL \Schema \DefaultExpression ;
1314use Doctrine \DBAL \Schema \DefaultExpression \CurrentDate ;
2122use Doctrine \DBAL \Schema \NamedObject ;
2223use Doctrine \DBAL \Schema \PrimaryKeyConstraint ;
2324use Doctrine \DBAL \Schema \Schema ;
25+ use Doctrine \DBAL \Schema \SchemaConfig ;
2426use Doctrine \DBAL \Schema \Table ;
2527use Doctrine \DBAL \Types \Types ;
2628use Doctrine \Deprecations \Deprecation ;
@@ -204,11 +206,36 @@ public function getSchemaFromMetadata(array $classes): Schema
204206 continue ;
205207 }
206208
207- $ table = $ schema ->createTable ($ this ->quoteStrategy ->getTableName ($ class , $ this ->platform ));
209+ $ tableName = $ this ->quoteStrategy ->getTableName ($ class , $ this ->platform );
210+
211+ // @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
212+ if (method_exists (Schema::class, 'edit ' )) {
213+ $ table = new Table (
214+ name: $ tableName ,
215+ configuration: $ metadataSchemaConfig ->toTableConfiguration (),
216+ );
217+ // Add default table options (charset, collation, engine, etc.)
218+ foreach ($ metadataSchemaConfig ->getDefaultTableOptions () as $ option => $ value ) {
219+ $ table ->addOption ($ option , $ value );
220+ }
221+ } else {
222+ $ table = $ schema ->createTable ($ tableName );
223+ }
208224
209225 if ($ class ->isInheritanceTypeSingleTable ()) {
226+ // For new schema API: collect join tables to add after this entity table
227+ $ joinTablesToAdd = [];
228+
210229 $ this ->gatherColumns ($ class , $ table );
211- $ this ->gatherRelationsSql ($ class , $ table , $ schema , $ addedFks , $ blacklistedFks );
230+ $ this ->gatherRelationsSql (
231+ $ class ,
232+ $ table ,
233+ $ schema ,
234+ $ addedFks ,
235+ $ blacklistedFks ,
236+ $ metadataSchemaConfig ,
237+ $ joinTablesToAdd ,
238+ );
212239
213240 // Add the discriminator column
214241 $ this ->addDiscriminatorColumnDefinition ($ class , $ table );
@@ -222,18 +249,37 @@ public function getSchemaFromMetadata(array $classes): Schema
222249 foreach ($ class ->subClasses as $ subClassName ) {
223250 $ subClass = $ this ->em ->getClassMetadata ($ subClassName );
224251 $ this ->gatherColumns ($ subClass , $ table );
225- $ this ->gatherRelationsSql ($ subClass , $ table , $ schema , $ addedFks , $ blacklistedFks );
252+ $ this ->gatherRelationsSql (
253+ $ subClass ,
254+ $ table ,
255+ $ schema ,
256+ $ addedFks ,
257+ $ blacklistedFks ,
258+ $ metadataSchemaConfig ,
259+ $ joinTablesToAdd ,
260+ );
226261 $ processedClasses [$ subClassName ] = true ;
227262 }
228263 } elseif ($ class ->isInheritanceTypeJoined ()) {
264+ // For new schema API: collect join tables to add after this entity table
265+ $ joinTablesToAdd = [];
266+
229267 // Add all non-inherited fields as columns
230268 foreach ($ class ->fieldMappings as $ fieldName => $ mapping ) {
231269 if (! isset ($ mapping ->inherited )) {
232270 $ this ->gatherColumn ($ class , $ mapping , $ table );
233271 }
234272 }
235273
236- $ this ->gatherRelationsSql ($ class , $ table , $ schema , $ addedFks , $ blacklistedFks );
274+ $ this ->gatherRelationsSql (
275+ $ class ,
276+ $ table ,
277+ $ schema ,
278+ $ addedFks ,
279+ $ blacklistedFks ,
280+ $ metadataSchemaConfig ,
281+ $ joinTablesToAdd ,
282+ );
237283
238284 // Add the discriminator column only to the root table
239285 if ($ class ->name === $ class ->rootEntityName ) {
@@ -253,7 +299,17 @@ public function getSchemaFromMetadata(array $classes): Schema
253299 $ this ->platform ,
254300 );
255301 // TODO: This seems rather hackish, can we optimize it?
256- $ table ->getColumn ($ columnName )->setAutoincrement (false );
302+ // @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API for version detection)
303+ if (method_exists (Schema::class, 'edit ' )) {
304+ // New API: modify column using table editor (creates new table object)
305+ // This is safe because we'll add the table to schema later after all modifications
306+ $ table = $ table ->edit ()->modifyColumnByUnquotedName (
307+ $ columnName ,
308+ static fn (ColumnEditor $ column ) => $ column ->setAutoincrement (false ),
309+ )->create ();
310+ } else {
311+ $ table ->getColumn ($ columnName )->setAutoincrement (false );
312+ }
257313
258314 $ pkColumns [] = $ columnName ;
259315 $ inheritedKeyColumns [] = $ columnName ;
@@ -305,8 +361,19 @@ public function getSchemaFromMetadata(array $classes): Schema
305361 }
306362 }
307363 } else {
364+ // For new schema API: collect join tables to add after this entity table
365+ $ joinTablesToAdd = [];
366+
308367 $ this ->gatherColumns ($ class , $ table );
309- $ this ->gatherRelationsSql ($ class , $ table , $ schema , $ addedFks , $ blacklistedFks );
368+ $ this ->gatherRelationsSql (
369+ $ class ,
370+ $ table ,
371+ $ schema ,
372+ $ addedFks ,
373+ $ blacklistedFks ,
374+ $ metadataSchemaConfig ,
375+ $ joinTablesToAdd ,
376+ );
310377 }
311378
312379 $ pkColumns = [];
@@ -377,6 +444,22 @@ public function getSchemaFromMetadata(array $classes): Schema
377444
378445 $ processedClasses [$ class ->name ] = true ;
379446
447+ // Add the fully populated table to the schema
448+ // @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
449+ if (method_exists (Schema::class, 'edit ' )) {
450+ // @phpstan-ignore method.notFound (Using unreleased Schema::edit() API)
451+ $ schemaEditor = $ schema ->edit ();
452+ $ schemaEditor ->addTable ($ table );
453+
454+ // Add any join tables collected during relation processing
455+ // This ensures join tables appear right after their owning entity table
456+ foreach ($ joinTablesToAdd as $ joinTable ) {
457+ $ schemaEditor ->addTable ($ joinTable );
458+ }
459+
460+ $ schema = $ schemaEditor ->create ();
461+ }
462+
380463 if ($ class ->isIdGeneratorSequence () && $ class ->name === $ class ->rootEntityName ) {
381464 $ seqDef = $ class ->sequenceGeneratorDefinition ;
382465 $ quotedName = $ this ->quoteStrategy ->getSequenceName ($ seqDef , $ class , $ this ->platform );
@@ -405,12 +488,12 @@ public function getSchemaFromMetadata(array $classes): Schema
405488 $ newTable = $ tableEventArgs ->getClassTable ();
406489
407490 // @phpstan-ignore method.notFound (Using unreleased Schema::edit() API)
408- $ schemaEditor = $ schema ->edit ();
409- $ schemaEditor ->dropTable ($ originalTableName );
410- $ schemaEditor ->addTable ($ newTable );
491+ $ schema = $ schema ->edit ()
492+ ->dropTable ($ originalTableName )
493+ ->addTable ($ newTable )
494+ ->create ();
411495
412- $ schema = $ schemaEditor ->create ();
413- $ table = $ newTable ;
496+ $ table = $ newTable ;
414497 }
415498 }
416499 }
@@ -614,15 +697,18 @@ private function gatherColumn(
614697 * fkOptions: array{onDelete?: string, deferrable?: bool, deferred?: bool}
615698 * }> $addedFks
616699 * @phpstan-param array<string, bool> $blacklistedFks
700+ * @phpstan-param list<Table> $joinTablesToAdd
617701 *
618702 * @throws NotSupported
619703 */
620704 private function gatherRelationsSql (
621705 ClassMetadata $ class ,
622706 Table $ table ,
623- Schema $ schema ,
707+ Schema & $ schema ,
624708 array &$ addedFks ,
625709 array &$ blacklistedFks ,
710+ SchemaConfig $ schemaConfig ,
711+ array &$ joinTablesToAdd ,
626712 ): void {
627713 foreach ($ class ->associationMappings as $ id => $ mapping ) {
628714 if (isset ($ mapping ->inherited ) && ! in_array ($ id , $ class ->identifier , true )) {
@@ -647,12 +733,25 @@ private function gatherRelationsSql(
647733 // create join table
648734 $ joinTable = $ mapping ->joinTable ;
649735
650- $ theJoinTable = $ schema ->createTable (
651- $ this ->quoteStrategy ->getJoinTableName ($ mapping , $ foreignClass , $ this ->platform ),
652- );
736+ $ tableName = $ this ->quoteStrategy ->getJoinTableName ($ mapping , $ foreignClass , $ this ->platform );
653737
654- foreach ($ joinTable ->options as $ key => $ val ) {
655- $ theJoinTable ->addOption ($ key , $ val );
738+ // Create the join table object
739+ // @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
740+ if (method_exists (Schema::class, 'edit ' )) {
741+ $ theJoinTable = new Table (
742+ name: $ tableName ,
743+ options: $ joinTable ->options ,
744+ configuration: $ schemaConfig ->toTableConfiguration (),
745+ );
746+ // Add default table options (charset, collation, engine, etc.)
747+ foreach ($ schemaConfig ->getDefaultTableOptions () as $ option => $ value ) {
748+ $ theJoinTable ->addOption ($ option , $ value );
749+ }
750+ } else {
751+ $ theJoinTable = $ schema ->createTable ($ tableName );
752+ foreach ($ joinTable ->options as $ key => $ val ) {
753+ $ theJoinTable ->addOption ($ key , $ val );
754+ }
656755 }
657756
658757 $ primaryKeyColumns = [];
@@ -680,6 +779,11 @@ private function gatherRelationsSql(
680779 );
681780
682781 self ::addPrimaryKeyConstraint ($ theJoinTable , $ primaryKeyColumns );
782+
783+ // @phpstan-ignore function.impossibleType (Using unreleased Schema::edit() API)
784+ if (method_exists (Schema::class, 'edit ' )) {
785+ $ joinTablesToAdd [] = $ theJoinTable ;
786+ }
683787 }
684788 }
685789 }
0 commit comments