hnhdigital-os/laravel-model-schema
Laravel Model Schema adds schema introspection to Eloquent models, letting you read column names, types, and related metadata directly from the model. Handy for generating forms, filters, validation rules, and admin scaffolding without querying the database manually.
Architecture fit
The package is a thin, opt-in concern layered on Eloquent: it replaces the three parallel model properties ($casts, $fillable, $hidden) with a single $schema array, typically applied via a trait or base model. Architecturally this is low-invasiveness — it does not alter persistence, query building, or the database layer. It is a developer-ergonomics and consistency play, not a functional one. That means it fits cleanly into an existing Laravel codebase if the team already treats models as declarative configuration, and it fits poorly if models are heavily dynamic or if the organization leans on Eloquent's native conventions for onboarding.
Integration feasibility
Feasible for a pilot, but the compatibility surface needs verification before commitment:
casts() and protected function casts(): array; Laravel 12 continues that direction. Confirm whether the package's property-override approach coexists with, or silently overrides, the method-based API. This is the single largest compatibility unknown.encrypted, AsArrayObject, AsCollection, Attribute accessors/mutators, and $appends. Any of these being read from a different source than $schema creates two sources of truth — the exact problem the package claims to solve.barryvdh/laravel-ide-helper), admin panels (Nova, Filament), API doc generators, php artisan model:show, static analysis (PHPStan/Larastan), and factory/state definitions commonly read $fillable/$casts directly. Each is a potential blind spot.Technical risk
$fillable is a mass-assignment boundary and $hidden is a data-exposure boundary. Centralizing them improves auditability but concentrates the cost of a single mistake. A schema entry that omits hidden on a sensitive column leaks it through JSON serialization; an over-broad fillable enables mass assignment. Guardrails are mandatory, not optional.$casts/$fillable, a developer editing the "wrong" property sees no error and no effect. Silent no-ops are worse than hard failures.Key questions
casts() method?$schema and native properties are defined — does it merge, override, or ignore?Stack fit
Strongest fit is a mid-to-large Laravel monolith with many models, multiple teams, and a history of inconsistent $fillable/$hidden declarations. Weakest fit is a small codebase, a codebase with few models, or one where API resources / DTOs already own the serialization boundary — in the latter case $hidden is doing little work and the consolidation payoff shrinks.
Migration path
Model::create() respects fillable, toArray() respects hidden, casts behave identically, and the existing test suite for that model passes unchanged.$fillable/$casts/$hidden (IDE helper config, Nova/Filament resources, custom validators, factories, serializers). Confirm each still works.$casts/$fillable/$hidden.Compatibility
Pin the exact version in composer.json (not a caret range) and require a green test suite on the target Laravel/PHP matrix before any framework upgrade. Treat any Laravel major upgrade as requiring a re-validation of this package specifically.
Sequencing
Do this after the Laravel upgrade cadence is settled, not before. Adopting a 2023-era model-layer package in the middle of a framework upgrade converts a routine upgrade into a dual-variable debugging exercise.
Maintenance
Assume the team owns this package's future. Pin the version, vendor it or mirror the repo internally, and budget roughly one engineer-day per Laravel major upgrade to validate or replace it. Because the feature surface is small, maintain a documented exit path: an internal HasSchema trait that provides the same behavior, so that if the package is abandoned the swap is a one-file change. Given the size of the dependency, seriously evaluate writing that trait first and skipping the vendor entirely — it is likely 50–150 lines and eliminates the entire external-risk column.
Support
No SLA, no vendor. Support is the team's own engineers plus GitHub issues on a low-traffic repository. Onboarding documentation is therefore not optional — it is the primary support artifact.
Scaling
Positively, it scales developer experience: one place to look per model, fewer three-way inconsistencies, easier security review of exposure boundaries. Negatively, wide models (30+ attributes) become large single arrays with mixed semantics — casting, mass assignment, and serialization concerns bundled together. Consider a convention for grouping or commenting within the schema, and expect review friction on the largest models. Runtime performance impact is negligible; this is a boot-time property read.
Failure modes
$casts natively; it is ignored; a type bug ships. Mitigation: CI check rejecting native property declarations.$hidden; it appears in API responses. Mitigation: tests asserting serialized shape for models backing public endpoints.fillable in the consolidated array. Mitigation: explicit $request->validated() discipline and tests on create/update paths.Ramp-up
Individual ramp-up is low — an Eloquent-fluent developer understands the pattern in under an hour. Organizational ramp-up is the real cost: it is a non-standard convention, so every new hire and every contractor must learn it, and every Stack Overflow answer or AI-generated snippet they encounter will use the native properties instead. Budget a short internal write-up plus a lint rule that fails loudly rather than relying on documentation alone. If the team cannot commit to that enforcement, the package will decay into a mixed-convention codebase and deliver negative value.
Recommendation: Trial, scoped to new models, contingent on the Laravel 11/12 compatibility answer. If the compatibility answer is unfavorable or unclear, build the trait internally — the vendor risk exceeds the functionality being purchased.
How can I help you explore Laravel packages today?