phl/laravel-sti
phl/laravel-sti brings Single Table Inheritance to Laravel Eloquent. Map multiple model subclasses to one database table using a type column, with automatic class resolution, scoped queries, and familiar Eloquent relationships and behavior for cleaner hierarchical models instead of separate tables.
Architecture fit. Single table inheritance is a data-modeling pattern, not a framework feature — the package's job is to hook Eloquent's hydration path (newFromBuilder / newInstance) so a row's discriminator column resolves to the right subclass, and to scope child queries to that discriminator. That is genuinely useful when a family of entities shares ~90% of its columns and differs only in behavior (e.g., notification channels, payment method variants, line-item types). It is a poor fit when subclasses already diverge in schema or when the table would become a sparse nullable matrix. The package itself is small and sits on a core, semi-internal Eloquent seam, which is both why it's cheap to adopt and why it's fragile.
Integration feasibility. MIT license means forking is a legitimate option. The blocker is version currency: last release April 2020 targets the Laravel 5.5–7 era, so composer constraints almost certainly exclude Laravel 9/10/11+ and PHP 8.1+. Adoption on a modern stack therefore means either a fork with updated constraints and PHP 8 return types, or vendoring the ~small trait into our own codebase. Neither is expensive; both transfer maintenance to us.
Technical risk. The dominant risk is silent incorrectness, not breakage. If child-query auto-scoping or create-time discriminator assignment has a gap, the failure mode is reading or writing the wrong subtype — data corruption that tests won't catch unless we write them. Secondary risks: hydration overrides drifting across Laravel majors; observers/events and queued-model serialization crossing subtype boundaries; relations typed against the base model returning mixed classes at runtime.
Ecosystem risk. 17 stars, 0 dependents, no release in five years, score 0.185. There is effectively no community validation and no upstream to escalate to. Treat this as "sample code we may adopt," not "a dependency we consume."
Key questions. Which Laravel and PHP versions must we support? Does the package auto-scope Child::query() and auto-set the discriminator on create()/save(), or is that the caller's responsibility? Is STI actually the right model versus a JSON/attribute column, polymorphic relations, or simply separate tables with a shared abstract base? What is the exit cost if we abandon it later (columns, migrations, test rewrites)? Who owns the fork?
Stack fit. No external services, no runtime infrastructure, no config surface to speak of — it is a pure in-process ORM concern. That makes it trivially compatible with any Laravel deployment topology and any CI setup.
Migration path. For new tables, design the discriminator (type string, indexed) in from the start and keep subclass-specific columns nullable with application-level invariants. For existing tables, the path is: add the discriminator column, backfill by row semantics, add the index, deploy read paths, then deploy write paths — in that order. Retrofitting STI onto tables that already diverge is usually the wrong call; prefer it for greenfield model families.
Compatibility. Pin explicitly. Do not rely on an unconstrained composer require; verify the constraint set, then either fork with widened constraints plus PHP 8 type declarations, or copy the logic into an internal HasSingleTableInheritance trait we control. The trait route is often the better product decision: same capability, no third-party risk, and we can align it with our own conventions.
Sequencing.
toArray stability.Maintenance. We become the maintainer. Budget a small ongoing allowance — roughly one developer-day per Laravel major plus a quarterly compatibility check — and assign a named owner. Without that, the package quietly becomes a landmine at the next framework upgrade.
Support. No upstream, no issue queue worth monitoring, no security response path. Anything we need, we build. Internal documentation covering "which classes are STI subclasses, why the discriminator exists, and never query the base model without scoping" is mandatory, because the pattern is invisible to a new hire reading the schema.
Scaling. Query performance is fine at moderate scale with an index on the discriminator. The real scaling constraint is schema evolution: every subclass-specific column addition rewrites or locks the shared table, and divergent requirements push the table toward sparsity. At high row counts, adding a column to a wide STI table becomes a coordinated, risky migration across all subtypes simultaneously rather than an isolated one.
Failure modes to instrument.
Mitigations: enforce a non-null discriminator at the database level, add a model-resolution assertion in the test suite, and add a metric/log on unrecognized discriminator values in production.
Ramp-up. Conceptually low — STI is easy to explain in ten minutes — but the risk is that engineers don't know it's in play until they hit a scoping bug. Ramp-up cost is documentation and code review discipline, not training.
How can I help you explore Laravel packages today?