dotmarn/laravel-simple-select
A lightweight Laravel package that makes building HTML select elements simple. Generate options from arrays or Eloquent models, handle selected values and placeholders, and keep your Blade forms clean with minimal boilerplate. Works with old input and validation.
The v1.0.0 tag is the package's first stable release, and the only change called out is Laravel v12 support. That makes this a good moment to adopt it: the public API is now covered by semver, so a ^1.0 constraint won't surprise you with breaking changes the way an unpinned 0.x requirement could.
composer require dotmarn/laravel-simple-select:^1.0
composer.json for the supported illuminate/* range and the minimum PHP version (Laravel 12 implies PHP 8.2+). If your app is still on Laravel 10/11, verify the constraint actually allows it rather than assuming — the release note only mentions 12.<select> in your app — usually a form with a belongsTo relation — and replace the hand-rolled @foreach ($roles as $role) <option value="{{ $role->id }}" @selected(old('role_id', $user->role_id) == $role->id)> block with the package's helper. That single swap exercises the three things you actually care about: option generation, selected-state resolution, and old-input handling. Once that works, roll it out form by form.Keep the changelog link (.../commits/v1.0.0) open while you onboard. Because this is the 1.0.0 milestone, read back through the 0.x tags too — the jump to 1.0.0 is where a maintainer normally banks up renames and signature changes, and the visible release note probably doesn't tell the whole story.
Controller-prepares-data, view-renders. The most maintainable pattern is to build the option set in the controller (or a form request / view model) and pass a plain array or collection into the view. This keeps queries out of Blade, makes the data easy to assert in feature tests, and means the package is only responsible for markup — so if you ever drop it, the swap is one line per template.
Model-backed options. For enum-ish tables, pluck only the two columns you need:
$roles = Role::orderBy('name')->pluck('name', 'id');
Being explicit about the select list avoids hydrating full models just to read a label, and it pairs naturally with whatever key/label signature the helper exposes.
Selected state resolution order. The rule you want in every form is: old input wins, then the persisted model value, then a default. Centralise that so no template reimplements it — if the package's helper takes the current value as an argument, pass old('role_id', $user->role_id) and let the package own the comparison. Watch for loose-vs-strict comparison: a string "3" from old() and an int 3 from the model must both match, which is exactly the kind of thing a helper exists to paper over.
Placeholders and empty options. Decide on a per-form basis whether an empty first option is a prompt ("Choose a role…") or a legitimate value ("Unassigned"). These behave differently on validation and on re-population after a failed submit; treat them as two distinct helpers or an explicit boolean flag in your own wrapper.
Wrap it once. Put a thin Blade component or helper of your own in front of the package — e.g. <x-form.select name="role_id" :options="$roles" :value="$user->role_id" />. That gives you one place to add @error class toggling, aria-invalid, required markers, and any app-specific styling, and it isolates your codebase from the package's API surface. This is the highest-leverage thing you can do with a small UI package.
Dependent / cascading selects. For country → region style forms, render the first select server-side and populate the second with a JSON endpoint or Livewire. The package handles the initial markup; it won't manage the async refresh, so keep that logic in your JS/Livewire layer and reuse the same option-shaping code in the endpoint as in the initial render.
Validation and errors. Pair every select with the matching @error('field') block, and assert in feature tests that a failed validation re-renders with the previously submitted value selected — that's the regression most likely to bite you after a refactor.
composer.json constraints on the 1.0.0 tag, and if you maintain a package or a multi-version app, add 12 to your CI matrix rather than trusting the changelog line.0.x version, diff the source or scan the README for renamed methods before bumping, and read the full commit list behind the link.$row->relation->pluck(...) is a classic. Eager-load or pre-pluck the option set once outside the loop.php artisan config:cache in deploy scripts will freeze it — remember to re-run on deploy, and don't read config at boot in a way that breaks under cache.bootstrap/cache/packages.php after a composer dump-autoload; stale discovery caches after a version jump are common and look like missing classes.^1.0 you get patch and minor updates safely; a fork means you own the Laravel 13 upgrade yourself."dotmarn/laravel-simple-select": "^1.0" is right for an app; for a library, widen to ^1.0 and test against the lowest supported Laravel so you don't quietly require 12-only APIs.How can I help you explore Laravel packages today?