Typed values
Values keep their real PHP type across write and read — no manual casting:
use RoundlyConsulting\Attributes\Facades\Attributes;
$attributes = Attributes::for($product);
$attributes->set('color', 'white'); // string
$attributes->set('rating', 5); // int
$attributes->set('on_sale', true); // bool
$attributes->set('price', 9.99); // float
$attributes->set('tags', ['a', 'b']); // array
$attributes->set('published_at', now()); // datetime
$attributes->get('rating'); // int(5)
$attributes->get('on_sale'); // true
$attributes->get('published_at'); // CarbonImmutable, in the app timezone
// Model shorthand — the same manager underneath
$product->attachAttribute('rating', 5);
$product->getAttachedAttributeValue('rating'); // int(5)How types are stored
Without a definition, the type is inferred from the PHP value you pass and stored in value_type next to the value, which is kept as text:
| PHP value | value_type | Stored as | Read back as |
|---|---|---|---|
| string (and anything else) | string | the string | string |
| int | integer | 5 → 5 as text | int |
| float | float | the shortest text that reads back as the same float | float |
| bool | boolean | 1 or 0 | bool |
| array | array | JSON | array |
| DateTimeInterface | datetime | ISO-8601 string in UTC | CarbonImmutable in the app timezone |
| null | string | SQL NULL (value_type is the defined type, if any) | null |
Datetimes are normalized to UTC, so one instant always has one stored string, and come back as the same instant in your app timezone. Floats keep their full precision.
Definitions set the type
With a definition, its type wins: the value is validated against that type and stored in it, so a request string “5” under an integer definition is stored and read back as the int 5:
use RoundlyConsulting\Attributes\DataTransferObjects\AttributeDefinitionData;
use RoundlyConsulting\Attributes\Enums\AttributeType;
use RoundlyConsulting\Attributes\Facades\Attributes;
Attributes::define(new AttributeDefinitionData('rating', AttributeType::Integer));
Attributes::for($product)->set('rating', $request->input('rating')); // '5' from the request
Attributes::for($product)->get('rating'); // int(5) — stored in the defined typeThe AttributeType enum
The supported types are the cases of RoundlyConsulting\Attributes\Enums\AttributeType. The enum also exposes the inference and conversion it uses internally:
use RoundlyConsulting\Attributes\Enums\AttributeType;
AttributeType::forValue(5); // AttributeType::Integer
AttributeType::forValue(now()); // AttributeType::DateTime
AttributeType::forValue('5'); // AttributeType::String_
AttributeType::Boolean->toStorage(true); // '1'
AttributeType::Array_->toStorage(['a']); // '["a"]'
AttributeType::Integer->fromStorage('42'); // 42
AttributeType::Float_->toStorage(0.1 + 0.2); // '0.30000000000000004' — full precision
AttributeType::DateTime->toStorage('2026-01-01 10:00:00+02:00'); // '2026-01-01T08:00:00+00:00' — UTC
AttributeType::Float_->isNumeric(); // true — integer and float are one familyShow 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.