Reading attributes
The owner handle covers the everyday reads — values with defaults applied, presence, bulk maps and history:
use RoundlyConsulting\Attributes\Facades\Attributes;
$attributes = Attributes::for($product);
$attributes->get('color'); // typed value (or the defined default)
$attributes->has('color'); // bool
$attributes->all(); // Collection<name, value>
$attributes->toKeyValue(); // array<name, value>
$attributes->keys(); // list<string>
$attributes->history('color'); // Collection<AttributeRevision>, newest firstOn the model
The trait adds richer reads — the Attribute row, the string form and metadata. Attributes::for($model)->get() and has() call the same methods:
$product->getAttachedAttributes(); // Collection: ['color' => 'white', 'rating' => 5]
$product->getAttachedAttributeValue('color'); // typed value (mixed)
$product->getAttachedAttributeValueAsString('rating'); // '5' (string view)
$product->getAttachedAttribute('color'); // Attribute model or null
$product->hasAttachedAttribute('color'); // bool
$product->getAttachedAttributeMeta('color'); // Collection|nullThe attr() accessor
attr() returns a typed value object — one entry point instead of a method per type. Defaults defined in a definition are applied automatically:
$product->attr('rating')->int(); // ?int (falls back to the defined default)
$product->attr('price')->float(); // ?float
$product->attr('on_sale')->bool(); // ?bool
$product->attr('tags')->array(); // ?array
$product->attr('published_at')->date(); // ?Carbon
$product->attr('color')->string(); // ?string
$product->attr('color')->raw(); // underlying typed value
$product->attr('color')->isNull(); // bool
(string) $product->attr('color'); // '' when unset, else the valueTyped readers
The per-type readers still work. Each returns null when the attribute is unset and has no default:
$product->attributeInt('rating'); // ?int
$product->attributeFloat('price'); // ?float
$product->attributeBool('on_sale'); // ?bool
$product->attributeArray('tags'); // ?array (a scalar is wrapped in an array)
$product->attributeDate('published_at'); // ?CarbonEager loading
Every read method is eager-loading aware: load attachedAttributes first and reads run against the loaded relation instead of querying per call:
$products = Product::query()->with('attachedAttributes')->get();
foreach ($products as $product) {
$product->getAttachedAttributeValue('color'); // read from the loaded relation
$product->attr('rating')->int(); // no query per call
}
$product->load('attachedAttributes'); // or load on a single modelBulk reads & exports
Besides the handle’s all(), toKeyValue() and keys(), attribute collections flatten to plain maps:
use RoundlyConsulting\Attributes\Models\Attribute;
$product->attachedAttributes()->get()->toKeyValue(); // array<name, value>
Attribute::collect()->keyByName(); // every stored attribute, keyed by nameBulk maps list stored rows only. Defaults are applied by single-value reads — getAttachedAttributeValue(), attr(), the typed readers and Attributes::for()->get() — and only when the attribute is not attached; an attached attribute holding null reads as null. getAttachedAttributes(), all() and toKeyValue() never apply them. Attribute::collect() loads every attribute row in the table, across all owners.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.