Skip to content

Fix: sort foreign keys in SchemaNormalizer to stabilize bean generation#305

Open
nguyenk wants to merge 1 commit into
masterfrom
fix/sort-foreign-keys-schema-normalizer
Open

Fix: sort foreign keys in SchemaNormalizer to stabilize bean generation#305
nguyenk wants to merge 1 commit into
masterfrom
fix/sort-foreign-keys-schema-normalizer

Conversation

@nguyenk
Copy link
Copy Markdown
Member

@nguyenk nguyenk commented May 13, 2026

When a table has two foreign keys pointing to the same target table, the generated Base bean methods for the reverse relation (one-to-many) were reordered on every tdbm:generate run.

Root cause: SchemaNormalizer::normalizeTable() wrote foreign_keys to the lock file in the order returned by DB introspection (AbstractSchemaManager::introspectSchema()). MySQL/MariaDB does not guarantee a stable ordering of FK constraints, so the lock file FK order could differ between environments or after schema changes. Since tdbm:generate writes the lock file then immediately reads it to generate beans, the bean method order inherited this non-determinism.

The post-processing normalize-tdbm-yaml.php script sorted the lock file after the fact, but beans were already generated by then.

Fix: apply ksort() on foreign_keys after collection in normalizeTable(), consistent with the existing ksort() on tables in normalize(). Guard with isset() for tables that have no foreign keys.

When a table has two foreign keys pointing to the same target table,
the generated Base bean methods for the reverse relation (one-to-many)
were reordered on every `tdbm:generate` run.

Root cause: SchemaNormalizer::normalizeTable() wrote foreign_keys to
the lock file in the order returned by DB introspection
(AbstractSchemaManager::introspectSchema()). MySQL/MariaDB does not
guarantee a stable ordering of FK constraints, so the lock file FK
order could differ between environments or after schema changes. Since
tdbm:generate writes the lock file then immediately reads it to
generate beans, the bean method order inherited this non-determinism.

The post-processing normalize-tdbm-yaml.php script sorted the lock
file after the fact, but beans were already generated by then.

Fix: apply ksort() on foreign_keys after collection in
normalizeTable(), consistent with the existing ksort() on tables in
normalize(). Guard with isset() for tables that have no foreign keys.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes non-deterministic ordering of generated bean reverse-relation methods by ensuring foreign keys are serialized in a stable order during schema normalization (rather than relying on DB introspection ordering, which can vary on MySQL/MariaDB).

Changes:

  • Sort foreign_keys by constraint name (ksort) inside SchemaNormalizer::normalizeTable() (guarded for tables with no FKs).
  • Add a PHPUnit test that verifies foreign keys are normalized in alphabetical order regardless of insertion order.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/SchemaVersionControl/SchemaNormalizer.php Stabilizes schema lock output by sorting foreign key entries per table.
tests/SchemaVersionControl/SchemaNormalizerTest.php Adds regression coverage to ensure FK ordering is deterministic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants