Spec sheet (attributes)
Products carry a typed, validated spec sheet via attributes-for-laravel — the descriptive data that complements variant options (which define SKUs). Define the schema under shops.attributes.definitions; each entry has a type (string, integer, float, boolean, array, datetime) plus optional rules, default, required, unique and encrypted. Definitions are registered at boot:
// config/shops.php
'attributes' => ['definitions' => [
'material' => ['type' => 'string'],
'weight' => ['type' => 'integer', 'rules' => ['min:0']],
]],Setting and reading values
$product->attachAttribute('material', 'wool');
$product->attachAttribute('weight', 500);
$product->attachAttributes(['material' => 'wool', 'weight' => 320]);
$product->syncAttributes(['material' => 'cotton']); // attach/replace, prune the rest
$product->detachAttribute('weight');
$product->attr('material')->string(); // 'wool'
$product->attr('weight')->int(); // 500
$product->attributeInt('weight'); // ?int
$product->validateAttributes(); // enforce the definitions' rules
$product->history('material'); // value-change historyFiltering and sorting
Query scopes build faceted catalogue browsing — wool only, lightest first:
Product::query()->whereAttribute('material', 'wool')->get();
Product::query()->whereAttributeIn('material', ['wool', 'cotton'])->get();
Product::query()->whereAttributeBetween('weight', 100, 500)->get();
Product::query()->whereHasAttribute('material')->get();
Product::query()->orderByAttribute('weight', 'desc')->get();Definitions are read strictly at boot: an entry that is not an array, a type typo, a required flag that is not a boolean or rules that are not an array throw an InvalidConfigurationException naming the shops key (for example shops.attributes.definitions.colour.type) instead of being skipped.
Every write is checked against its definition before anything is stored: a value that fails the type or rules throws InvalidAttributeValueException, whose attributeName, messages() and errorBag() carry the details. Turn on the attributes package’s attributes.strict to also reject undefined attribute names with UnknownAttributeException.
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.