Type Validation
Every setting has a declared type. Every value written to it is coerced to that type before it is stored.
Where the type lives
You declare a setting's type in code, with Core's #[Setting] attribute on the artifact class that owns the setting (Reading & Writing):
use LaravelUi5\Core\Parameters\Attributes\Setting;
use LaravelUi5\Core\Parameters\Enums\ValueType;
#[Setting(key: 'billing.invoice.dueDays', type: ValueType::Integer, default: 30, note: 'Payment term in days.')]ui5:sync stores the type on the setting's row in sdk_settings. value_type holds the ValueType, and for Model and ModelArray settings model_class holds the Eloquent model class. A new override row copies both from the Platform row. A write is always checked against the type on the Platform row.
The declared default is stored as given at sync, without coercion. Give it a value of the right type.
Coercion on write
When an override is written, the settings writer coerces the value to the setting's value_type (and model_class). The string "42" becomes the integer 42. A value that cannot be coerced throws an InvalidArgumentException, and nothing is stored.
Reads return the stored JSON decoded, so a value you read back has its declared type.
Supported types
The list of setting types is Core's ValueType enum (LaravelUi5\Core\Parameters\Enums\ValueType):
| Type | Stored value | On write |
|---|---|---|
String | string | cast to string |
Integer | integer | must be numeric, then cast |
Float | float | must be numeric, then cast |
Boolean | boolean | accepts true/false, 1/0, "yes"/"no", "on"/"off", in any case; an empty string is false; anything else is rejected |
StringArray | array of strings | must be an array; each element goes through the String rule |
IntegerArray | array of integers | must be an array; each element goes through the Integer rule |
FloatArray | array of floats | must be an array; each element goes through the Float rule |
BooleanArray | array of booleans | must be an array; each element goes through the Boolean rule |
Date | ISO-8601 string | parsed and stored as an ISO-8601 timestamp with offset; unparseable input is rejected |
Model | integer id | a positive id of an existing model_class row; the model must use the primary key id. Declare modelClass: without it, or with a class that does not exist, the application does not boot |
ModelArray | array of integer ids | positive ids, duplicates removed; every id must exist in model_class |
DateTime | ISO-8601 string | parsed and stored as an ISO-8601 timestamp with offset; unparseable input is rejected |
Decimal | string | must be numeric, and is kept as a string — see below |
An array is validated exactly as hard as the single value would be: each element runs through the scalar rule of its own type, and the first one that fails rejects the whole write, naming its key — Element [2]: Invalid integer value. Keys are preserved, not renumbered.
Until 1.2.0 the elements were cast instead. That was not a weaker check but a different answer: (bool) "false" is true, so a BooleanArray of disabled flags stored as enabled, and (int) "none" is 0, which is indistinguishable from a caller who meant zero. The scalar Boolean rule had always read "false" correctly, and Integer had always refused "none" — the array types were the one place where the same input meant something else.
Decimal is a string on purpose
Decimal exists because Float is binary: 19.99 does not survive the round trip exactly, and the trailing zero in 0.10 does not survive at all. Storing the value as the string you wrote keeps both. Read it back with your own arbitrary-precision arithmetic (bcmath, brick/math, a decimal column) rather than casting it to float — the cast is the loss the type was chosen to avoid.
DateTime and Decimal store exactly what ParameterValueCaster writes for the matching ParameterType, which is also the form a slot value has when it reaches your code. A slot's own values never pass through this validator: every #[Slot] expands into a synthetic setting, but the settings writer refuses to override one — the parameter chain reads only its default, and a person's value lives with the actor.