manmohanjit/nova-belongs-to-dependency
A Laravel Nova field package that makes BelongsTo relationships depend on another form field. When the parent select changes, the dependent BelongsTo options reload and filter automatically, enabling cascading dropdowns such as country, state, and city inside Nova resources.
Install the package like any Nova tool/field package and confirm asset registration before touching your resources:
composer require manmohanjit/nova-belongs-to-dependency
php artisan nova:publish # or: vendor:publish --tag=nova-belongs-to-dependency
Where to look first:
vendor/manmohanjit/nova-belongs-to-dependency/README.md — the exact method name for wiring the parent field lives here. Don't guess from the class name; the API is small and the README is the only source of truth.src/BelongsToDependency.php (or similarly named field class) — check which base class it extends (Laravel\Nova\Fields\BelongsTo), what it adds to relatableQuery/jsonSerialize, and whether it ships a compiled dist/js bundle or source only.composer.json — check the laravel/nova and php constraints. This tells you instantly whether it targets Nova 3 or Nova 4.First use case: the classic cascading select — Country → State, Tenant → Team, Category → Subcategory. Drop it on a resource form where a BelongsTo should only list options valid for the sibling field's current value:
use Manmohanjit\BelongsToDependency\BelongsToDependency;
public function fields(Request $request)
{
return [
BelongsTo::make('Country'),
BelongsToDependency::make('State')
->dependsOn('country'), // parent's field attribute, not its label
];
}
Then filter the child resource's relatableQuery on the value the field forwards. Start there, verify the options refresh in the browser when you change the parent, and only then wire up a second dependency.
Filter on the child resource, not in the field. The package sends the parent's value along with Nova's relatable request; the actual narrowing is your job on the target resource:
// app/Nova/State.php
public static function relatableQuery(NovaRequest $request, $query)
{
if ($countryId = $request->input('dependsOn') ?? $request->input('country')) {
$query->where('country_id', $countryId);
}
return $query;
}
Confirm the request key the field actually sends by inspecting the network tab once — the package's JS is what names it.
Pair it with the matching parent field type. The package advertises support for BelongsTo, Text and Status parents, which means "dependency value" can come from a searchable dropdown, a free-text slug, or an enum-backed status. For Text parents, normalize server-side (strtolower, trim) before filtering so the option list stays stable.
Edit vs. create forms. On edit, the parent already has a value, so the child populates immediately. On create, the child is blank until the user picks a parent — make sure the field clears its stale selection when the parent changes, and consider gating submission with ->rules('required') only after the parent is required too.
Multi-level chains. Chain two instances (Country → State → City) by having each dependsOn the previous. Keep the chain shallow — each link is another round-trip on the relatable endpoint, and the component re-fetches on every parent change.
Pair with dependsOn-aware index filters. If the same cascade appears in a Nova filter or lens, reuse the same query logic in a private helper on the child resource so the form and the filter can't drift.
Testing. Feature-test the relatable endpoint directly — it's the real contract:
$this->actingAs($user)
->getJson("/nova-api/states?dependsOn=5")
->assertOk()
->assertJsonCount(3, 'resources');
That test survives a package upgrade or a swap to a hand-rolled field, which is exactly the insurance you want for an archived dependency.
laravel/nova constraint and budget a replacement (a small custom BelongsTo with a relatableQuery filter and dependsOn meta passed via withMeta) before your next major Nova upgrade. For a 10-star package, assume you own the code the day you install it.dist/js was built against Nova 3's Vue 2 toolchain, the field may render nothing or throw in the console. Check dist/ first; if there's no source, you cannot rebuild it.nova:publish after any composer update that touches the package.->dependsOn('Country') will silently never match if the attribute is country. Symptoms are an unfiltered dropdown, not an error.->rules() guard or an saving() check on the model so a now-invalid belongs_to id can't be persisted.relatableQuery with LIKE; adding a where on the dependency without an index on that foreign key turns the dropdown into a table scan on large tables. Index country_id (etc.) before shipping.dependsOn and nullable interact badly on bulk create. If the parent is optional, define explicit behavior for the empty case — usually "show all options" or "show none and disable" — rather than letting the backend return the full unfiltered list.relatableQuery on your child resource combined with the field's meta payload. You can keep the package's frontend behavior and move all business logic server-side, which is the least painful path to eventually replacing it.How can I help you explore Laravel packages today?