webparking/nova-belongs-to-dependency
A Laravel Nova package that makes BelongsTo fields depend on other form fields, enabling cascading or filtered selects. Choose a parent value and the related BelongsTo options update accordingly, keeping Nova forms dynamic and consistent.
What is being bought. webparking/nova-belongs-to-dependency is a single-purpose Laravel Nova field that turns a native BelongsTo into a dependent select: the child field's option list is narrowed by the current value of a sibling field on the same form. It replaces the custom Vue field a team would otherwise write for Country → Region → City, Client → Project → Task, Category → Subcategory. That is the entire value proposition — there is no reporting layer, no workflow engine, no multi-tenant option plumbing.
Architecture fit. The fit is good where the requirement is narrow and the stack is standard:
belongsTo chains, back-office create/update forms, two-level cascades (roughly 90% of real usage) and simple three-level chains.BelongsToMany, MorphTo, non-Eloquent option sources, custom search endpoints, or heavy multi-tenant scoping. Those should be scoped as custom work — the package cannot be bent into them.relatable() / relatableQueryUsing() constraints and Nova's searchable() behavior keep working. This matters for a TPM because it means adoption is a field-level change, not an architectural one. No service layer, no schema, no API contract changes.Integration feasibility. Low effort. Public API is effectively one make() plus one dependency method (->dependsOn('country')). A competent Laravel developer integrates a two-level cascade in hours, not days. The real cost is not writing the field — it is the surrounding discipline: declaration order, validation mirroring, modal field sets, asset cache clearing, and post-deploy verification. Budget roughly 1–2 developer days for the first cascade including tests and a deployment runbook, and materially less per additional cascade once the convention is established.
Technical risk. Manageable but real, and concentrated in two places:
fields(), not the foreign-key column. ->dependsOn('country') pairs with BelongsTo::make('Country', 'country', ...). A mismatch produces a silently empty dropdown — no error, no alert. This is the single most likely first-integration stumble and it is a configuration error, not a package bug.Rule::exists('states','id')->where('country_id', $this->country?->country_id). Without it, a crafted request or stale form post persists an invalid pairing. If this is not in the acceptance criteria, the feature ships as a UX nicety rather than a control.Secondary risks: the field reads sibling form state, so it misbehaves anywhere the parent is absent (action modals that list only the child field, parents hidden via onlyOnDetail() or placed in a different grouping). Stale compiled assets are the dominant deployment failure mode — "component not resolving" is almost always cache, not code.
Key questions to resolve before committing:
belongsTo cascades on internal admin screens, or does anything in scope need BelongsToMany/MorphTo/custom sources? If the latter, this is not the package for that requirement.2.0.1 (component-loading fix) actually resolve whatever workaround, if any, we would otherwise apply? Treat ^2.0.1 as the floor, not a feature upgrade — nothing in the field API changed.BelongsTo plus manual/validated entry) and the switch procedure.Stack fit and compatibility gates. Confirm Nova 4/5, current Laravel and PHP versions, and that any prior install of this package is either absent or upgraded. Pin webparking/nova-belongs-to-dependency:^2.0.1 in composer.json and verify the constraint landed. 2.0.0 carries the component-registration defect and surfaces as blank index/detail columns and Failed to resolve component console warnings — do not accept a build on 2.0.0, and remove any local workaround that manually registered the component or forced ->component(...) before trusting the fix.
Migration path. Adoption is additive and reversible, which is why it is a low-risk sequencing choice:
fields() on the affected resources. No schema, no data migration, no route or policy changes.Nova::script() / Nova::style(). If anyone previously ran vendor:publish for this package, delete the stale published copies so the provider's registration is authoritative.Sequencing recommendation:
2.0.1 renders in index/detail without ->onlyOnForms().Rule::exists(...)->where(parent_fk, ...) validation and a Nova feature test for create/update asserting both valid and invalid pairings. Decide nullable explicitly on every child, since partial saves and "clear the parent" flows will hit it.fields(), largest child tables first with ->searchable() + relatableQueryUsing() so the dropdown is server-side search rather than a giant option list.Where to look first, in order. README usage block (effectively the whole public API — a minute's skim eliminates rework on the method signature) → src/BelongsToDependency.php (which sibling attribute it reads, how it composes the options query; this is the file forked if customization is ever needed) → the service provider (Nova::script()/Nova::style()) → the three Vue components (one read-through explains every "why is it empty in a modal" question).
Extension stance. Customize by subclassing BelongsToDependency and overriding option-query building, then registering the subclass in fields(). Do not patch vendor code — that forecloses the upgrade path on a package whose maintenance cadence is already the primary risk.
Maintenance and support. This is the dominant total-cost-of-ownership item, not the integration. Last release 2023-02-15, 11 stars, 0 dependents, small contributor base, MIT licensed, no vendor SLA. Nova major releases will likely outpace package maintenance. Mitigations that must be in place before rollout:
^2.0.1) and no floating versions; re-validate index, detail, form, and modal rendering on every Nova upgrade.Failure modes and how they surface. Triage quality depends on knowing these up front:
| Symptom | Most likely cause | First move |
|---|---|---|
Blank index/detail columns, Failed to resolve component |
Stale assets, or build on 2.0.0 |
view:clear, config:clear, restart Octane/FrankenPHP, hard refresh; confirm ^2.0.1 |
| Child dropdown silently empty | Wrong dependency argument (column vs. field attribute), or parent declared after child | Compare dependsOn(...) against the parent's make() second argument and field order |
| Create works, edit empty | Parent attribute wrong, or parent not in the current field set | Check parent presence in fields(); inspect the child's lookup request payload |
| Empty/full list inside an action modal | Parent field not in the modal's field set | Include the parent, use Action::standalone(), or precompute in PHP |
| Previously selected value renders empty / drops on save | Global scope or soft delete on the child | withTrashed() / relax scopes inside relatableQueryUsing() |
| Invalid pairing persisted | UI restriction with no server-side rule | Add Rule::exists(...)->where(parent_fk, ...) |
The general debugging heuristic worth writing into the runbook: watch the network tab for the child field's option request and compare the parent value it carries. That splits the problem in one step — "parent state never sent" (form/order/scope) versus "query returned wrong rows" (resource or relatableQueryUsing).
Scaling. Package runtime overhead is negligible; the scaling variable is option list size. A child table with tens of thousands of rows is a bad dropdown regardless of package. Standard posture: ->searchable() on the child, an index on the foreign key being filtered, and relatableQueryUsing() to constrain the candidate set. Because the dependency filter composes with the resource's relationship definition, this is configuration, not a package limitation.
Ramp-up. Low. A developer already fluent in Nova resources needs the README plus one read of the field class — call it half a day to first working cascade, including the gotcha list. The knowledge that actually matters is the operational envelope: dependency argument semantics, declaration order, the validation mirror, modal field sets, and the deploy cache sequence. Put those five items in the team's Nova conventions doc rather than leaving them in the package source; the next developer will not read it.
Verification and release hygiene. Feature-test the Nova API for create and update asserting both valid and invalid pairings. The reload itself is client-side and will not be exercised by HTTP tests — cover it with Dusk or a short manual QA checklist (create, update, index, detail, one modal). Add a smoke check post-deploy: a page that renders the form with no browser console errors is the cheapest regression signal available after any asset-touching change.
Recommendation. Adopt, conditionally: pinned at ^2.0.1, scoped to internal admin cascades over standard Eloquent belongsTo, with a named owner, mirrored server-side validation, a documented deploy cache sequence, and a documented fork/fallback path. Do not adopt for business-critical admin workflows or any requirement involving BelongsToMany, MorphTo, or non-Eloquent option sources without separately scoped custom work.
How can I help you explore Laravel packages today?