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

Nova Belongsto Depends Laravel Package

emilianotisato/nova-belongsto-depends

A Laravel Nova field that makes BelongsTo selects depend on another form field: options reload dynamically based on the parent value, so related records stay filtered as users change selections in resource forms.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the field with Composer and let Nova's package discovery pick it up:

composer require emilianotisato/nova-belongsto-depends

There is no migration or config file to publish in the typical Nova-field layout. If the package ships a service provider that registers assets, run php artisan vendor:publish --provider="Emilianotisato\NovaBelongsToDepends\FieldServiceProvider" (or nova:publish on newer Nova versions) and check that dist/js/field.js is registered via Nova::script().

The public API is intentionally small, so verify the exact signature in src/BelongsToDepends.php before wiring it in. The intended shape is a BelongsTo field with a dependsOn() modifier that names the sibling field to watch plus a way to resolve the filtered options (a URL/endpoint or a callback evaluated server-side):

use Emilianotisato\NovaBelongsToDepends\BelongsToDepends;

BelongsToDepends::make('City', 'city_id')
    ->dependsOn('state_id', '/nova-vendor/cities')
    ->searchable();

Where to look first, in order:

  1. README.md — the only authoritative source for the dependsOn() signature and expected endpoint contract.
  2. src/BelongsToDepends.php — the Nova field class; everything it exposes lands in withMeta(), which the browser component reads.
  3. dist/js/field.js (or resources/js/components/FormField.vue if source is included) — this is where the "watch parent field → refetch options" behavior actually lives. Read it before assuming how re-renders behave.
  4. composer.json — check the laravel/nova constraint; a Nova 2/3-era field will silently break under Nova 4/5's asset pipeline.

First use case: a two-level cascade on a Resource form — Category → Product, State → City, Country → Region — where the child dropdown must reset and re-query whenever the parent changes. This is the single thing the package exists for; start there before trying anything more elaborate.

Implementation Patterns

Basic cascade. Put the parent BelongsTo and the dependent field in the same fields() panel. The dependent field needs a resolvable options source — either a Nova-vendor route you own or the package's built-in associatable endpoint with an added filter parameter. Keep the parent field nullable() if the child should be optional, and pair it with ->rules('required') on the child only when a parent is guaranteed.

Filtering options server-side. Do not try to push the full option set into the browser and filter in Vue. For anything beyond a few dozen rows, expose a small route under /nova-vendor/... protected by Nova's middleware (nova guard) that accepts the parent ID and returns {label, value} pairs. The field component should hit that route through Nova.request() so auth, CSRF, and error handling are inherited automatically.

Reusable field builders. Because the API is thin, wrap it in a helper so every resource uses the same shape:

class DependentSelect
{
    public static function make(string $name, string $parent, string $endpoint, ?string $label = null)
    {
        return BelongsToDepends::make($label ?? $name, $name)
            ->dependsOn($parent, $endpoint)
            ->searchable()
            ->nullable();
    }
}

This keeps the dependsOn contract in one place when you eventually need to swap or extend the field.

Clearing stale values. The most common production bug is the child retaining its old ID after the parent changes. Two fixes: (a) have the Vue component emit null to the form field when the parent watcher fires (check FormField.vue for a handleChange hook), and (b) defend on the server with a Rule::exists scoped to the parent, e.g. Rule::exists('cities', 'id')->where('state_id', $request->state_id).

Searchable + dependent. Nova's searchable() hits the associatable endpoint independently of your dependency filter. If you enable both, make sure the search endpoint applies the same scoping — otherwise search returns rows the cascade would have hidden.

Detail and index pages. The dependency only affects the form. On index/detail the field renders as a normal BelongsTo label, so don't expect the same filtering in read views; it's display-only.

Inline creation and modals. Nova's "create related" modal does not know about your cascade. If the field allows inline creation, hide it (->hideCreateRelationButton()) rather than ship a modal that ignores the parent selection.

Gotchas and Tips

  • This package has 2 stars and 0 dependents. Treat it as a reference implementation, not infrastructure. Before adopting, fork it into app/Nova/Fields/ or vendor it, pin the commit, and read FormField.vue end to end. The assessment score (~1.05) reflects a package that has not been battle-tested across Nova versions or edge cases.
  • Nova version compatibility is the first failure mode. A field compiled for Nova 3 will not load under Nova 4/5's asset pipeline without rebuilding. Check dist/ freshness and the laravel/nova constraint in composer.json; if it's ^2.0 or ^3.0, expect to recompile the JS yourself with the Nova-matching toolchain.
  • The dependency is client-side only. Nothing stops a crafted payload from writing a child ID that doesn't belong to the chosen parent. Always pair the field with a Rule::exists(...)->where(...) in the FormRequest or rules().
  • Watcher scope. dependsOn('state_id') only works if state_id is rendered in the same form and is visible. Fields hidden via canSee() or moved into a separate panel with conditional display won't trigger reliably. Confirm the event the Vue component listens to ($watch, Nova's field-changed bus, or a Nova.$emit) and that your layout preserves it.
  • Stale options after create. If the parent field is populated by a BelongsTo inline-create, the child endpoint may not yet include the new parent's children on the next tick. Force a refresh by keying the watcher on the parent value, not on mount.
  • Debugging checklist: (1) open DevTools → Network and confirm the request fires on parent change with the right parent ID and 200 status; (2) inspect the field's meta in the /nova-api/{resource} schema response to confirm dependsOn serialized; (3) temporarily log inside the Vue watcher to verify the value changes; (4) check the browser console for Nova is not defined — a sign the asset wasn't registered via Nova::script().
  • Endpoint auth. Any custom route the field calls must sit under the nova middleware group, otherwise the fetch returns the login redirect and the dropdown silently empties. This looks like a dependency bug but is almost always routing.
  • Performance. The cascade is one request per parent change; on lists of hundreds of parents, cache the pairs (Cache::remember('cities.'.$stateId, 3600, ...)) and invalidate on save via a model observer. Do not load every possible option on mount.
  • No BelongsToMany / MorphTo. The field is named BelongsTo for a reason; don't expect polymorphic or pivot-based relations to work. For those, write a custom field or fall back to a filtered relatable() query with a full form reload.
  • Extension point: the cleanest long-term move is to extend the PHP field class and override the Vue component ($component = 'your-dependent-select'), copying the existing component and adding the hooks you need (clear-on-change, debounce, caching headers). That keeps you on the package's API surface while owning the parts most likely to need changes.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings