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.
First, calibrate expectations: padam87/attribute-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no config publish, no facade. Treat it as a reference implementation of an "entity attributes" pattern rather than a drop-in dependency — the score (2.2) and the 2015 last release reflect that it will almost certainly conflict with modern Laravel/Symfony constraints if you composer require it directly.
If you are evaluating the pattern to port into Laravel:
AttributeValueInterface → AttributeTrait → AttributeValue entity. That's the entire contract — a polymorphic key/value store attached to any model.name/value pair and an owner reference. This is EAV-lite.morphMany relation from your model to an attribute_values table (attributable_type, attributable_id, name, value).First use case: arbitrary per-record metadata you don't want as columns — product specs, CMS page settings, feature flags scoped to a tenant, custom fields captured from a form. Start with a single model and a single HasAttributes trait; do not generalize until you have two consumers.
Trait-on-the-model (the core pattern). In Symfony the bundle expects you to use AttributeTrait on your entity and implement the interface. The Laravel port is the same idea:
trait HasAttributes
{
public function attributes(): MorphMany
{
return $this->morphMany(AttributeValue::class, 'attributable');
}
public function setAttribute(string $name, $value): static
{
$this->attributes()->updateOrCreate(['name' => $name], ['value' => $value]);
return $this;
}
}
Accessor workflow. The original exposes getAttribute($name)/setAttribute($name, $value) style calls. Keep the read path cheap: eager-load attributes on list endpoints (->with('attributes')) and build a keyed array once, rather than hitting the relation per key. A small AttributeBag value object that hydrates from the loaded collection pays for itself quickly.
Form / request integration. The Symfony bundle ships form types for editing attributes. In Laravel, the equivalent is a repeatable field group (attributes[color], attributes[size]) validated against an allow-list of known attribute names per entity type. Store the allow-list in config, not the database, so validation stays declarative.
Querying. Don't try to whereHas for every filter — join the attributes table once for the filters you actually support, or denormalize the handful of searchable attributes into real columns. The bundle's generic storage gives you no index on value; plan for that.
Integration tip. If you retain any of the original PHP classes for reference, keep them out of app/ and out of the autoload path — put them under docs/ or a scratch namespace so Laravel's container and route model binding never pick them up.
composer.json targets Symfony 2.x-era components and old Doctrine. Expect constraint conflicts; use --dry-run first if you're just curious.Illuminate\Support\ServiceProvider, no config/attribute.php. Anything you want from this package you write yourself."false" being truthy.$model->getAttribute('x') that isn't backed by a loaded collection is a query. Load the relation eagerly, always.value column length and collisions. The original schema uses a modest string column; long text, JSON blobs, and duplicate names per owner need explicit handling (unique index on attributable_type, attributable_id, name).->cascadeOnDelete() or a model deleting hook.AttributeValue entity via Doctrine mapping overrides. In your Laravel port, that's simply your own model — take the opportunity to add a type column now rather than migrating later.Relation::enforceMorphMap) and the attributable_type strings — class renames silently orphan rows.How can I help you explore Laravel packages today?