evercodelab/dependent-select-bundle
Symfony bundle for dependent select fields: dynamically load and update dropdown options based on another field’s selection, simplifying cascading forms with AJAX-backed choices.
This is a Symfony bundle, not a Laravel package — that shapes everything below. composer require evercodelab/dependent-select-bundle will pull it into a Laravel app, but it won't do anything useful: there is no ServiceProvider, no facade, and the code targets Symfony's Form component (AbstractType, FormBuilderInterface, FormEvents::PRE_SUBMIT) plus a Symfony controller for the AJAX endpoint. Laravel's form story is Blade/HTTP, not a Form component, so the bundle has nothing to hook into.
First steps for a Laravel dev:
DependentSelectType or a parent/child pair) and an AJAX endpoint (a controller action that returns JSON options for the child field based on the parent value).composer.json for symfony/form and symfony/framework-bundle constraints — these reveal which Symfony era this targets (likely 2.8/3.x, which is long EOL). That's your signal for how much of the API surface is still relevant.dependent-select.js or inline script in a Twig template). This is the most portable piece: it listens for change on the parent <select>, calls the endpoint, and rebuilds the child <select>. That pattern translates 1:1 to Blade + vanilla JS, HTMX, Livewire, or Alpine.GET /api/regions?country_id= JSON route and a Blade partial, or a Livewire component with wire:model.live.Realistically: treat the repo as reference material, not a dependency. With 0 stars and 0 dependents it's effectively a dormant reference implementation. Copy the idea (parent change → fetch children → swap options → reset downstream selects) into your own stack.
If you're stuck supporting a Symfony app in the same monorepo (or a legacy service you're proxying to from Laravel), the pattern looks like this:
// Symfony side — register in config/bundles.php (modern) or AppKernel (legacy)
$bundles = [ EvercodeLab\DependentSelectBundle\DependentSelectBundle::class => ['all' => true] ];
// A form type that exposes parent/child relationship
$builder->add('country', CountryType::class);
$builder->add('city', DependentSelectType::class, [
'parent' => 'country',
'endpoint' => 'app_cities_by_country',
]);
The bundle wires an event listener so that when the parent submits, the child's choices are populated server-side — this matters because Symfony's Form component validates against declared choices. Laravel has no equivalent constraint, which is why porting the server-side half is mostly unnecessary.
Day-to-day Laravel patterns that replace this bundle:
wire:model.live selects, updatedCountryId() resets cityId and repopulates cities. Zero JS, built-in CSRF, testable. This is the direct analogue of the bundle's intent.<select hx-get="/cities" hx-target="#city"> or Alpine x-on:change="fetch('/cities?country='+$event.target.value)...". Keep the endpoint returning an HTML <option> list so you avoid client-side templating.GET /api/regions?country_id= with a resource collection; the JS swaps <option> elements. Mirror the bundle's endpoint contract exactly (parent key in → child list out) so a Symfony frontend and a Laravel frontend can share it.Integration tips:
Cache::remember("regions:{$countryId}", 3600, ...)) because dependent-select endpoints get hammered by every keystroke if you attach them to search inputs.[{'id'=>1,'name'=>'...','selected'=>true}]).Rule::exists scoped to the parent (Rule::exists('cities','id')->where('country_id', $countryId)). This is the equivalent of the Symfony Form's choice validation the bundle gives you for free.extra.laravel in composer.json, no service provider, no config publish. Installing it in Laravel is a no-op; don't expect a facade or config/dependent_select.php.$.ajax with no CSRF token — a bad import into a Laravel app where VerifyCsrfToken is active).api.php route it will be unauthenticated by default. Put it behind auth + web middleware and include @csrf or X-CSRF-TOKEN in the fetch headers.Content-Type: application/json; then confirm the JS is targeting the correct child select ID (bundle-generated IDs often differ from your Blade IDs — this is the most common silent bug).endpoint route name string (breaks if you rename routes), and a template path override. All three are hard-coded assumptions that don't map to Laravel's Route names.PRE_SUBMIT idea (server-side revalidation of dependent choices) and the "return options as JSON, let JS rebuild the DOM" contract. Implement the former as a FormRequest rule and the latter as a resourceful JSON endpoint.->where('country_id', $id) with an index on country_id, and eager-load if the child has grandchildren. For three-level cascades add a composite index and cache the leaf level.GET /search/cities?q= instead.php artisan your way through it. Test the ported behavior directly — Livewire::test(CitySelector::class)->set('countryId', 1)->assertSee('...') or a feature test against your JSON route. The bundle itself has no tests you can lean on.Bottom line: the package is a useful conceptual reference for dependent selects but is not a dependency a Laravel developer can adopt. Skim the JS and the endpoint contract, then build the cascade natively with Livewire or HTMX and enforce the parent-child relationship with a scoped exists rule.
How can I help you explore Laravel packages today?