elhareth/laravel-eloquent-metable
elhareth/laravel-eloquent-metable adds flexible key-value metadata to any Eloquent model via a simple trait, letting you attach custom attributes without extra migrations or schema changes.
Architecture fit. laravel-eloquent-metable is a lightweight, trait-based extension that attaches arbitrary key/value metadata to Eloquent models via a polymorphic relationship. It fits the standard Laravel idiom (trait + published migration + relationship), so it slots into a conventional Laravel service-layer architecture without introducing a new abstraction or runtime dependency beyond Eloquent itself. It is complementary to — not a replacement for — JSON columns, Spatie's schemaless-attributes, or a dedicated EAV design. Fit is strongest where metadata is sparse, heterogeneous across records, and read-mostly.
Integration feasibility. Low-to-moderate effort. Typical adoption is composer require, publish and run the migration, then add the trait to each model requiring metadata. Two integration details drive the real cost:
HasUlids (or UUIDs) on parent models, the package's polymorphic *_id column must be a string/ULID-compatible type rather than bigint. This is the single highest-value item in the release and must be verified against the published migration stub before rollout.Technical risk. Moderate, concentrated in three areas:
@elhareth, who is also listed as a first-time contributor. There is no visible second reviewer or co-maintainer.(metaable_type, metaable_id, key) and disciplined eager loading are prerequisites, not optimizations.Key questions.
metaable_id, and is that change backward compatible with existing bigint-keyed tables?composer.json?Stack fit. Designed for Laravel with Eloquent; assumes a relational database. No queue, cache, or external service dependency. Works alongside existing model concerns and does not require changes to controllers or resources beyond exposing/validating meta on write paths. It should be treated as an infrastructure concern owned by the platform team, not a feature-level dependency.
Migration path. Two distinct migrations to plan:
Compatibility. Verify declared support for the current Laravel and PHP versions in composer.json before committing. Confirm behavior on the target database driver (MySQL/PostgreSQL/SQLite) — particularly index length limits on metaable_type and collation behavior for key lookups. If the application already uses JSON columns for metadata, do not run both patterns on the same model; pick one per aggregate to avoid ambiguous read paths.
Sequencing.
HasUlids.Maintenance. Low code surface but non-trivial ownership. The package is small and single-maintainer; budget for the possibility of vendoring or forking if upstream stalls. Pin the version explicitly in composer.json (avoid floating constraints) and require a review of the diff on each upgrade rather than auto-merging Dependabot PRs — the 1.0.0 notes demonstrate that upstream changelogs cannot be relied on for change classification.
Support. Support burden shifts to the internal team: the allowed metadata keys, their types, and their consumers become an internal contract that must be documented and governed. Without a key registry, metadata devolves into an unowned schemaless dumping ground that no one can safely remove. Assign an owner and a documented key namespace per domain.
Scaling. Performance is dominated by access pattern, not by the package. Requirements:
(metaable_type, metaable_id, key); verify with EXPLAIN on the top three queries.with('meta')) in list endpoints; treat lazy meta access in a loop as a defect.Failure modes.
Ramp-up. Developer ramp-up is genuinely low: a mid-level Laravel engineer can be productive in under a day, and the trait is self-evident to anyone who has used polymorphic relations. The real ramp-up cost is organizational — establishing and communicating the metadata key contract, the eager-loading convention, and the "when to use meta vs. a real column" decision rule. Codify that rule early: if a field is queried, sorted, or validated as first-class, it belongs in a column, not in meta.
How can I help you explore Laravel packages today?