myvars / form-flow
Generic Symfony controller-flow coordinators (Form/Action/Confirm/Search/InlineEdit) — the FormFlow pattern.
Requires
- php: >=8.5
- pagerfanta/core: ^4
- symfony/config: ^8.1
- symfony/dependency-injection: ^8.1
- symfony/deprecation-contracts: ^3
- symfony/form: ^8.1
- symfony/http-foundation: ^8.1
- symfony/http-kernel: ^8.1
- symfony/object-mapper: ^8.1
- symfony/options-resolver: ^8.1
- symfony/routing: ^8.1
- symfony/security-csrf: ^8.1
- symfony/ux-turbo: ^3.1
- twig/twig: ^3
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95
- phpstan/phpstan: ^2.3
- phpstan/phpstan-symfony: ^2.1
- phpunit/phpunit: ^13.4
- symfony/validator: ^8.1
Suggests
- symfony/validator: Checks an inline edit against the entity property's rules (InlineEditContext property)
Provides
None
Conflicts
- symfony/event-dispatcher-contracts: <2.5.3 || >=3.0,<3.4.1
- symfony/property-info: <6.4.3 || >=7.0,<7.0.3
- symfony/translation-contracts: <2.5.3 || >=3.0,<3.4.2
Replaces
None
- dev-main / 1.6.x-dev
- v1.6.0
- v1.5.0
- v1.4.0
- v1.3.1
- v1.3.0
- v1.2.0
- v1.1.0
- v1.0.0
- dev-docs/36-getting-started
- dev-chore/prepare-1.6.0
- dev-chore/18-dependency-floors
- dev-chore/27-strict-types
- dev-docs/30-redirect-stream-action
- dev-fix/28-search-out-of-range-in-frame
- dev-docs/19-docs-and-claude-md
- dev-chore/23-unreachable-operation-check
- dev-chore/17-non-yoda-style
- dev-chore/16-test-gaps
- dev-chore/15-flow-test-assertions
- dev-chore/14-test-conventions
This package is auto-updated.
Last update: 2026-11-03 15:01:15 UTC
README
Thin controllers, consistent flows. myvars/form-flow is a small Symfony bundle that factors the
repetitive parts of CRUD-style controllers — form handling, validation feedback, CSRF-guarded
confirmation, pagination, Turbo-aware redirects — into five reusable flow coordinators. Your
controller actions stay one call long, and every create/update/delete action follows the same shape.
#[Route('/task/new', name: 'app_demo_task_new', methods: ['GET', 'POST'])] public function new(Request $request, CreateTaskMapper $mapper, CreateTaskHandler $handler, FormFlow $flow): Response { return $flow->form( request: $request, formType: TaskType::class, data: new TaskForm(), mapper: $mapper, // form DTO -> command handler: $handler, // command -> ResultInterface context: FlowContext::forCreate(FlowModel::create('demo', 'task')), ); }
The task classes in this example (TaskForm, TaskType, the mapper and the handler) are built in
getting started.
The five flows
| Flow | Purpose |
|---|---|
FormFlow |
Create/update with Symfony forms — validate, map to a command, dispatch, flash, redirect |
ActionFlow |
State transitions without a form (approve, complete, …) |
ConfirmFlow |
Confirm-then-execute (delete, cancel, rewind, …) with per-action CSRF scoping |
SearchFlow |
Paginated index pages from a Pagerfanta adapter |
InlineEditFlow |
Single-field inline editing over Turbo Frames |
Which one fits a given action: choosing a flow.
Design
The package owns logic only. It depends on framework packages, Pagerfanta and its own Contract\
ports — never on application code. The consuming app supplies two things:
- Adapters for the ports (
ResultInterface,RedirectTargetInterface,FlasherInterface,SearchCriteriaInterface), small classes it writes once (docs/adapters.md). - Templates — the bundle ships design-neutral defaults so flows render out of the box; your app
overrides any of them by placing its own styled version at the same
templates/shared/form_flow/...path (yourtemplates/wins). See docs/templates.md.
This keeps the flows decoupled from your domain, your DTOs and your design system.
Requirements
- PHP 8.5+
- Symfony 8.1+ components: Config, DependencyInjection, Form, HttpFoundation, HttpKernel, ObjectMapper, OptionsResolver, Routing and Security CSRF
- Twig 3
- Pagerfanta 4 (
pagerfanta/core) - Symfony UX Turbo 3.1+
Optional: symfony/validator, to check an inline edit against the entity property's rules
(InlineEditContext property, see inline editing).
Installation
composer require myvars/form-flow
Enable the bundle (if Symfony Flex did not):
// config/bundles.php return [ // ... MyVars\FormFlow\FormFlowBundle::class => ['all' => true], ];
New to the package? Getting started builds one complete resource in a new
app, from the adapters to the tests. The wiring on its own (adapters, templates, the redirect Turbo
Stream action) is in docs/installation.md.
Documentation
- Getting started — one resource end to end in a new app: adapters, entity, form, handlers, controller, templates, tests
- Installation & wiring — bundle setup, the adapters you implement, the
redirectstream action, path-repo/Docker notes - Using the flows — which flow to use, a worked controller example for every flow, filter forms, testing your controllers; responses, Turbo-aware redirects and flashes;
FlowContext/FlowModel - Ports & adapters — implementing
Result,RedirectTarget,Flasher,SearchCriteria - Template contract — the templates the flows render, how to override them, and their variables
Quality
composer test # PHPUnit — unit, flow, integration and template tests composer phpstan # PHPStan level 7 composer cs # php-cs-fixer (dry-run)
How tests are written: CONTRIBUTING.md › Testing.
License
Released under the MIT License.