nowo-tech/select-all-choice-bundle
A Symfony bundle that adds a "select all" option to ChoiceType form fields, letting users check or uncheck every choice at once with a single click. Simple to install, configurable, and ideal for long checkbox or radio lists where bulk selection improves usability.
Install the bundle
composer require nowo-tech/select-all-choice-bundle
Zero dependents and zero stars at the time of writing, so pin the constraint (^x.y) rather than tracking *, and skim src/ before shipping — there is no community track record to lean on.
Register it the Symfony way
config/bundles.php.NowoTech\SelectAllChoiceBundle\SelectAllChoiceBundle::class => ['all' => true].Wire the frontend asset
This bundle is frontend-driven: the PHP side only sets data-* attributes, and a Stimulus controller does the actual work. Confirm which asset strategy the repo targets (src/Resources/assets/ for source, src/Resources/public/build/ for the built output) and load it via AssetMapper/importmap, Encore, or the bundle's Twig include. If the controller never registers, the field renders as a plain ChoiceType with a stray data attribute — the classic "nothing happens" symptom.
Where to look first
| Concern | Location |
|---|---|
| Form option / type extension | src/Form/ (extension + type) |
| Data attributes injected into the view | buildView() in the form type/extension |
| Twig form theme blocks | src/Resources/views/ |
| Toggle behaviour | src/Resources/assets/controllers/ |
First use case
A multi-checkbox role picker, which is the simplest expanded case:
$builder->add('roles', ChoiceType::class, [
'choices' => $roles,
'multiple' => true,
'expanded' => true,
'select_all' => true, // verify the exact option name against the README/src
]);
Render, then inspect the DOM: you should see the attribute that marks the field plus a config payload on the wrapping element. That markup is the contract between PHP and the Stimulus controller — if it is missing, the problem is server-side wiring, not JavaScript.
Per-field opt-in, not global. Add the option only where a "select all" genuinely helps (filters, role assignment, bulk tag pickers). Enabling it for every multiple field makes dense forms noisier and confuses screen-reader users who meet a second checkbox above a list.
Two rendering paths, one option. The bundle covers both:
// expanded: renders N checkboxes; toggle is a sibling checkbox
'expanded' => true, 'multiple' => true,
// collapsed: renders <select multiple>; toggle drives selectedness
'expanded' => false, 'multiple' => true,
Treat these as separate features in your test plan — the DOM differs completely and a bug in one will not surface in the other.
Keep validation server-side. The toggle is a UI accelerant; it submits nothing of its own. Preserve your existing constraints (NotBlank, Count, Choice) on the mapped property. Never derive "all selected" logic from the toggle's presence.
Integrate with your form theme once. If the bundle ships Twig blocks, add its theme at the application level (twig.form_themes in config/packages/twig.yaml) instead of repeating {% form_theme %} in every template. This keeps the markup hook stable when you upgrade the bundle.
Compose with Turbo / Live Components. The controller must re-initialise when a form is re-rendered inside a Turbo frame or a Live Component re-render. Symfony UX's stimulus-loading handles this if the controller is registered through the normal controllers.json / importmap path; hand-rolled DOMContentLoaded bootstraps will not.
Isolate scope. If you must customise behaviour, subclass the bundle's Stimulus controller and override the toggle handler rather than forking the asset. Selectors should be scoped to the field's own wrapper (the element carrying the bundle's data attribute) so sibling checkbox groups in the same fieldset are unaffected.
Consistent presentation for grouped choices. With choice_group (optgroups in collapsed mode, nested containers in expanded mode), verify the toggle's semantics: "select all" should mean every leaf choice, not every group header.
Progressive enhancement as a rule. Anything you build on top — counters ("3 of 12 selected"), disabled submit buttons — should read the underlying inputs, not the toggle. That way the UI stays correct if the controller fails to load.
select_all key in $request->request. If you need that value server-side, compute it from the submitted collection.disabled checkboxes; in collapsed mode it must skip disabled <option> elements. Verify both, plus the case where every choice is disabled (the toggle should hide or disable itself).querySelectorAll('input[type=checkbox]') inside the controller will sweep up unrelated fields in the same form. If you patch the controller, prefer this.element.querySelectorAll(...) on the field wrapper.data-*. Nested JSON in HTML attributes breaks on quotes and on htmlspecialchars interplay — if the controller reads nothing, dump the raw HTML ({{ form_widget(form.x) }} in isolation) and check the escaping before blaming Stimulus.Error: Missing controller element for "..." or nothing at all. Verify the controller name in the HTML matches the file name and the controllers.json identifier exactly.src/Resources/public/build. That is generated output. Patch src/Resources/assets/, then rebuild with whatever the repo uses (check package.json / Vite config in the bundle). If you are a consumer rather than a maintainer, overriding the built asset through your own entrypoint is safer than patching vendor.this.element.dataset)? (3) DOM — is the injected checkbox present and correctly parented? Most reports conflate these.prototype cloning). Write one smoke test per rendering mode in your app and re-run it on every upgrade; that is cheaper than reading the source each time.prototype, the newly cloned inputs are not known to a toggle created at page load. Re-run the controller's sync when the collection adds an entry — this is the most likely real-world gap.How can I help you explore Laravel packages today?