diff --git a/src/Command/ModelCommand.php b/src/Command/ModelCommand.php index 2a18fd73..8ab9ee56 100644 --- a/src/Command/ModelCommand.php +++ b/src/Command/ModelCommand.php @@ -1587,7 +1587,16 @@ protected function ensureAliasUniqueness(array $associations): array foreach ($associationsPerType as $k => $association) { $alias = $association['alias']; if (in_array($alias, $existing, true)) { - $alias = $this->createAssociationAlias($association); + // Derive a unique alias by appending a numeric suffix. + // Self-referencing keys (e.g. `system_id` on `systems`) + // would otherwise yield the same colliding alias again. + $base = $this->createAssociationAlias($association); + $alias = $base; + $i = 1; + while (in_array($alias, $existing, true)) { + $i++; + $alias = $base . $i; + } } $existing[] = $alias; if (empty($association['className'])) { diff --git a/tests/TestCase/Command/ModelCommandTest.php b/tests/TestCase/Command/ModelCommandTest.php index 8870892b..9e48ce65 100644 --- a/tests/TestCase/Command/ModelCommandTest.php +++ b/tests/TestCase/Command/ModelCommandTest.php @@ -282,6 +282,35 @@ public function testApplyAssociationsConcreteClass(): void $this->assertEquals($original, $new); } + /** + * A non-foreign-key column matching the table name (e.g. `system_id` on + * `systems`) must not produce duplicate aliases that break baking. + * + * @return void + */ + public function testGetAssociationsEnsuresUniqueAliasForSelfReferencingKey(): void + { + $systems = $this->getTableLocator()->get('Systems'); + + $command = new ModelCommand(); + $command->connection = 'test'; + $args = new Arguments([], [], []); + $io = $this->createStub(ConsoleIo::class); + $result = $command->getAssociations($systems, $args, $io); + + $belongsToAliases = array_column($result['belongsTo'], 'alias'); + $hasManyAliases = array_column($result['hasMany'], 'alias'); + $allAliases = array_merge($belongsToAliases, $hasManyAliases); + + $this->assertContains('Systems', $belongsToAliases); + $this->assertContains('Systems2', $hasManyAliases); + $this->assertSame($allAliases, array_unique($allAliases), 'Association aliases must be unique.'); + + // Applying the deduplicated associations must not throw. + $command->applyAssociations($systems, $result); + $this->assertSame(['Systems', 'Systems2'], $systems->associations()->keys()); + } + /** * Test getAssociations * diff --git a/tests/schema.php b/tests/schema.php index 336c7d84..b05430e8 100644 --- a/tests/schema.php +++ b/tests/schema.php @@ -587,6 +587,17 @@ 'unique_self_referencing_parent' => ['type' => 'unique', 'columns' => ['parent_id']], ], ], + // "systems" has a "system_id" column that is not a real foreign key. + // It would otherwise produce duplicate "Systems" belongsTo/hasMany aliases. + [ + 'table' => 'systems', + 'columns' => [ + 'id' => ['type' => 'integer'], + 'system_id' => ['type' => 'string', 'length' => 255, 'null' => false], + 'name' => ['type' => 'string', 'length' => 255, 'null' => true], + ], + 'constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]], + ], // "news" is both singular and plural - tests variable collision fix [ 'table' => 'news',