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

Laravel Selectable Laravel Package

ringlesoft/laravel-selectable

Laravel Selectable generates HTML select options directly from Laravel collections using a fluent, chainable API. Customize labels and values with strings or closures, set selected/disabled options, add data attributes, grouping, and export arrays for AJAX or SPAs.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. This is a presentation-layer convenience package, not infrastructure. It registers collection macros (toSelectable(), toSelectOptions(), toSelectItems()) that turn an Eloquent/Support collection into an HTML <option> string or an array of selectable items. It fits a Blade-first, server-rendered Laravel application where forms are authored in .blade.php files and options are derived from models or config collections. It fits poorly with an API-first / Inertia / Vue or React frontend where markup should be produced client-side — there the only relevant surface is toSelectItems(), which is a thin mapping we could write in ~30 lines. It also overlaps directly with Laravel's native @selected() / @disabled() Blade directives and with a form-component library (e.g. Blade UI kit), so adopting it means standardizing on one idiom across the codebase or tolerating two.

The design is idiomatic (fluent builder, closure-or-string field selection, Collection pass-through including groupBy()), and the toSelectItems() escape hatch is the strongest architectural decision because it decouples the package from markup for SPA/AJAX consumers.

Integration feasibility. Very high. Single composer require, no config, no publishable assets, no migrations, MIT license, additive-only — it cannot break existing rendering because nothing calls it until we do. Cost of a spike is measured in hours, not days. Auto-discovery via a service provider is implied but not documented; that is a five-minute verification.

Technical risk.

  • Escaping / XSS (highest). The documented usage is raw Blade output: {!! $users->toSelectOptions() !!}. If labels derive from user-controlled columns (names, titles, org names), the package must escape internally. The README never states that it does. This is a must-verify before any use on non-admin surfaces; if it does not escape, we either wrap output ourselves or restrict usage to trusted label sources.
  • Type coercion on selected. Default value is id (int), while docs also demonstrate uuid (string) and int arrays. Loose vs. strict comparison in selection matching is a classic source of "the wrong role is preselected" bugs, which are silent and high-consequence in permission/payment forms.
  • Documentation quality as a coverage signal. The README's disabled example uses $item->status = 'inactive' (assignment) rather than a comparison, and one snippet has a stray brace (!!}}). Sloppy docs on the primary feature page suggest thin review of test coverage around disabled/selected semantics.
  • Macro namespace surface. Global collection macros are shared state; collisions with other macro packages or first-party macros are possible and fail at runtime, not compile time. Static analysis (Larastan/PHPStan) will likely not see the macros without the shipped IDE helper.
  • Sustainability. 5 stars, 0 dependents, one maintainer, a low opportunity score. The codebase is small enough to vendor, which materially defuses this risk — but we should price in owning it.
  • Encouraged anti-pattern. Both the README and the derived guides demonstrate Model::all() inside Blade. A note discourages it, but the examples teach it. Left unchecked, this becomes N+1 queries and full-table loads in views.

Key questions.

  1. Does toSelectOptions() escape labels/attribute values, or emit raw model data into HTML?
  2. What are the enforced php and illuminate/support version constraints, and is Laravel 11/12 covered by the CI matrix?
  3. Exact matching semantics for withSelected() — strict or loose, and how are int/string IDs reconciled?
  4. Is macro registration conditional/idempotent, and can it be disabled or namespaced?
  5. Test coverage and CI status; is there any test asserting escaping or groupBy() nesting?
  6. How are closures that throw, or return null, handled during render — silent skip or exception in the view?
  7. Is there a documented upgrade path / semver policy for breaking changes?
  8. How does grouped output interact with selected when the collection is re-keyed by groupBy()?

Integration Approach

Stack fit. Best fit: Laravel 10–12, PHP 8.2+, Blade-rendered admin and internal tooling forms — settings pages, filters, assignment dropdowns, exports. Acceptable: Livewire/Alpine screens consuming toSelectItems() as JSON. Poor fit: public-facing forms with user-supplied labels (until escaping is confirmed), and any Inertia/SPA frontend, where the package reduces to a mapper we should own internally.

Migration path. Purely additive; no data or schema migration. Adopt in a branch, pin an exact version in composer.json, and immediately place an internal seam — a small App\Support\SelectOptions wrapper or a Blade component — so every call site goes through our own API. That makes the exit (drop the dependency, or replace with a @foreach) a single-file change, and lets us add escaping centrally if the package does not.

Compatibility. Verify against our actual version matrix before rollout: PHP constraint, illuminate/support constraint, and one smoke test per supported Laravel major. Check for existing collection macros that could collide. Run Larastan with the shipped IDE helper and confirm no new baseline errors. Confirm auto-discovery registers the provider under our optimized/config:cache deployment path — packages that rely on runtime macro registration occasionally behave differently in cached production boots, which must be caught in staging, not production.

Sequencing.

  1. Timeboxed spike (~half day): install in a feature branch, render one real select, run the XSS probe (label containing <script>/"), run the type-mismatch probe (string vs. int selected), confirm Laravel version support.
  2. Decision gate: adopt, adopt-with-wrapper, or build a ~50-line internal helper. Given 0 dependents and a tiny surface, "build internal" is a legitimate outcome; the package's value is mostly ergonomics.
  3. If adopted: pin version, add the internal wrapper + 3–4 regression tests (escaping, selected semantics, grouping, disabled), and a one-page usage guideline.
  4. Pilot one or two low-risk forms (internal admin filters). Measure query counts to catch the ::all()-in-view pattern.
  5. Codify the rule that option data is prepared in a controller/view model/toSelectItems() payload, never queried inline in Blade; add a code-review checklist item.
  6. Broaden to other forms only after the pilot shows no escaping or selection defects; revisit the wrapper-versus-direct-call ratio at the next dependency review.

Operational Impact

Maintenance. Low absolute maintenance cost: small package, low churn, MIT fork rights. The real overhead is governance — pinning, a per-Laravel-upgrade smoke test, and a standing owner for the wrapper. Budget roughly a day per year plus a small share of each framework upgrade. Because the surface is a handful of classes, an abandoned upstream is recoverable in a day via fork or in-house reimplementation; the credible worst case is two days of engineering, not a stalled roadmap.

Support. No vendor SLA; best-effort community support from a single maintainer. Internal documentation must carry the weight: which methods we sanction, the escaping rule, the "prepare data outside the view" rule, and the pinned version. Bug reports upstream are unlikely to be prioritized given the adoption level, so assume we fix our own issues.

Scaling. Option rendering happens in the request cycle and builds a full HTML string in memory. That is fine to a few hundred options; beyond roughly 500–1,000 rows per select it becomes measurable in response size, memory, and browser render cost. Large catalogs should be served via toSelectItems() + AJAX/typeahead, or cached option arrays (config/reference data is the ideal case: render once, cache the fragment). Watch for closure-based labels touching lazy-loaded relations — that turns one select into N+1 queries, invisible in a small dataset and painful in production.

Failure modes. Ranked by blast radius:

Failure Impact Detection
Unescaped label/attribute output Stored XSS on admin or public forms Probe in spike; CSP and code review as backstops
Selected/disabled mismatch from type coercion Wrong record preselected on edit forms; data written to the wrong entity Regression test on int vs. string keys; QA on every edit form
Closure bug in withDisabled/withSelected returning truthy always Every option disabled — form unusable Unit test the actual predicates
Query-in-Blade pattern copied from docs Latency, memory growth as tables grow Query-count assertions in CI; review checklist
Macro collision or rename on a version bump Runtime exception rendering a page Pinned version + smoke tests per Laravel major
Grouped output mis-nesting with groupBy() Malformed <select>; selections lost on submit Visual + submit round-trip test

None of these are systemic; all are contained by the wrapper and a handful of tests, which is precisely why the wrapper is non-negotiable in the integration plan.

Ramp-up. A mid-level Laravel developer is productive in under an hour — the fluent API reads well and the README's examples are directly copyable. That same copyability is the main onboarding hazard: new developers will query models inside Blade and emit raw output because that is what the documentation shows. The onboarding artifact that matters is therefore not a tutorial but a short convention doc plus the wrapper's enforced signature, so correct usage is the path of least resistance.

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.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
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
adachsoft/php-code-style