← ClaudeAtlas

mir-backend-php-symfonylisted

Make It Right (Symfony module). Symfony 6/7 + Doctrine ORM + PostgreSQL/MySQL + Messenger + API Platform specific reliability augmentation. Use alongside mir-backend and mir-backend-php when the target stack is Symfony — it carries the mechanical footguns that the framework-agnostic tiers deliberately omit: Doctrine N+1 via lazy proxies, Unit of Work memory exhaustion in batch loops, EntityManager becoming closed/stale after an exception, service container singleton semantics and request-state bleed, Serializer group discipline to avoid data leaks, Messenger for async/queued work with idempotent handlers, Validator on DTOs, and explicit flush boundaries. TRIGGER only when the PHP backend stack is Symfony — building, reviewing, or debugging a Symfony controller, Doctrine entity/repository, Messenger handler, migration, or service. Always loads TOGETHER WITH mir-backend (the gates) and mir-backend-php (Zend Engine runtime concerns: shared-nothing lifecycle, FPM worker model, persistent-runtime state bleed, opca
anantbhandarkar/make-it-right · ★ 12 · API & Backend · score 83
Install: claude install-skill anantbhandarkar/make-it-right
# /mir-backend-php-symfony · Make It Right (Symfony) Bottom tier of the chain: `mir-backend` (generic gates) → `mir-backend-php` (Zend Engine runtime model) → **this** (Symfony/Doctrine library mechanics). Run the gates first; load the PHP runtime tier for the lifecycle/process model; reach for *this* at Gate 5 (design mechanics), Gate 6 (implementation), and Gate 7 review. **Runtime-level concerns (shared-nothing lifecycle, FPM sizing, Swoole/RoadRunner/FrankenPHP state bleed, opcache, pconnect, error model) live in `mir-backend-php` — not here.** **Stack assumed:** Symfony 6/7 · Doctrine ORM 2/3 · PostgreSQL or MySQL · Symfony Messenger · API Platform (optional). If the project uses DBAL only (no ORM) or a different message bus, note the divergence before applying these. ## The Symfony/Doctrine footguns AI walks into most ### 1. Doctrine N+1 via lazy proxies — silent DB storm Doctrine wraps unloaded associations in lazy-loading proxy objects. Accessing a proxy property (e.g. `$order->getCustomer()->getName()`) inside a loop triggers a SELECT per iteration — N orders produce N+1 queries total. AI writes this constantly because the PHP code *looks* correct. - **Fix:** use DQL fetch joins (`JOIN FETCH`) or `findBy` with `fetch: 'EAGER'` configuration. For collections, use `Criteria` or a custom repository method with `leftJoin`/`addSelect`. In API Platform, configure `eager` fetch on the relation or use an `ApiFilter` with join. ```php // WRONG — N+1: one SELECT per $or