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

Nova Multiselect Field Laravel Package

marshmallow/nova-multiselect-field

A Laravel Nova field that lets you attach multiple selectable options to a resource in one clean input, with searchable choices and a simple API for defining options and storing selected values as arrays.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a single-field Nova package: it provides one custom field class (a multi-value select) plus a compiled Vue component. Treat it as a small dependency, not a framework — read the source before you rely on it. With 0 stars, 0 dependents, and a single maintainer, you should plan to pin the version and keep a fork within reach.

Install and inspect

composer require marshmallow/nova-multiselect-field

Then look at, in order:

  1. vendor/marshmallow/nova-multiselect-field/composer.json — confirm the laravel/nova constraint and PHP minimum against your app. This is the number one reason these packages silently break.
  2. vendor/marshmallow/nova-multiselect-field/src/ — locate the field class (typically Marshmallow\NovaMultiselectField\Multiselect) and check whether it extends Field or Select. That tells you whether ->options(), ->displayUsing(), and ->rules() behave like a normal Nova select.
  3. vendor/marshmallow/nova-multiselect-field/dist/js/ — if a compiled field.js exists, no npm run dev step is needed. If it's absent, you'll have to build the package's frontend yourself, which is a red flag worth catching on day one.
  4. README.md for the documented ->options() / ->multiple() signature — the method names are the contract, and you should not guess them.

First use case: replacing a comma-separated tags column or a checkbox group. Create a JSON column first:

// migration
$table->json('regions')->nullable();
// app/Models/Project.php
protected $casts = ['regions' => 'array'];
// app/Nova/Project.php
use Marshmallow\NovaMultiselectField\Multiselect;

Multiselect::make('Regions')
    ->options([
        'emea' => 'EMEA',
        'apac' => 'APAC',
        'amer' => 'Americas',
    ])
    ->rules('array')
    ->help('Select every region this project ships to.'),

Smoke-test by saving two values, reloading the detail view, and inspecting the raw column (select regions from projects). You want ["emea","apac"] — not "emea,apac" and not "[\\"emea\\"]". Confirm which shape the field writes before building anything on top of it.

Implementation Patterns

Static option sets are the sweet spot. Regions, tags, permissions, feature flags, notification channels. If the options live in a database table with their own attributes, use a BelongsToMany relationship field instead — a multiselect storing IDs you can't join against will hurt when you need ordering, counts, or admin CRUD for the options.

Options from a model or enum. If the field accepts a callable, this is the cleanest way to keep a single source of truth:

use App\Enums\Region;

Multiselect::make('Regions')
    ->options(fn () => collect(Region::cases())
        ->mapWithKeys(fn ($c) => [$c->value => $c->label])
        ->all());

If it only accepts an array, build that array in a small Options helper class so the same list feeds Nova, validation, and the filter.

Validation. Nova applies rules to the resolved value, so array rules are mandatory:

->rules('array')
->rules('min:1')
->rules(fn () => Rule::in(array_column(Region::cases(), 'value')))

Without ->rules('array'), a in: rule can blow up with "Array to string conversion" on submit.

Keep the model cast and the field in sync. The single most common failure mode is a field writing JSON while the model has no array/AsArrayObject cast (or vice versa: a comma-separated column with an array cast). Add a feature test that saves through the Nova endpoint and asserts the stored value.

Index and detail display. JSON arrays don't render nicely by default:

->displayUsing(fn ($value) => collect($value ?? [])
    ->map(fn ($v) => Region::from($v)->label)
    ->join(', '))

Filtering. You'll need a custom Nova filter; there's no built-in search for JSON columns:

public function apply(Request $request, $query, $value)
{
    return $query->whereJsonContains('regions', $value); // MySQL 5.7+, Postgres jsonb
}

For Postgres json (not jsonb), or SQLite in local dev, whereJsonContains behaves differently or isn't available — write the filter defensively and test against your real database engine.

Conditional options. If the field supports Nova's ->dependsOn(), use it for dependent selects (country → region). If it doesn't, the options won't recompute on the client and you'll need to render the field only when the parent is set, or drop to two separate fields in a Flexible/Repeater.

Escape hatch: subclass it. Rather than patching vendor/, extend the field and override resolve() / fill() to normalize nulls and empty arrays to a shape the rest of your app understands:

class Regions extends Multiselect
{
    public function resolve($resource, $attribute = null)
    {
        parent::resolve($resource, $attribute);
        $this->value = $this->value ?? [];
    }
}

Gotchas and Tips

  • Version compatibility is the whole risk. Nova 4 vs Nova 5 means Vue 2 vs Vue 3 and different asset pipeline expectations. Check the composer.json constraint and the compiled field.js before upgrading Nova — a 0-dependency field can't be assumed to keep pace with Nova majors.
  • Zero dependents means zero social proof. No Stack Overflow answers, no GitHub issues from other users. Keep a composer.json "repositories" fork entry ready, and pin marshmallow/nova-multiselect-field to an exact version in composer.json rather than a caret range.
  • Confirm the storage format early. JSON array, comma-separated string, and serialized PHP are all plausible for a package like this, and the README may not say. Save once and inspect the column; everything downstream (filters, exports, API resources) depends on that answer.
  • Normalize empties. Decide whether "nothing selected" is null, [], or '' and enforce it in a mutator. Mixed empties are a recurring source of implode() warnings in resources and false positives in whereJsonContains.
  • Don't sort on it. Ordering by a JSON/CSV column is meaningless. If users need sortable tags, that's a pivot table.
  • Assets. If the field renders blank or unstyled, it's almost always a missing compiled component. Run php artisan nova:publish and hard-refresh; if dist/js/field.js doesn't exist in the vendor directory, you must build it.
  • Debug the payload, not the PHP. On save, open the browser network tab and inspect the PUT /nova-api/... body. That instantly tells you whether the component is sending ["emea","apac"], a single string, or a nested object — far faster than adding dd() to the field.
  • Testing. Cover it with one Nova endpoint test (save → assert raw column → assert model cast) and one filter test. That's enough to catch every upgrade break on this package, which is proportional to its size.
  • Extension points are limited. Expect options(), rules(), displayUsing(), and whatever Select/Field inherits. Don't assume dependsOn, searchable, or sortable work — verify in src/ before designing a form around them.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager