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

Castable Request Laravel Package

lionix/castable-request

Laravel package for casting incoming request data into typed objects and structured values, helping keep controllers clean and reducing manual array handling when working with validated input.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

  • Build vs. buy on request-input plumbing. Converting validated strings into enums, dates, collections, value objects, and DTOs is undifferentiated work every Laravel service writes by hand or buries in a base FormRequest. This package replaces that with a declarative casts() map. Because 2.0.0 depends only on illuminate/database, illuminate/http, and illuminate/support rather than the full laravel/framework, the audit, licensing, and dependency surface is smaller than the in-house trait it replaces — an unusually clean buy case for a small package.
  • API error-contract design — the single most decision-relevant change. In 2.0.0, casts run after validation. An unknown enum case or unparsable date now returns a 422 field error instead of a 500 from an exception thrown inside the caster. That is a user-visible change to your public contract: it touches API docs, client/SDK error handling, support runbooks, rate-limit and alerting thresholds, and any contract tests asserting status codes. This belongs in a product/API review, not only a code review. Framed correctly, it is a reliability win — bad input becomes a well-formed field error instead of a server fault and a page.
  • Roadmap sequencing: the platform upgrade is the gate. The package requires PHP 8.2+ and Laravel 12 or 13, and explicitly does not support Laravel 11 (EOL with open security advisories). If the roadmap still lists a framework upgrade, this package is a dependent of that decision, not a driver of it. Sequence it after the upgrade lands; do not let a convenience library become the argument that forces or delays a security-relevant platform move.
  • Typing as a quality lever, not a style preference. Moving DTOs and value objects to the request boundary means controllers receive OrderStatus and Carbon instances rather than strings. That pays out in fewer defensive null/format checks downstream, better static analysis (Larastan), and tests that assert domain objects instead of array shapes — all measurable in defect rates on input-handling paths.
  • Multi-tenant / long-lived-runtime readiness (Octane, queues). 2.0.0 fixed a real correctness bug: casts declared by one form request leaked into every request resolved afterwards in the same process. If you run Octane, Horizon, or a long-lived test suite, this is the difference between "safe to adopt" and "data corruption waiting to happen." That fix, plus request-level casts now overriding global ones instead of mutating a shared singleton, is the strongest technical argument for pinning to ^2.0 specifically.
  • Use cases that justify adoption: public or partner APIs where consistent 422 error shapes matter; domain-rich services with enums and value objects at the boundary; codebases already invested in FormRequest validation; teams standardising DTO construction so new endpoints don't each invent their own mapping.
  • Use cases that do not: thin CRUD apps with three endpoints; services on Laravel 10/11 with no upgrade budget; teams relying heavily on $request->input()/$request->all() after validation (cast values land in the request, and Symfony's InputBag rejects non-stringable objects — read from validated()/safe() instead).

When to Consider This Package

Adopt when all of the following hold:

  • Platform is already there or funded. PHP 8.2+, Laravel 12 or 13. Verify with composer why-not lionix/castable-request ^2.0 before committing — the split illuminate/* dependencies surface version conflicts here rather than as a runtime surprise.
  • You have a real boilerplate problem. Multiple form requests doing manual array→object mapping, or a base FormRequest trait that has quietly become load-bearing framework-internals code. Below that threshold, the package adds a concept without removing work.
  • You can absorb a deliberate breaking-change cadence. 2.0.0 renamed ImMemoryCastsRegistry → InMemoryCastsRegistry, removed Handlers\RequestResolvingHandler, added native types to all contracts, and reordered validation vs. casting. A pre-2.0.0 custom caster without return types fatals at resolution. That is fine for a team with CI and a clean upgrade path; it is a hazard for a codebase full of undocumented customisations.
  • You will adopt the whole pattern, not just the happy path. Budget for: rewriting rules() that only passed because the value had already been cast (use date_format:Y-m-d rather than date when the cast is what normalises), deleting now-dead try/catch blocks that mapped InvalidArgumentException to 422, and grepping for strict comparisons against validated() payloads ('pending' === $data['status'] silently changes truthiness once that value is an enum).
  • You accept the maintenance risk honestly. 23 stars, zero dependents, and a small real-world upgrade-report base. Mitigations are cheap and should be non-negotiable: pin to ^2.0, mirror the package's own CI matrix (HTTP tests, Pint, Larastan across supported PHP/Laravel pairs), and keep the integration behind your own thin abstraction so a future swap is a day's work rather than a quarter's.

Look elsewhere — or build in-house — when:

  • The app is on Laravel 10/11, or PHP < 8.2, with no approved upgrade.
  • You need only one or two casts. A 20-line helper or Laravel's own Enum rule plus a small factory method covers it with zero dependency.
  • You require vendor SLAs, active maintainer commitment, or an established security-response process. Zero dependents means zero community pressure on maintainers; treat it as a single-point-of-failure dependency and govern it accordingly.
  • Your architecture puts DTO construction in an application/service layer with its own mapper, in which case casting at the HTTP boundary duplicates a mapping layer you already own.
  • You depend on customising RequestResolvingHandler — that handler is gone, and the two-phase resolve/after-resolve split no longer exists. Migration cost may exceed the benefit.

How to Pitch It (Stakeholders)

To executives (one paragraph): This is a small, MIT-licensed Laravel library that removes hand-written boilerplate for turning incoming API request data into typed objects. The business case is not the library — it is the two things adoption forces us to do properly: upgrade off an end-of-life Laravel 11 that carries open security advisories, and standardise the error contract on our public API so that malformed input returns a clean 422 field-level error instead of a 500. Both are risk reductions with a scheduled cost, not speculative engineering. The dependency itself is low-risk to try and cheap to remove: it pulls in three Laravel sub-packages rather than the whole framework, and it can be isolated behind a thin internal abstraction. The counterweight is that it is a small, low-adoption project (23 stars, no dependents), so we pin the version, run its test and static-analysis matrix in our own pipeline, and treat it as infrastructure to be monitored rather than assumed.

To engineering (what we are signing up for):

  • Sequence: finish the PHP 8.2 / Laravel 12–13 upgrade first. This package cannot enter the tree before that, and must not be the reason we rush or defer it.
  • The behavioural change to internalise: rules validate the raw wire format; casts run afterwards. Every rules() method in the diff needs reading. Anything relying on $request->input() already being typed — custom rules, prepareForValidation(), middleware — needs re-checking. Read from validated()/safe(), which now return cast values; do not write cast values back via $request->merge().
  • Cleanup that comes free: delete try/catch blocks and handler mappings that converted caster exceptions into 422s; delete index-keyed workarounds for the old wildcard bug; delete manual forget() calls that papered over cross-request cast leakage. Use CastsRegistryInterface::forget() in tearDown() for test isolation, and never call it at runtime — it mutates a shared singleton visible to every Octane worker and queued job.
  • Use the fast path: prefer castAttributes() for batch casting outside the form-request lifecycle. Input is replaced once per request rather than once per attribute, which is faster and avoids half-cast intermediate states.
  • Governance: pin ^2.0, mirror the package's CI matrix (HTTP-level tests, Pint, Larastan across every supported PHP/Laravel combination), and keep the integration behind our own interface so the exit cost stays low. Check UPGRADE.md on every minor bump — the project has demonstrated it will ship breaking changes in a major.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource