WIP: rewrite migrations to Doctrine Schema tool + PHPStan enforcement#442
Draft
turegjorup wants to merge 4 commits into
Draft
WIP: rewrite migrations to Doctrine Schema tool + PHPStan enforcement#442turegjorup wants to merge 4 commits into
turegjorup wants to merge 4 commits into
Conversation
Replaces every raw `$this->addSql(...)` call in the consolidated end-of-2.7 migration with the equivalent Schema tool builder API (`$schema->createTable()`, `$table->addColumn()`, `$table->addForeignKeyConstraint()`, `$schema->dropTable()`). The emitted DDL is identical for MariaDB but is now generated through Doctrine's platform abstraction, so the same migration produces platform-native DDL on any database Doctrine supports. Verified end-to-end on MariaDB: - Fresh install: drop → migrate → schema:validate exits 0 - Upgrade from 2.7: 25 latest-2.x applied → swap files → rollup → schema:validate exits 0 - mysqldump diff against the previous raw-SQL version: only column ordering and index ordering changes (the Schema tool emits columns in addColumn() insertion order). No functional differences. - Full PHPUnit suite green (143 tests / 607 assertions). A small `applyTableOptions()` helper at the bottom of the class collapses the engine/charset/collation triplet that would otherwise repeat 29 times. Other Doctrine-supported platforms ignore these MariaDB-specific options. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds `App\PhpStan\NoAddSqlInMigrationRule`, a project-local PHPStan
rule that flags any `$this->addSql(...)` call in files under
`migrations/`. Future migrations are forced through Doctrine's Schema
tool API (`$schema->createTable()`, `$table->addColumn()`, …),
keeping the consolidated 3.0 migration's portability property as the
project evolves.
Wiring:
- Rule lives in `tools/phpstan/`, mapped via composer's autoload-dev
to `App\PhpStan\` so it's available at lint time but never loaded
into the runtime container (Symfony's `App\:` resource scan only
covers `src/`).
- `phpstan.dist.neon` registers the rule and adds `migrations/` to
the analysed paths.
Native SQL in entity listeners is intentionally out of scope here;
that conversion is deferred. The rule's path-based scoping means it
only enforces the convention for migrations, not the rest of the
codebase.
Verified the rule fires by adding a temporary `_RuleSmokeTest.php`
migration with a single `addSql('SELECT 1')` call — PHPStan reported
the expected `migrations.noAddSql` error and exited non-zero.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents the convention shift under [Unreleased]: future migrations must use Doctrine's Schema tool API; the consolidated 3.0 migration already does, and the new PHPStan rule enforces it for everything that follows. Calls out that this lays the groundwork for portability without claiming it — runtime is still MariaDB-only and CI does not yet cover other platforms. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the type-name string literals passed to `$table->addColumn()` with the matching `Doctrine\DBAL\Types\Types::*` constants — and `UlidType::NAME` / `RRuleType::RRULE` for the two custom types in this codebase. Refactor-safe (renames at the type-class level propagate via IDE rename), and signals at a glance that these are Doctrine type identifiers rather than free-form strings. No Rector rule covers this conversion for `addColumn()` directly — `Rector\Transform\Rector\Attribute\AttributeKeyToClassConstFetchRector` handles `#[Column(type: 'string')]` on entity attributes but not method-call arguments. A custom Rector rule would be overkill for one migration; conversion was scripted via a small Python regex pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
Stacked on #441. Targets
feature/consolidate-migrations; retarget torelease/3.0.0after #441 merges.Why now
A major version is the natural cut-point for changing migration conventions, and the consolidated 3.0 migration is the entire current migration population — so it's also a one-time chance to rewrite everything in one place.
Concretely, migrations now go through Doctrine's Schema tool API (
$schema->createTable(),$table->addColumn(),$table->addForeignKeyConstraint(), …) instead of rawaddSql(...)strings. The emitted DDL is identical for MariaDB but the migration is now generated through the platform abstraction, so the same code produces native DDL on any database Doctrine supports.This PR lays the groundwork for portability — it does not claim it. Runtime is still MariaDB-only, native SQL in entity listeners is deferred, and CI does not yet exercise other platforms. The conversion is scoped to migrations because we're already touching them in #441 (consolidation + rollup), so the cost of doing it now is low and the convention is set going forward.
Summary
Version20260506215847.phpfrom 154 rawaddSqlcalls to the equivalent Schema tool builders. A smallapplyTableOptions()helper at the bottom collapses the engine/charset/collation triplet that would otherwise repeat 29 times.'string','datetime_immutable', …) with the correspondingDoctrine\DBAL\Types\Types::*constant — plusUlidType::NAMEandRRuleType::RRULEfor the two custom types — so renames propagate via the type system rather than across free-form strings.App\PhpStan\NoAddSqlInMigrationRule, a project-local PHPStan rule that flags any$this->addSql(...)call insidemigrations/. PHPStan path coverage extended tomigrations/.tools/phpstan/+ composerautoload-devso it's available at lint time but never loaded into the runtime container.[Unreleased].On Rector
No off-the-shelf Rector rule covers either conversion:
Rector\Transform\Rector\Attribute\AttributeKeyToClassConstFetchRectorhandles#[Column(type: 'string')]on entity attributes but not method-call arguments, and there's no rule foraddSql→ Schema tool. The string-to-constant conversion in this PR was scripted with a small Python regex pass and verified by re-runningmigrate+schema:validate. A custom Rector rule was considered overkill for a single migration; the PHPStan rule prevents the worse problem (rawaddSql) on future migrations.Test plan
migrations:migrate→schema:validateexits 0migrations:rollup→schema:validateexits 0mysqldump --no-datadiff vs the previous raw-SQL version on MariaDB: only column reordering and index reordering (Schema tool emits columns inaddColumn()insertion order). No functional differences. The single FK name divergence oninteractive_slide_config(FK_138E544D9033212AvsFK_D30060259033212A) is a pre-existing artifact from the table rename that was already present in WIP: consolidate 25 historical 2.x migrations into one end-of-2.7 schema #441 and didn't break CI there.addSql('SELECT 1')(then deleted)Out of scope (deferred)
🤖 Generated with Claude Code