alexbowers/nova-prepopulate-searchable
A Laravel Nova package that lets you prepopulate searchable relationship fields from URL query parameters, making it easier to create records with preselected BelongsTo values from links or filtered lists.
Requirements: Laravel with Nova installed (package last shipped Feb 2022, so assume Nova 3.x first, Nova 4.x with verification). PHP 7.4/8.0-era code.
Install:
composer require alexbowers/nova-prepopulate-searchable
No publish step or config file is documented; the package ships a field class plus its compiled Nova JS component. Confirm the README at your pinned tag for the exact class namespace and method names before wiring it into a resource.
Where to look first:
vendor/alexbowers/nova-prepopulate-searchable/README.md — the API surface is small, but method names are version-sensitive.src/ — the field class (extends Nova's BelongsTo) and any service provider.dist/js/ — the compiled Vue component; this is what actually replaces the stock searchable BelongsTo UI.First use case — nested / scoped create forms. The canonical scenario is a "create child from parent" screen: you already know the parent record (route model binding, query string, or tenant context), but a plain BelongsTo::make(...)->searchable() renders an empty search box because the option list is fetched by AJAX only when the user types. Prepopulating seeds the option list so the known record displays its title() immediately.
use Alexbowers\NovaPrepopulateSearchable\PrepopulateSearchable;
public function fields(Request $request)
{
return [
// exact method name per README; conceptually: provide the initial option(s)
PrepopulateSearchable::make('Author', 'author_id')
->searchable()
->prepopulate(fn () => User::whereKey($request->query('author_id'))->pluck('name', 'id')),
];
}
If that single use case is all you need, this package is a reasonable shortcut; if you need more, see the migration note in Gotchas.
1. Fixed record prepopulation (nested resource create). Pass the already-known model or a single-entry map. The user sees "Jane Doe" without searching, and the hidden author_id is set.
PrepopulateSearchable::make('Post', 'post_id')
->searchable()
->prepopulate(fn () => Post::whereKey(request()->route('post'))->pluck('title', 'id'))
->readonly(), // if the relation is fixed by the route
2. Closure driven by request context. Because the value is resolved at field-serialization time (per request), closures can read query strings, route params, or the authenticated user. Always scope:
->prepopulate(fn () => User::query()
->where('tenant_id', request()->user()->tenant_id)
->whereKey(request('author_id'))
->pluck('name', 'id'))
3. Tenant / ownership scoping as a habit. Prepopulation bypasses the normal search endpoint, so your relatableQueryUsing() rules are not applied to the seeded options. Mirror the same constraints in the closure, or you create a display-level leak (the label resolves for a record the user shouldn't see).
4. Combine with searchable() and relatableQueryUsing(). Keep the field searchable so users can change the selection; only the initial options come from the package.
PrepopulateSearchable::make('Author', 'author_id')
->searchable()
->relatableQueryUsing(fn ($request, $query) => $query->where('tenant_id', $request->user()->tenant_id))
->prepopulate(fn () => /* same scope */);
5. Shared builder for index/show vs. forms. Nova serializes fields for both the resource index and the detail form; the prepopulation payload is usually wasted on index. Guard the closure with cheap whereKey lookups rather than loading full lists, and prefer pluck('name','id') over get().
6. Update forms. On edit, Nova already knows the current value and resolves its label. Prepopulation matters mainly for create and for dependent/dynamic forms — verify the field still behaves on update in your Nova version, since older components sometimes overwrite the resolved value.
7. Display-only prepopulation. For immutable relations (a comment always belongs to its post), pair prepopulation with readonly()/disabled() so the UI shows the label but the user cannot re-search and swap parents.
app/Nova/Fields or packages/ rather than waiting for upstream.field.meta, component registration, field.choices/options handling), so a silent no-op render is the most likely failure mode — not a hard error.prepopulate()/prepopulateUsing() from memory; read src/ before writing resource code.relatableQueryUsing().whereKey + pluck, never Model::all(), and avoid loading relationships you don't display.[id => label] map and matches against the field's stored value, often as a string. Cast keys (->pluck('name','id') returns ints from many drivers; use ->mapWithKeys or string-cast if the component fails to select the initial value).title() mismatch. The label shown by prepopulation should come from the same attribute the related resource uses for title()/$title, otherwise the displayed name changes after the first search.dependsOn). Prepopulation is computed at serialization time; it will not react when a parent field changes. If the relation depends on another field's value, you need custom JS or a different approach — this package cannot do it.null should degrade to a normal empty searchable field; test that it doesn't render a stray blank option or throw./nova-api/... field payload: confirm the prepopulated entries actually appear in the field's options/choices meta. If they're present but not rendered, the issue is the Vue component vs. Nova version; if absent, it's your closure or method call.jsonSerialize()/resolve() to inject options yourself, or sidestep the package entirely:
Select with ->options(...) when the set is small and static;BelongsTo::make(...)->searchable()->default($id) plus a small custom field if you only need the value preselected;Select populated from the same scoped query is often simpler and survives Nova upgrades.Bottom line: good fit for the narrow "known related record should display immediately in a searchable BelongsTo" problem, with a short implementation. Treat it as archived code you own: pin, scope all closures, and plan a fork or an inline replacement before your next Nova major upgrade.
How can I help you explore Laravel packages today?