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 Prepopulate Searchable Laravel Package

genealabs/nova-prepopulate-searchable

Nova Prepopulate Searchable pre-fills Laravel Nova's searchable select fields with their current values, so BelongsTo and similar relationships display the selected option correctly on create and edit forms instead of showing up blank.

View on GitHub
Deep Wiki
Context7

Getting Started

What it actually does. Nova's searchable() relationship fields (BelongsTo, MorphTo) normally resolve their selected option through an async request to the resource's search endpoint. On a plain edit form Nova hydrates the value for you, so it looks fine. The gap appears on create forms and on deep links: the field has an ID (from a ->default(), a query parameter, or a parent resource context) but renders as an empty search box, so the user re-searches for a record the app already knows about. This package subclasses the Nova field to hydrate the option list and selected label on first render.

Prerequisites — read these before installing.

  • Laravel Nova is required (paid, separately licensed). The package is meaningless outside a Nova app.
  • Last release 3.0.0 (2022-02-08) added only Laravel 9 compatibility. Treat Laravel 9 / Nova 3–4 era as the effective ceiling.
  • The repo is archived. There are no security patches, no issue triage, and no future Nova support. Before you write a single field, decide whether you are installing it or absorbing it — see Gotchas.

First place to look. Don't start with the README; it's thin. Start with the source in vendor/genealabs/nova-prepopulate-searchable/src/ and composer.json:

composer show genealabs/nova-prepopulate-searchable   # declared constraints
ls vendor/genealabs/nova-prepopulate-searchable/src  # the field class(es)

The whole package is one field class extending Nova's BelongsTo. Read it end to end — it's short — and note (a) the exact namespace, (b) which method it overrides to hydrate the label, and (c) whether it reads request input itself or expects you to pass values in. Everything in the rest of this document assumes you've confirmed those three things.

First use case. A Post resource whose author (or team, or category) is effectively required and usually the same record. Swap the field so the relation arrives pre-selected instead of blank:

use Laravel\Nova\Fields\BelongsTo;
// use GeneaLabs\NovaPrepopulateSearchable\BelongsTo;  // verify exact namespace in src/

public function fields(Request $request)
{
    return [
        // ... title, slug, body
        BelongsTo::make('Author', 'author', User::class)
            ->searchable()
            ->default(fn () => $request->user()->id),
    ];
}

Smoke-test on staging with production-shaped data: create form, edit form, and the deep-linked variant. A clean composer require proves nothing about whether the field renders on your Nova major — the frontend component is the most version-sensitive part of Nova and nobody has validated this package against a post-2022 Nova.

Implementation Patterns

Field swap, not global config. There is no service provider to register, no middleware, no schema change. The package's entire blast radius is the field classes in your fields() methods — which is exactly why removal is also cheap. Adopt it per-field and keep a list of every resource that uses it; you'll need that inventory the day you upgrade Nova.

Deep-link hydration (the highest-value pattern). Launch "Create Post" from a filtered User index or a User detail page and carry the parent through the query string:

// User detail page / index toolbar
Linking to: /resources/posts/new?viaResource=users&viaResourceId={$user->id}&viaRelationship=posts

Nova populates viaResourceId for related-resource creation, but for a plain BelongsTo not declared as the inverse of a HasMany, that context is lost and the field sits empty. Hydrating from viaResourceId (or your own query param) here removes a search round-trip on the most common admin flow. Also use it for ?user_id=42-style links from custom tooling and dashboards.

Only prepopulate when there's nothing to preserve. The failure you're guarding against is clobbering user intent. Order of precedence should be: old input from a failed validation → existing model value → query string / default. Nova handles most of this, but if you hand-roll hydration, check $request->old() and $resource->exists first:

->default(function ($request) {
    if ($request->old('author')) return $request->old('author');
    if ($request->viaResourceId && $request->viaResource === 'users') {
        return $request->viaResourceId;
    }
    return null; // let the user choose; don't guess on update forms
})

Don't double up on edit forms. Nova already hydrates the selected value on update. Prepopulating there is redundant at best and, if your logic is unconditional, can silently swap an existing relation for the default. Scope the behavior to $resource->exists === false unless you specifically want a bulk-create tool.

Pair it with narrowing, not with authorization. Hydration only controls what's displayed; it must not bypass Nova's relatable query. If you scope BelongsTo with relatableQuery/searchable callbacks (tenant scoping, only-active records), your prepopulated ID must live inside that set — otherwise the field renders blank or an inconsistent label and the user can't tell whether it's set. Make the prepopulation source read from the same scope:

BelongsTo::make('Team', 'team', Team::class)
    ->searchable()
    ->relatableQueryUsing(fn ($request, $query) => $query->where('org_id', $request->user()->org_id))
    ->default(fn ($request) => $request->user()->team_id)   // guaranteed in-scope

Test the hydration path, not just the install. A feature test asserting the create form returns the expected field meta, plus one Dusk/browser assertion that the selected label is visible, is the difference between "renders" and "works". This is the single most valuable thing you can add, because the package's dominant failure mode is silent.

Forking is the supported path. If you need this across more than one or two fields, copy src/ into app/Nova/Fields/ (or an internal package) under your own namespace, keep the MIT header, and add a method rather than editing behavior in place. One day of work, a named owner, and the upgrade question becomes "does our field still work" instead of "is upstream alive".

Gotchas and Tips

Silent hydration failure is the real risk. The field can render perfectly and still fail to resolve the selected option, showing an empty relation. A user saves the form, the relation is null, and you have a data-integrity defect that looks cosmetic in the UI and won't throw an exception. Any resource where the relation is genuinely required belongs behind a required validation rule and an integration test — don't rely on the field's good behavior.

Nova version coupling is the brittle axis. Nova field classes bind to Nova's internal field API and compiled frontend assets. A Nova major upgrade can break the component with no upstream fix forthcoming. Pin the package and pin Nova together in your composer.json, and never schedule this field's introduction in the same window as a Nova or Laravel major — land it on a known-good baseline so a failure is unambiguous.

Composer constraint drift. An archived package's declared constraints often resolve "creatively," pulling unrelated upgrades along as collateral. If Composer wants to move other packages to satisfy it, that's the signal to vendor instead of require. Check composer show --tree after install.

->default() alone may not be enough. In some Nova versions, default() on a searchable BelongsTo sets the form value but doesn't resolve the display label — which is precisely the gap this package fills. Verify the rendered label, not the submitted value; the submitted ID can be correct while the UI shows blank, and vice versa.

Failed validation repopulation. After a validation error, Nova repopulates from old input. If your hydration logic runs unconditionally on every request, it can override what the user picked. Guard with $request->old(...) and $resource->exists as shown above, and test a deliberately failed submit.

Watch for an extra query per field. Hydration means the label is resolved server-side on first render instead of via async search. That's the intended trade — one query up front versus a click-plus-AJAX later — but if a resource has several searchable relations, it adds up on index-adjacent create screens. Keep it to fields that actually need it.

Authorize the record, not just the field. If prepopulation can be driven by a query parameter (?user_id=42), you've created a user-controlled input path into a relation. Hydration shouldn't grant anything the user couldn't have selected manually, but make sure the value is validated against the relatable query and that authorizeToSee / field-level policies are still applied. Untrusted query-string IDs feeding a field is an easy way to surface records a user shouldn't see in the search dropdown.

Exit plan before you enter. Record in the PR or an internal note: Nova version pairing, declared Laravel/PHP constraints, every resource and field using it, the named owner, and the staging verification evidence. When the next Nova upgrade lands, that note turns a two-day rediscovery into a one-hour fix — and it's the artifact that lets you say "remove it" with confidence.

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