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

Many To Many Laravel Package

armincms/many-to-many

A Laravel package that extends Eloquent many-to-many relationships with helpers for attaching, detaching, and managing related models through pivot tables. It aims to simplify common relationship workflows and offer a cleaner API for complex many-to-many handling.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm the prerequisite before reading anything else. This is a Laravel Nova form field for BelongsToMany relations, not a general Eloquent helper. If the project has no Nova license, no NovaServiceProvider, and no admin panel, there is nothing to integrate — the package is inert. Read the description as "a Nova field for editing many-to-many associations with pivot columns," not as a general relationship utility.

Read the source before running composer require. The README is a paragraph. Spend fifteen minutes on the repo instead:

  • composer.json — check the laravel/nova, laravel/framework, and php constraints. The last tag is 2023-02-20, which places it in the Nova 4 era. Nova 5 compatibility is unverified and is the single largest adoption risk.
  • src/ — the field class, its namespace, and which fluent methods actually exist. Do not assume parity with Nova's native BelongsToMany field; verify each method you plan to call.
  • dist/ — whether the compiled Vue bundle is committed. If it is not, you need an internal npm run build step in CI, which changes the cost of adoption materially.

Pin exactly, not with a caret.

composer require armincms/many-to-many
"armincms/many-to-many": "1.0.0"

A hard Composer constraint from this package can block a Nova upgrade across the entire admin panel, not just the resources using the field. An exact pin makes the blast radius explicit and reversible.

Define the Eloquent relationship first — it is the contract. The field is only the UI. Pivot columns must be whitelisted in withPivot() or they will silently fail to save:

// app/Models/Post.php
public function tags(): BelongsToMany
{
    return $this->belongsToMany(Tag::class)
        ->withPivot(['weight', 'assigned_at'])
        ->withTimestamps();
}

Register the field on one low-risk resource.

// app/Nova/Post.php
use Armincms\ManyToMany\ManyToMany; // verify FQCN against src/

public function fields(Request $request)
{
    return [
        ID::make()->sortable(),
        Text::make('Title'),
        ManyToMany::make('Tags'), // attribute must match the relationship method name
    ];
}

First use case: tagging. Pick the least consequential resource that has a simple pivot (tags, categories, segments). Do not start with roles or permissions — detaching the wrong pivot row there has authorization consequences. Get the tagging flow working end-to-end before anything else.

Smoke test before trusting it: open the edit form, attach a record, set a pivot value, save, reload. A blank field is almost never a PHP error — it is an asset/component mismatch.

Implementation Patterns

Pivot data is declared twice, and both declarations matter. The fluent field renders the inputs; persistence is governed entirely by withPivot() on the relation. Keep the two lists in sync and cover it with a test that asserts a pivot column round-trips through sync().

ManyToMany::make('Tags')
    ->fields(function () {
        return [
            Number::make('Weight', 'weight')->min(0)->max(100),
            DateTime::make('Assigned At', 'assigned_at')->nullable(),
        ];
    });

Keep the native field as a fallback path. Rollback is a one-line field swap, which is what makes this a cheap experiment. Adopt field-by-field at the resource level, never globally:

BelongsToMany::make('Tags')  // Nova native
ManyToMany::make('Tags')     // this package

Record in the repo which resources use which field. Without a written record, the first team rotation loses that knowledge and nobody knows what is safe to remove.

Use it where the delta over native is real. Good candidates: tagging with a weight, product↔category assignment with ordering, team↔member with a role pivot column, editorial sections with an assigned_at. Weak candidates: anything requiring audit trails, approvals, bulk operations, or import/export — build those with Nova Actions plus plain Eloquent rather than bending this field.

Set title() and $search on related resources explicitly. Nova resolves related records through the resource's title method. If the model has no name/title or the resource lacks a public static $title, you get IDs or blanks in the selector:

// app/Nova/Tag.php
public static $title = 'label';
public static $search = ['id', 'label', 'slug'];

Publish assets in the build step, not on the server.

php artisan vendor:publish --provider="Armincms\ManyToMany\ManyToManyServiceProvider" --tag=assets --force

Run it in a fresh CI container. A warm local machine hides missing publish steps because the files are already there from an earlier experiment.

Compose with Nova's own tooling. Keep the field inside fields(). For bulk attach/detach, write a Nova Action. Mixing responsibilities is how a small UI dependency turns into a fork you maintain full-time.

CI will not catch a broken field. The package is UI-only, so seeders, factories, and feature tests are unaffected — which also means nothing fails when the component stops mounting. Add a Nova/Dusk browser test that loads the detail page of the pilot resource, or at minimum a manual checklist item run after every Nova minor upgrade.

Debugging order when something breaks: blank field → check dist/ assets were published → check browser console for a missing Vue component → confirm the field class is registered in fields() with a name matching the relation method → confirm composer show armincms/many-to-many matches the pinned version. PHP-side errors land in storage/logs/laravel.log; front-end errors never will.

Gotchas and Tips

  • Blank field after a Nova minor upgrade is the classic failure mode. Nova 3 → 4 → 5 changed front-end internals materially, and a compiled Vue bundle can stop mounting without any logged error. Add "open the Post edit form and attach a tag" to the Nova upgrade checklist permanently.
  • Component name collisions. Nova field packages register global Vue components. If another many-to-many or tag field is already installed, verify the registered component name in the service provider before assuming the right one renders.
  • withPivot() is the whole ballgame. If pivot values save on create but not on update (or vice versa), look at sync() versus syncWithPivotValues() in the vendor class. Silent persistence failures are the highest-frequency bug with this kind of field.
  • Large related-record sets. Many-to-many selectors degrade badly without server-side search. Test against production-scale counts (thousands of tags) on staging — ten fixtures locally proves nothing.
  • Morph relations are likely unsupported. The name says many-to-many. If you need morphToMany, verify in src/ and plan a custom field if it is absent.
  • Detach ≠ soft delete. The field removes pivot rows outright. If the relationship carries business meaning (membership history, assignment records), you need a pivot model with SoftDeletes and a different UI. Discover that before launch, not after.
  • Eager loading / N+1. If you render pivot attributes per row, confirm the vendor class eager-loads, otherwise add a resource-level mitigation via relatableQuery() or indexQuery().
  • Treat extension points as source-first, not docs-first. 25 stars, 0 dependents, no release since Feb 2023, single maintainer. Assume you may need to fork. Record the fork point (commit SHA) and name an owner for Nova-upgrade triage before promoting beyond the pilot. MIT makes the fork clean legally; the cost is ongoing ownership of a small PHP + Vue component.
  • Keep a written record of: which resources use the field, the native-field fallback for each, the pinned version, the fork location, and the Nova-upgrade checklist line. Without it, this becomes silent technical debt the first time the team rotates.
  • Default position: native BelongsToMany everywhere unless a time-boxed spike proves a specific gap this package closes. Rollback is one line, which is exactly what makes it a cheap thing to authorize and a cheap thing to abandon.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource