Detail Phase 2 scope: 5 sub-commits, Doctrine ORM 3 + SQLite, headline maker
Some checks failed
CI / Quality (push) Has been cancelled
Some checks failed
CI / Quality (push) Has been cancelled
Adds a stack-additions table (ORM, dev DB, default ID type, pagination strategy, Doctrine→Mercure trigger), a 5-step sub-commit sequence, and the done-criteria for Phase 2. Defaults baked in (subject to user override before sub-commit 1 starts): - Doctrine ORM 3.x with DoctrineBundle + DoctrineMigrationsBundle - SQLite at var/data.sqlite for dev - Auto-incrementing int IDs by default; the maker takes a UUIDv7 flag - Cursor-based pagination, default page size 50 - Synchronous postPersist/postUpdate/postRemove subscribers Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
30
PLAN.md
30
PLAN.md
@@ -655,6 +655,36 @@ framework/
|
||||
- `make:bridge:resource` maker implemented end-to-end (entity + controller + lifecycle wiring + QML snippet).
|
||||
- Convention test: run `bin/console make:bridge:resource Todo`, then `make:migration` and `doctrine:migrations:migrate`; verify a QML `ListView` updates on backend changes triggered from a CLI command. No handwritten glue between the two sides.
|
||||
|
||||
#### Phase 2 detailed scope
|
||||
|
||||
Phase 2 turns the framework from "transports work" into "you can ship a reactive list-of-X with three commands". After this phase, the smallest working bridge app is `make:bridge:resource Foo && make:migration && doctrine:migrations:migrate` plus a `<Foo>List.qml` snippet — and the list updates live as `Foo` rows change.
|
||||
|
||||
**Stack additions (skeleton):**
|
||||
|
||||
| Thing | Choice |
|
||||
| --- | --- |
|
||||
| ORM | Doctrine ORM 3.x + DoctrineBundle + DoctrineMigrationsBundle |
|
||||
| Dev DB | SQLite at `var/data.sqlite` (zero-config) |
|
||||
| Default ID type | auto-incrementing `int` (the maker takes a flag for UUIDv7 if asked) |
|
||||
| Pagination | cursor-based (opaque base64-JSON of `{lastId, lastSortKey}`), default page size 50 |
|
||||
| Doctrine→Mercure trigger | `postPersist` / `postUpdate` / `postRemove` event subscribers (synchronous) |
|
||||
|
||||
**Sub-commits (each ends runnable):**
|
||||
|
||||
1. **Doctrine + migrations into the skeleton.** `composer require doctrine/orm doctrine/doctrine-bundle doctrine/doctrine-migrations-bundle`, generate `config/packages/doctrine.yaml` and `doctrine_migrations.yaml`, point the dev DB at `var/data.sqlite`. `bridge:doctor` gains a `database reachable` check. `make doctor` is green on a fresh clone after `make install` + `bin/console doctrine:migrations:migrate`.
|
||||
2. **`ModelPublisher` (PHP) + Doctrine subscriber.** New service in `framework/php/src/`: takes a Doctrine entity + change op + correlation key, computes the envelope and dual-publishes to `app://model/{name}` (collection topic) and `app://model/{name}/{id}` (entity topic). The subscriber introspects entities tagged with `#[BridgeResource]` and routes lifecycle events through `ModelPublisher`. PHPUnit covers the envelope shape, dual publish, and correlation-key passthrough.
|
||||
3. **Reactive models + full Update Semantics (QML).** `ReactiveListModel` (`QAbstractListModel` + topic subscription + initial fetch + cursor-driven `fetchMore` + `pending` role + diff application). `ReactiveObject` (single-entity equivalent). `BackendConnection`'s enum extended to `Connecting / Online / Reconnecting / Offline` with thresholds (PLAN.md §5). `AppShell.qml` ships a `Reconnecting` top banner and `Offline` overlay with retry. Optimistic command wiring: `RestClient.invoke()` returns a Promise that resolves on the matching Mercure echo (correlation-key-matched), rolls back on `4xx`/`5xx` or timeout (default 10s).
|
||||
4. **`make:bridge:resource` maker.** `symfony/maker-bundle` becomes a `require-dev` of the bundle. `BridgeResourceMaker` generates: `src/Entity/<Name>.php` (`#[BridgeResource]` attribute, `id` + `title` stub fields), `src/Controller/<Name>Controller.php` (CRUD on `/api/<name>`), and `qml/<Name>List.qml` (a starter `ListView` bound to a `ReactiveListModel`). After-hint points at `make:migration`. Lifecycle wiring is automatic (the subscriber from sub-commit 2 handles any `#[BridgeResource]` entity), so no per-resource listener is generated. The maker output is checked into the skeleton as a regression reference for Phase 3's CI snapshot test.
|
||||
5. **Convention test + phase closure.** Run the maker against a `Todo` resource, run migrations, trigger inserts/updates/deletes via `bin/console` (a one-liner) and confirm the skeleton's QML window shows the list updating live, with row-level `pending` rendering during the brief in-flight window. Capture a short `framework/skeleton/README.md` walkthrough so future readers can reproduce.
|
||||
|
||||
**Done criteria:**
|
||||
|
||||
- `make:bridge:resource Todo` plus `make:migration` plus `doctrine:migrations:migrate` produces a working reactive list with no handwritten bridge glue.
|
||||
- Triggering CRUD via `bin/console` updates the QML `ListView` within ~50 ms of the SQL commit.
|
||||
- Killing FrankenPHP mid-mutation: `connectionState` transitions to `Reconnecting` then `Offline`; the optimistic row stays `pending` until rollback fires; reconnect re-fetches and clears.
|
||||
- `make quality` stays green (PHPStan, cs-fixer, PHPUnit, qmllint).
|
||||
- The skeleton's checked-in maker output is byte-for-byte the same as a fresh maker run, so Phase 3's CI snapshot test has a baseline.
|
||||
|
||||
### Phase 3 — POC application, testing infrastructure (built via the makers)
|
||||
|
||||
- Build `examples/todo` by running the makers — `make:bridge:resource Todo`, `make:bridge:command MarkAllDone`, `make:bridge:window TodoWindow`. The example doubles as a maker-output regression test (CI diffs generator output against a checked-in reference).
|
||||
|
||||
Reference in New Issue
Block a user