martinjuul/many-to-many
martinjuul/many-to-many is a Laravel package for simplifying many-to-many relationships. It offers convenient helpers for attaching, detaching, syncing, and managing pivot data, making related model interactions cleaner and easier to maintain.
Because this is a low-traffic (0 stars, 0 dependents) Nova field, treat the README as the source of truth and verify the API against src/ before adopting it. The steps below are the expected shape for a Nova many-to-many field; adjust class names/path to what the package actually ships.
Install into an existing Nova app
composer require martinjuul/many-to-many
Confirm the package's composer.json allows your Nova major version (Nova fields are tightly coupled to Nova 3/4/5 APIs).
Look here first
README.md — usage snippet and any publishing/config options.src/ — the field class and any ServiceProvider; check whether it registers assets, config, or macros.resources/js/ (if present) — tells you whether it's a custom Vue field or a thin wrapper over Nova's BelongsToMany.composer.json — require constraints against laravel/nova and php.First use case — a BelongsToMany on a resource
use MartinJuul\ManyToMany\ManyToMany; // verify namespace from src/
public function fields(NovaRequest $request): array
{
return [
// ...
ManyToMany::make('Tags', 'tags', Tag::class),
];
}
Smoke test
select * from tag_post).If the README examples do not run cleanly against your Nova version, stop and evaluate Nova's built-in BelongsToMany instead — the cost of debugging an unmaintained field usually exceeds the benefit.
Standard attach/detach on a resource. Use the field in place of Nova's native BelongsToMany when you need whatever enhancement it advertises (often searchable labels, inline creation, or pivot attribute editing). Keep the relationship method standard on the model:
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class)->withTimestamps();
}
Searchable / async options. For large related tables, prefer Nova's searchable mode if the field supports it:
ManyToMany::make('Tags')->searchable()
Searchable pulls options via the relationship's query; ensure the related resource is registered in NovaServiceProvider::$resources.
Pivot attributes. If the field renders pivot columns, define them on the relationship with withPivot() and, where required, withPivotValue(). Only expose pivot fields you actually validate — Nova does not enforce pivot rules for you.
Policy-aware usage. Gate attach/detach actions with related policies where possible. If the field bypasses Nova's attachAny/detachAny checks, add explicit authorization in the resource or a custom BelongsToMany subclass.
Nullable vs. required. For optional relationships use ->nullable() on the field and ->nullOnDelete() on the FK (N/A for pivot, but relevant for polymorphic variants). For required relationships, add ->rules('required', 'array') — Nova's field rules run against the submitted ID list.
Polymorphic variant. If the package ships a morph field, use it for MorphToMany relations and confirm the morph_type column is filtered correctly in the query. Test both sides of the morph map.
Compose with other Nova fields. Many-to-many is often paired with a KeyValue/Text field for pivot metadata. Keep pivot editing on the Update/Detail views only unless you've verified inline editing works in the index view.
Migration / data safety. Before swapping from the native BelongsToMany field to this one, dry-run both on the same resource. The sync behavior (sync vs syncWithoutDetaching, pivot data retention) is the most likely place for silent divergence.
0 dependents and 0 stars, there is little evidence of testing against recent Nova releases. Check composer.json require and the last commit date before adopting.vendor:publish doesn't surface the assets, run npm run prod against resources/js inside the package, or inspect NovaServiceProvider for Nova::script(...)/Nova::style(...) registration.request()->all() in a temporary middleware if attach/detach appears to no-op.withPivot(), pivot attribute editors render but save nothing. Mirror the withPivot list exactly between the model relation and the field configuration.update. Nova saves fields in declaration order; a many-to-many field placed before fields that mutate the parent model can sync against a stale record. Place relationship fields last when dependencies exist.indexQuery, global scopes can hide valid options. Test with a policy-restricted user; the option list will shrink silently.nova:field stubs or dump the field's resolved config via Nova::resources() and dd() on the field instance to confirm what options actually reach the front end.BelongsToMany wrapper may be the safer long-term choice.How can I help you explore Laravel packages today?