genealabs/laravel-casts
GeneaLabs Laravel Casts adds flexible custom attribute casting to Eloquent models, making it easy to map database columns to value objects, DTOs, and other PHP classes. Keeps model code clean and cast logic reusable, testable, and simple to configure.
Resolve the identity question before you install anything. The listing says "form macros for Laravel's Blade template engine." The bundled description.md describes an Eloquent custom-cast layer for value objects. The changelog (0.13.3 — "label on Tel field. Thanks @sawantops!") is the tiebreaker and points squarely at Blade form macros built on laravelcollective/html. Treat the cast language as a documentation error. Ten minutes in composer.json and src/ settles it; do that before you commit to an approach, because the two readings lead to completely different integration work.
Install and pin hard.
composer require genealabs/laravel-casts:0.13.3
Exact version, no caret. The repo is archived (final release February 2024, 109 stars, 0 dependents), so there is no patch path for anything you find. "Latest allowed" buys you nothing and costs you reproducibility.
Read the dependency graph, not the README. The form-macro reading puts laravelcollective/html underneath you — also archived. Run:
composer why laravelcollective/html
composer depends genealabs/laravel-casts
If laravelcollective/html is already in the tree for other reasons, the marginal risk is low. If this package is what would drag it in, the correct move is to skip the package and write the handful of macros yourself in AppServiceProvider — they are string builders, not infrastructure.
Where to look, in order:
composer.json — real require block and the extra.laravel.providers entry used for auto-discovery.src/Providers/*.php — where the macros are registered and under what names. This file is the product.tests/ — if coverage is thin, assume rendering is unverified and budget for your own HTML assertions.First use case: one field on one internal form. Don't start with checkout, KYC, or anything customer-facing. Take an admin resource edit form, replace a single hand-written tel/email input with the macro, and verify three things in the rendered output: old() repopulation after a failed validation round-trip, error message placement, and attribute escaping. If one field doesn't survive that, nothing downstream will.
Baseline field pattern. The entire value proposition is collapsing label + input + error + wrapper into one call, repeated across every resource form:
{{ Form::open(['route' => ['admin.contacts.update', $contact], 'method' => 'PUT']) }}
{{ Form::tel('phone', old('phone', $contact->phone), ['label' => 'Phone']) }}
{{ Form::email('email', old('email', $contact->email), ['label' => 'Email']) }}
{{ Form::submit('Save') }}
{{ Form::close() }}
On first use, confirm explicitly whether the macro reads old() itself or expects you to pass it, whether it renders the $errors message for its own field name, and whether labels/attributes are escaped. Those three behaviours are where an unmaintained macro silently diverges from the block you replaced.
Wrap every macro in a project-owned partial. This is the highest-leverage habit with this package — it converts a dependency question into a file swap:
{{-- resources/views/components/field/tel.blade.php --}}
{{ Form::tel($name, old($name, $value ?? null), ['label' => $label] + $attributes) }}
When you eventually replace the macro with a native <x-input> component, every call site is untouched. Do this even if you intend to keep the package; with an archived dependency, "intend to keep" has a shelf life.
Inventory before you migrate anything large. If the app already uses these macros broadly, find out what's actually called rather than what's documented:
grep -rE 'Form::(tel|email|date|number|...)' resources/views
Then vendor only those macros into your own service provider under identical names and remove the package in a single commit. You are reproducing tens of lines of string concatenation per macro.
Keep domain logic out of the view layer. Macros emit markup; they don't validate, authorize, or persist. Pair them with Form Requests and policies exactly as with hand-written HTML. Never let a macro become the home of a default value, mutator, or business rule — once it lives inside a rendered string it has no test surface.
Test the rendered HTML, not the macro call. Feature tests asserting on output are the only real safety net:
$response->assertSee('name="phone"', false);
$response->assertSee('value="+1 555 0100"', false);
One such test per form that uses the macros. When you bump Laravel or PHP, this suite is the only compatibility signal you'll get — there are zero dependents to lean on for evidence.
Know the native alternatives before committing. Blade components with @props (first-party, no laravelcollective tail), or spatie/laravel-html if you specifically want PHP-built form markup and an maintained package. If the field is genuinely about value objects (Money, PhoneNumber, typed IDs), that's a different concern entirely: native CastsAttributes (Laravel 7+) plus purpose-built libraries such as moneyphp/money or propaganistas/laravel-phone. Do not adopt this package because of its description.md.
The identity contradiction is a diligence finding, not a footnote. A package whose listing, docs file, and changelog describe different products has not been curated. Assume tests, compatibility claims, and README are equally stale, and read the source rather than trusting any of it.
Archived means you own it in perpetuity. Compatibility, security review, and every future Laravel/PHP bump are your work items with no upstream fix path. The failure mode is the bad one: silent divergence in rendered HTML after a framework upgrade, discovered during a form submission rather than in a stack trace.
Pin >= 0.13.3. The Tel label fix landed there. Pin below it and you will debug a bug that was already fixed and will never be fixed again.
Macro names are global mutable state. Macros register onto the Form facade at provider boot; any other package or a teammate's AppServiceProvider can collide with or overwrite yours, and last registration quietly wins. Verify ownership after adding anything:
php artisan tinker --execute="dump(Form::hasMacro('tel'));"
laravelcollective/html is the real dependency risk. It's archived too. Its Form::open handles CSRF tokens and method spoofing — verify both still work on your Laravel version, because that's what breaks first and silently. If you don't need Form::open/Form::model, don't let the package pull the entire library in just to render an input.
Escaping is a security surface you must verify yourself. Labels, placeholders, and attribute arrays all funnel into string concatenation. Test a label containing " and <script> before shipping. An unmaintained macro that skips e() is an XSS vector with no security release coming.
Old input and error placement are the most common behavioural surprises. Check repopulation on a failed validation round-trip and that the error text actually renders adjacent to the field — not merely that $errors contains it. This is precisely where a macro diverges from the markup you replaced.
Caching will waste your afternoon. Run php artisan view:clear after changing macro output; remember registration happens at provider boot, so cached config or compiled views serve stale HTML. If a macro "doesn't exist," confirm auto-discovery ran (composer dump-autoload) and the provider is registered in bootstrap/providers.php or config/app.php.
Extension points belong to you, not them. You can add macros in your own provider and wrap output in partials, but you cannot expect upstream changes. Everything beyond 0.13.3 is yours to write and own.
For new work, the answer is usually no. Laravel 7+ ships native custom casts, enum casting, and Blade components; an archived dependency duplicating first-party capability is hard to defend on a greenfield project. Legitimate uses are narrow: you're already on it and need a scoped migration, you're on a pre-Laravel-7 codebase, or you're reading the source once as a reference implementation. For anything touching money, identity, phone, address, or compliance-adjacent data, choose a maintained library behind a boundary you control.
How can I help you explore Laravel packages today?