Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Dependent Select Bundle Laravel Package

liz/dependent-select-bundle

A Symfony bundle for dependent select fields: adds cascading dropdowns to forms so one select's options update based on another field's value, with form type integration and front-end wiring for linked selects.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm what you're actually installing before writing any code. This is a fork of evercodelab/dependent-select-bundle — a Symfony bundle, not a Laravel package. There is no service provider, no publishable config, no Blade directive. Before anything else, read its composer.json:

  • require will almost certainly pin symfony/form to the 2.x/3.x era and php to >=5.5.
  • It will likely pull doctrine/orm (via EntityType) and expect a Doctrine entity repository.

If those constraints conflict with your Laravel 10/11 or PHP 8.1+ stack, stop here: you cannot composer require this into a Laravel app. The rest of this section assumes one of two realities — you're in a Symfony codebase (rare for this audience), or you're porting the pattern into Laravel.

Path A — Symfony host (the intended deployment). If you genuinely run Symfony:

composer config repositories.dependent-select vcs https://github.com/l396635210/dependent-select-bundle
composer require liz/dependent-select-bundle:dev-master

Then register the bundle in config/bundles.php, import its routing (usually Resources/config/routing.yml or a Routing loader), and generate the Doctrine migration for the pair table yourself — the bundle ships an entity, not a migration.

Path B — Laravel host (the likely reality). Do not try to bridge Symfony Form into Laravel. Instead, port the concept:

  1. A dependent_select_options table: (group, parent_value, child_value, sort).
  2. A route: GET /api/dependent-options/{group}?parent={value} returning {id, label}[], authorization-scoped.
  3. A small Alpine.js or Livewire component that fires on change of the parent <select> and repopulates the child.

That is roughly 100 lines and is what you'd actually maintain day-to-day.

Where to look first (Symfony path):

  • src/Form/Type/DependentSelectType.php — the option names (parent, group, use_ajax, class) and whether it extends ChoiceType or EntityType.
  • src/Entity/DependentSelect.php — the pair model. Check the column names; from/to are keyword-adjacent and a frequent first-run SQL headache.
  • Resources/public/js/*.js — the cascade trigger; note which selector it binds to and which route name it hits.
  • Resources/config/routing.* — the AJAX endpoint the JS expects.

First use case: country → region → city on an address form. Seed the pair table with group = 'geo', attach the type to region with parent => 'country', then watch the network tab for a single XHR on change returning the filtered list.

Laravel-first use case: same geography, but with Eloquent. A regions table with country_id, a controller returning Region::where('country_id', $id)->get(['id','name']), and Alpine binding to @change. That's the day-one deliverable.

Implementation Patterns

Seed the mapping table, don't hand-edit it. On the Symfony path the bundle's whole model is dumb rows of parent→child pairs. Generate those from real domain data via a console command or migration, and treat the table as a rebuildable cache. On the Laravel path, the equivalent is a seeder or an Eloquent observer that syncs regions/cities down into a flattened option table if you decide you need one at all (often you don't — a joined query is simpler).

Declare the dependency on the child, not the parent. In Symfony:

$builder
    ->add('country', CountryType::class)
    ->add('region', DependentSelectType::class, [
        'parent'   => 'country',
        'group'    => 'geo',
        'use_ajax' => true,
    ]);

In the Laravel port, mirror the same idea in your form request or Blade partial — one prop (data-parent="country") that the JS reads to decide which sibling field to watch.

Namespace your groups. Use values like checkout.geo and profile.geo rather than a bare geo, so two forms sharing the pair table can't bleed options into each other. This is the most common "why did the wrong list appear?" bug with this bundle.

Handle the re-render / edit case server-side. The classic failure is an edit form where the parent is already set: the child <select> shows nothing until the user touches the parent. Fix it by pre-populating the child options in the initial render (Symfony: a form event that loads the child choice list from the persisted parent; Laravel: eager-load the child options and pass them into the view on first paint). Do not rely on the JS to "catch up."

Wrap the dependency lookup behind a service. Both paths benefit from the same seam:

interface DependentOptionsResolver {
    public function options(string $group, ?string $parentValue): array;
}

On Symfony, back it with the bundle's repository. On Laravel, back it with Eloquent (and later swap to Redis or a cached array if the reference data is hot). Either way, the form/controller depends on the interface, not on DependentSelect directly — which is what makes the eventual "drop the bundle, keep the feature" migration cheap.

Cache reference data. Geography, categories, and product variants rarely change. Cache the resolver output per (group, parent_value) with a keyed TTL and bust it on seed. This is the single largest performance win and costs almost nothing.

Authorization at the endpoint, not just the UI. The AJAX endpoint will happily return child options the current user isn't permitted to see. Scope the query by policy in the resolver — on Laravel, a Gate::authorize or a scoped query; on Symfony, a voter. Do not rely on the parent field being hidden.

Gotchas and Tips

Version rot is the headline risk. The upstream targets Symfony 2.x/3.x and PHP 5.x–7.x. Expect Composer to refuse to install on modern Laravel/Symfony. Confirm the diff against upstream before assuming the fork fixed anything — a 0-star fork with no dependents is usually a verbatim snapshot cut for one application, and you will own every future patch.

from / to column names bite. Reserved-ish in several DB engines and an occasional problem in Doctrine's schema tool. Quote them in raw SQL, and check the generated migration carefully on first install.

use_ajax => true is not the same as "works with a large list." The type still needs a data source for the initial render. If the child list is empty on first paint, you are hitting the pre-population problem above, not a broken endpoint.

The JS binds by selector, not by field name. If you rename the parent field or wrap the form in a modal that re-ids inputs, the cascade silently stops. Keep the data-parent attribute stable, or patch the bundle's JS to bind on a class rather than an ID.

Debug the endpoint independently of the form. Before chasing form events, hit the routing URL directly with a parent value and inspect the JSON. Nine times out of ten the bug is there (empty mapping table, missing group, auth block) and not in the form type.

Test with an integration test that asserts the network contract. On Symfony, assert the JSON response shape. On Laravel, a feature test hitting /api/dependent-options/geo?parent=FR and asserting [{id, label}]. This is the part teams forget when they port the pattern — you rebuild it natively, so you also rebuild the test coverage, and that's fine, but do it.

Extension point worth knowing: because the Symfony type extends ChoiceType, you can subclass it and override configureOptions or finishView to inject pre-populated options on edit. That's the cleanest place to fix the re-render case without touching the JS.

Escape hatch. If the bundle's constraints ever fight you (PHP upgrade, Symfony version bump, a fork you can't patch because you don't control the repo), the code you wrote against DependentOptionsResolver still works — swap the implementation for a native query resolver and delete the dependency. Design for that exit from day one; with zero dependents and no maintenance signal, assume you'll take it.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core