Symfony workflow
Writes Symfony PHP — DI container, bundles, Doctrine, Messenger, Security voters, console commands. For Laravel / Eloquent / Artisan use `laravel`. For framework-free PHP use `php-coder`.From its SKILL.md
npx -y skills add event4u-app/agent-config --skill symfony-workflowAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 7 stars7 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.5 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it
symfony-workflow
When to use
Use this skill for all Symfony-specific code generation and editing tasks, especially when working with:
- Controllers (annotated / attribute-routed)
- Request listeners / event subscribers
- Services and Dependency Injection (
services.yaml) - Forms and validators
- Doctrine entities, repositories, and migrations
- Security: firewalls, voters, authenticators
- Messenger handlers and transports
- Console commands
- Bundles, compiler passes, and tagged services
- Twig templates and view logic
This skill extends the base php-coder skill and applies Symfony conventions on top of the project's general PHP rules.
When to use the analysis sibling
When the task is understanding how a Symfony app boots, wires its container, routes requests, or fails at runtime — defer to project-analysis-symfony first, then return here for the edit. This skill assumes the kernel/container layout is already known.
Procedure: write Symfony code
→ First apply the php-coder skill for general PHP rules.
Then add these Symfony-specific checks:
- Confirm Symfony —
bin/consoleexists,composer.jsonlistssymfony/framework-bundle. - Confirm version —
composer.lockfor the framework-bundle major. 5.x → 6.x → 7.x differs in attribute routing, voter signatures, and Messenger DSN shape. - Inspect app structure — standard, modular (
src/Module/<Name>/), or DDD-style. Do not enforce a layout the project does not use. - Check config layout —
config/packages/<env>/,services.yamlautowiring vs explicit bindings,config/bundles.php. - Check test conventions — PHPUnit/Codeception; unit vs integration vs functional split.
Core Symfony principles
- Follow Symfony conventions unless the project explicitly does otherwise.
- Keep controllers thin — delegate to services.
- Rely on autowiring + autoconfigure unless the project has explicit bindings.
- Prefer attributes over annotations on 6.x+; keep annotations only if the codebase still uses them.
- Use service IDs by FQCN —
App\Service\Foo, not custom string IDs. - Services are private by default; do not flip
public: trueto make tests pass. - Do not bypass the container with
newon classes that have collaborators.
HTTP layer rules
- Controllers:
- extend
AbstractControlleronly when the project does - accept
Requestor a DTO; delegate business logic; return aResponsevariant
- extend
- Use
#[Route]attributes on 6.x+; YAML routes only where the project already does. - Use
#[MapRequestPayload]/#[MapQueryString](7.x+) for request DTOs when the project uses them. - Validate via Symfony Validator on the DTO, not inline in the controller.
- Use
ParamConverter/ argument resolvers for entities only when the project uses them.
Validation rules
- Symfony Validator with constraints on DTOs / entities.
- Prefer attribute constraints (
#[Assert\NotBlank],#[Assert\Email]) on 6.x+. - Render errors from
ConstraintViolationListInterface— never compose error arrays by hand. - Validation is declarative; do not put domain validation in entity setters.
Service layer and DI rules
- One responsibility per service; constructor injection.
- Interfaces when there are multiple implementations or the boundary is mocked.
- Tagged services for collecting implementations —
#[AutoconfigureTag]or YAML tags, never an injected array of FQCNs. - Decorators via
#[AsDecorator](6.1+); respect priority. - Compiler passes only when wiring cannot be expressed via attributes/YAML.
- Do not call
Container::getin application code.
Routing rules
- Follow the existing organization — attributes on controllers, or YAML in
config/routes/. - Route names:
<resource>_<action>(user_show,invoice_list). #[IsGranted],#[RateLimit]at the route level, not inside the controller body.requirements:for path parameter constraints; do not validate in the controller.
Response rules
- Match the project's response style: Twig,
JsonResponse, API Platform, or redirects with flash. - For APIs: consistent status codes;
ConstraintViolationList→application/problem+json; DTOs / serializer groups, not raw entities. - Do not return entities directly unless the project consistently does that.
Messenger and async work
- Messenger for async/deferred work; one message class per intent.
- Handlers:
MessageHandlerInterface(5.x) or#[AsMessageHandler](6.x+). - Route via
framework.messenger.routinginconfig/packages/messenger.yaml. - Configure
failure_transportexplicitly — without it, failed messages disappear. - Pass IDs, not entities; the consumer re-fetches.
Events and subscribers
#[AsEventListener](6.1+) orEventSubscriberInterface— match the project's convention.- Past-tense event names (
UserRegistered,OrderPaid); one side-effect per subscriber. - Respect priority on
kernel.request/kernel.response— wrong priority is a frequent bug source.
Security, voters, authorization
- One firewall per surface in
config/packages/security.yaml(main, API, admin). - Voters for object-level permissions; never role checks in templates or controllers.
#[IsGranted]on actions;$this->isGranted()only when the result drives downstream logic.- Stateless APIs: token-based authenticator, not form-login.
Config and environment
- Read via
ParameterBagInterfaceor#[Autowire(param: ...)]— never$_ENVdirectly. - New env vars in
.env(+.env.test); production values in deployment config. - Bundle config under
config/packages/<bundle>.yaml; env overrides underconfig/packages/<env>/.
Doctrine and persistence
- Doctrine ORM unless the project uses DBAL/raw SQL by convention.
- Repositories for non-trivial queries; no inline QueryBuilder in controllers/services.
- N+1 awareness: fetch joins via
addSelectorEAGERwhen always needed. - Transactions via
EntityManager::wrapInTransaction()for multi-write atomicity. - Lifecycle hooks (
PreFlush,PostUpdate) — no domain logic there unless the project already does.
Migrations
- Generate via
doctrine:migrations:diff; review before commit. - Reversible — implement
up()anddown(). - One concern per migration; destructive prod changes split into expand → migrate → contract.
Twig
- Templates are dumb — presentation only; pre-computed view models from the controller/service.
- Reuse via
{% extends %}/{% include %}/ macros. - Auto-escape on;
|rawonly when content is provably safe.
Bundles and compiler passes
- Bundles are for reusable, redistributable code — not "another folder".
- Compiler passes only when wiring cannot be expressed via attributes/YAML.
Console commands
#[AsCommand](6.x+); one command class per intent; constructor injection.- Long-running:
--limit,--time-limit, gracefulSIGTERMshutdown. - Output via
OutputInterface— neverecho.
Output format
- Symfony code following framework conventions and project architecture.
- All related files (controller, service, DTO, repository, test, config) as needed.
- Schema changes — migration file plus updated entity/mapping.
Do NOT
- Business logic in controllers, entities, listeners, or Twig.
- Bypass the container with
newon classes with collaborators. $_ENV/$_SERVERdirect access — go through the parameter bag.- Return Doctrine entities from an API endpoint — use DTOs or serializer groups.
- Silently swallow Messenger failures — route to a failure transport.
- Flip services
public: trueto make tests pass — use the test container. - Pass entities through Messenger — pass IDs.
- Mix attribute and YAML routing for the same controller surface.
Gotcha
- Autowiring fails silently when two implementations exist without explicit binding — read the error, don't just flip
public: true. #[IsGranted]is a no-op if the controller is not a service (autoconfigure handles it by default).- Messenger
failure_transportis opt-in; without it, failures vanish. - Compiled container changes need
cache:clearinprodbefore debugging "config not applied". - Symfony 7.x removed deprecated APIs — verify
composer.lockbefore assuming 6.x patterns work.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most automation workflows skills give in ~1.9k tokens
Counted across 745 of the 1,008 authors here whose files we hold, read 2026-08-07
- Write conventional commit messagesin 36 of 745, across 35 files
- Delete branches after mergein 30 of 745, across 21 files
- Make atomic commitsin 25 of 745, across 15 files
- Write minimal code to pass testsin 22 of 745, across 10 files
- Re-snapshot after navigation or DOM changesin 21 of 745, across 13 files
- Use try-catch for error handlingin 20 of 745, across 8 files
- Run tests before committingin 20 of 745, across 12 files
- Write tests before implementationin 20 of 745, across 8 files
- Configure branch protection rulesin 19 of 745, across 5 files
- Explain the why in commit messagesin 19 of 745, across 9 files
- Refactor code while tests remain greenin 19 of 745, across 6 files
- Interact with elements using refsin 19 of 745, across 11 files
Said here and by no other author read
- confirm the Symfony major version before coding
- keep controllers thin and delegate to services
- use constructor injection for dependencies
- generate migrations using doctrine:migrations:diff
- implement both up and down methods in migrations
- configure an explicit messenger failure transport
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.