kayacekovic/belongs-to-many-field
Laravel package adding a belongs-to-many form field for attaching and detaching related models through a pivot table. Search, select, and manage associated records directly from the form without leaving the page, with customizable labels and query constraints.
Install the package like any Nova field:
composer require kayacekovic/belongs-to-many-field
Before you write a single line of resource code, do two things:
Laravel\Nova\Fields\BelongsToMany field that handles attach/detach, pivot fields, searchable relations, and authorization hooks. This package is a third-party reimplementation of that same concept (0 stars, 0 dependents, no listed Nova version constraints on the README). Your first task is to answer: what does this do that the core field doesn't? If the answer is "nothing," uninstall it — you are taking on upgrade risk against Nova major versions for zero functional gain.vendor/kayacekovic/belongs-to-many-field/src/. You are looking for three things: the field class name and namespace (it is almost certainly Kayacekovic\BelongsToManyField\BelongsToMany or similar — confirm, don't guess), whether it extends Laravel\Nova\Fields\Field or the core BelongsToMany, and whether dist/js/field.js is committed. That last point determines whether you can use it at all without building assets yourself.Your first use case is the classic one: a User resource with a roles() belongsToMany, where an admin needs to attach/detach roles on the detail page and optionally edit pivot columns (e.g. assigned_at, is_primary). Register it in fields():
use Kayacekovic\BelongsToManyField\BelongsToMany; // verify actual namespace
public function fields(NovaRequest $request)
{
return [
// ...
BelongsToMany::make('Roles')
->searchable()
->rules('required', 'array'),
];
}
If that renders and saves, you're done with getting started. If the assets 404 or the field renders blank, jump to the Gotchas section — asset mismatches are the most likely failure mode for a low-traffic package.
Treat it as a drop-in for the core field until proven otherwise. Because the API is modeled on Nova's BelongsToMany, most of your existing patterns port over. Write your resource code against the core Nova interface (searchable(), display(), nullable(), relatable()) rather than any package-specific methods. That way, if the package dies, you swap one use statement and move on.
Pivot attribute editing. The valuable part of any belongsToMany field is the pivot form. Use the closure form so the fields resolve lazily:
BelongsToMany::make('Teams')
->fields(function () {
return [
Text::make('Role')->default('member'),
Boolean::make('Primary'),
];
}),
For this to work your relationship must declare ->withPivot('role', 'is_primary'), and if you need model events on the pivot you need ->using(PivotModel::class) on the relation itself — the field cannot do that for you.
Constrain the relatable query per-request, not globally. Multi-tenant apps need this constantly:
BelongsToMany::make('Projects')
->relatableQueryUsing(fn ($request, $query) => $query->where('tenant_id', $request->user()->tenant_id)),
Cascading selects. Pair it with dependsOn() so the option list narrows after a parent field changes; this is the standard pattern for "pick a client, then pick which of that client's projects."
Authorization belongs in policies, not the field. The field respects attachAny / detachAny / updateAny abilities on the related model's policy in core Nova. Verify the package does too — if it doesn't, you have a silent privilege-escalation surface where users see attach buttons they shouldn't, or worse, the request succeeds server-side.
Testing. Cover the pivot write path with a feature test that posts to the Nova resource update endpoint and asserts the pivot row via $model->pivot or assertDatabaseHas. Do not rely on Dusk tests alone; the JS component is the least trustworthy part of a package like this.
sync semantics differ subtly between implementations. If you see SQLSTATE[23000] on save, check whether the field is calling attach where it should call syncWithoutDetaching.composer.json and add a smoke test that loads the resource detail page — this is your canary for Nova upgrades.public/vendor/kayacekovic/belongs-to-many-field/ (or wherever the service provider publishes to). Many small Nova packages expect you to run npm install && npm run prod inside the vendor directory, which is fragile — some omit the build step from the README entirely. Confirm dist/js/field.js is committed; if not, budget time to build it.showOnIndex is a trap. belongsToMany is a detail-form field. On index it typically renders a comma list or nothing. If you need it there, use ->displayUsing() to produce a compact string, and be aware that each row triggers lazy-loading of the relation — N+1 on a 50-row index.->rules('required', 'array') on a belongsToMany with existing pivot rows can fire validation errors on detach-all. Test the "remove every related model" path explicitly; it's the most common bug report for fields of this shape.resolveUsing / fillUsing let you override read and write behavior without forking. If you need behavior the package lacks, subclass it in app/Nova/Fields/ and fix the use statement in one place — don't scatter the vendor namespace across fifty resources.fill/resolve methods (temporarily, in vendor/) to confirm what the request payload looks like versus what the field expects. The JS component sends a specific shape (array of IDs plus pivot objects); a mismatch here produces a silent no-op with a 200 response, which is the worst kind of bug.Bottom line: this package occupies space already filled by a first-party Nova field. Adopt it only if you have verified a concrete capability it adds, and isolate it behind your own field class so the exit cost is one line of code.
How can I help you explore Laravel packages today?