padam87/attribute-bundle
A Symfony bundle for adding flexible, configurable attributes to Doctrine entities without schema changes. Define attribute types, attach dynamic values to records, and query them through a simple, reusable API.
Architecture fit. Despite the "Laravel/PHP package" framing, the *Bundle suffix, the author's ecosystem, and the 2015 release date all point to this being a Symfony bundle built on Doctrine ORM — service definitions in YAML/XML, a compiler pass or two, annotation/XML metadata drivers, and Doctrine event subscribers. That is a structural mismatch for a Laravel application: Laravel's container, Eloquent, and validation pipeline do not consume Symfony bundle service definitions. Practical fit in a Laravel codebase is therefore effectively zero without also adopting laravel-doctrine/orm, and even then the bundle layer itself is Symfony-specific and would be inert. The idea — attaching typed, user-defined attributes to arbitrary entities without per-attribute schema migrations — is the transferable asset here, not the code. That pattern is genuinely valuable for CMS-style products, configurable catalogs, and tenant-defined fields, so the real question is whether we are buying a dependency or reading a reference implementation. On this evidence, it is the latter.
Integration feasibility. Low as a dependency, moderate as a port. The upstream cost to modernize is unbounded: PHP 5-era code, no CI badge or test suite evident, no evidence of PHP 7/8 support, and Doctrine 2.x APIs around UnitOfWork, proxy generation, and metadata caching have all drifted. A composer require on a modern runtime will most likely fail at the platform constraint, and if forced through, will fail again at container compilation or metadata parsing. Porting the entity model and form type into our own domain code is a bounded, well-understood exercise — perhaps one to two weeks for a senior engineer including tests and migrations — and produces something we own outright.
Technical risk. High and asymmetric. Abandonment since July 2015 with 24 stars and 0 dependents means no community safety net, no security disclosure path, no patch cadence, and no peer-review signal that the design survived contact with production. An EAV-style attribute layer is also load-bearing by nature: it touches entity persistence, so a bug is a data-corruption bug, not a UI bug. The MIT license is the one clean part of the picture — we can fork and modify freely, but forking means permanently owning a small ORM extension, which is the worst kind of code to own.
Key questions. What runtime are we actually targeting — Laravel/Eloquent, Laravel with a Doctrine bridge, or Symfony? Does the package's composer.json constrain Symfony ~2.x and Doctrine ~2.2 in a way that hard-blocks install? Does the value model serialize or type-cast stored values, and if so what is the deserialization surface? Does it ship any tests we can use as a behavioral specification for a port? And most importantly: has anyone evaluated spatie/laravel-schemaless-attributes, Postgres JSONB with GIN indexes, or the Sylius attribute component as a maintained substitute before we spend effort here?
Stack fit. If the target is Laravel + Eloquent, the idiomatic equivalent is a small internal package: an attributes definition table (name, slug, type, validation rules, options), a polymorphic attribute_values table, a trait for the owning model, and casts for typed access. If the target is Postgres, a single JSONB column with a GIN index outperforms the EAV approach on both query flexibility and operational simplicity for most attribute-cardinality profiles. Adopting the bundle's schema shape while dropping its Symfony plumbing is the only path that preserves the original value at acceptable cost.
Migration path. Two viable routes. Route A — vendor and fork — only makes sense if we are already on Symfony 2.x/Doctrine 2.x, which is unlikely for new work and would be a red flag for any greenfield effort. Route B — extract and reimplement — is the default: read the source as a specification, reproduce the attribute definition/value model, the type-resolution logic, and the form integration in our own package, and write the tests upstream never had. Estimate 8–10 engineer-days for a production-grade internal version with migrations, validation hooks, and query helpers; roughly 1 day for a throwaway spike to validate the schema shape against our access patterns.
Compatibility. The concrete blockers to verify before committing any time: the require block's PHP constraint, the Symfony and Doctrine version pins, and whether metadata is read via annotations (which additionally couples to doctrine/annotations and its deprecation path). Expect the package to be uninstallable on PHP 8.x and unusable with Doctrine 3.x. There is no realistic compatibility shim; patching a decade-old bundle to modern Doctrine is a larger job than the reimplementation.
Sequencing.
Maintenance. Upstream is dormant; the effective maintenance burden transfers entirely to us at the moment of adoption. That means owning PHP and framework upgrades, security patches, and Doctrine API migrations for a component we did not write and that has no test suite to catch regressions. The honest framing for planning is: this is not a dependency, it is an acquisition of legacy code with no warranty.
Support. No maintainer SLA, a single author, and an issue queue that has been cold for roughly a decade. Any incident becomes an internal debugging exercise against unfamiliar 2015-era idioms, with no upstream escalation path. If adoption proceeds, a named internal owner and a written runbook are non-negotiable.
Scaling. The EAV pattern is the primary concern here, independent of code quality. Attribute filtering requires a join per attribute, index selectivity is poor, and reporting/analytics queries degrade quickly — the classic profile is fine under roughly 10^5 rows with low attribute cardinality and painful beyond that, especially for multi-attribute faceted search. Postgres JSONB with GIN indexes is the modern substitute and avoids the join explosion entirely. Hydration of attribute collections is also a latent N+1 source unless eager-loading is disciplined.
Failure modes. The realistic ones: install-time hard failure on modern PHP or Doctrine (most likely outcome); silent metadata cache staleness after schema changes; orphaned attribute values when owning entities are deleted without cascade rules; type coercion or unserialization errors at hydration time, which is a data-integrity rather than an availability risk; and schema lock-in — once the attribute tables carry production data, swapping implementations requires a migration project rather than a composer update. The lock-in risk is the one that most often turns a small convenience decision into a multi-quarter liability.
Ramp-up. Conceptually cheap — the attribute-definition/attribute-value model is a well-known pattern and any mid-level engineer grasps it in an afternoon. Environmentally expensive: the Symfony 2 idioms, annotation-based metadata, and Doctrine 2 event system are unfamiliar to a Laravel-fluent team, and documentation appears to be limited to a README. Budget a day for a senior engineer to read the source end to end, and assume the knowledge is concentrated in one or two people unless deliberately spread.
Recommendation. Do not adopt as a dependency. Treat it as a pattern reference and invest 8–10 engineer-days in a small, tested, internally owned implementation — preferably JSONB-based if the datastore allows — unless a maintained alternative already covers the requirement. The 2.2 score is consistent with what the code inspection implies: interesting design, unmaintainable artifact.
How can I help you explore Laravel packages today?