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

Belongs To Many Field Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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:

  1. Diff it against what Nova already gives you. Nova ships a first-party 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.
  2. Read the source. Open 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.

Implementation Patterns

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.

Gotchas and Tips

  • Duplicate pivot rows / unique constraint violations. 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.
  • Nova major version drift is the real risk. Nova 4+ moved to Vite and a different asset pipeline than Nova 3's Mix build. A package with zero dependents and no CI is unlikely to have been tested against the version you're on. Pin the package version in composer.json and add a smoke test that loads the resource detail page — this is your canary for Nova upgrades.
  • Assets missing or stale. If the field renders as an empty div, check 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 interact badly with empty state. ->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.
  • Extension points. 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.
  • Debugging a failed save. Log inside the field's 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.

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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log