This page covers two related topics: how FoxDB converts database values to PHP types when you read a model's attributes (casting), and how models and collections are converted back into arrays and JSON for output (serialization).
PDO always returns raw values as strings (or null) — even for integer, boolean, and JSON columns. Casts tell FoxDB how to convert these raw values into proper PHP types automatically whenever you read an attribute, and convert them back when you write.
class User extends Model
{
protected array $casts = [
'is_active' => 'bool',
'age' => 'int',
'score' => 'float',
'settings' => 'array',
'born_at' => 'datetime',
];
}| Cast | Aliases | PHP type |
|---|---|---|
int |
integer |
int |
float |
double, real |
float |
bool |
boolean |
bool |
string |
— | string |
array |
json |
array (JSON encoded/decoded) |
object |
— | stdClass (JSON decoded) |
datetime |
date |
DateTime |
immutable_datetime |
— | DateTimeImmutable |
$user = User::find(1);
$user->is_active; // true or false — not "1" or "0"
$user->age; // 25 (int) — not "25" (string)
$user->score; // 99.5 (float)
$user->settings; // ['theme' => 'dark', 'lang' => 'fa'] — decoded from JSON
$user->born_at; // DateTime instance — can call ->format(), ->diff(), etc.Casts work in both directions. When you assign a value and call save(), FoxDB converts it back to the correct format for storage.
$user->settings = ['theme' => 'light'];
$user->save(); // stored as '{"theme":"light"}'
$user->is_active = true;
$user->save(); // stored as 1array decodes JSON into a PHP array (accessed with []); object decodes into a stdClass (accessed with ->):
protected array $casts = [
'settings' => 'array', // $user->settings['theme']
'metadata' => 'object', // $user->metadata->theme
];datetime returns a mutable DateTime instance. immutable_datetime returns a DateTimeImmutable instance, which is safer when you want to guarantee the original value cannot be accidentally modified elsewhere in your code.
protected array $casts = [
'created_at' => 'datetime', // DateTime
'expires_at' => 'immutable_datetime', // DateTimeImmutable
];$user->hasCast('age'); // true
$user->hasCast('name'); // falseA null value is never passed through a cast — it remains null regardless of the declared cast type.
protected array $casts = ['settings' => 'array'];
// If settings is NULL in the database:
$user->settings; // null, not []Serialization converts a model — or a Collection of models — into a plain array or JSON string, ready for an API response, a view, or storage.
toArray() returns an associative array of the model's attributes, with:
- All
$castsapplied - All
$hiddenattributes removed - Any loaded relations included
class User extends Model
{
protected array $hidden = ['password'];
protected array $casts = ['is_active' => 'bool'];
}
$user = User::with('posts')->find(1);
$user->toArray();
// [
// 'id' => 1,
// 'name' => 'Ali',
// 'is_active' => true,
// 'posts' => [ ... ],
// ]
// 'password' is excludedtoJson() returns the result of toArray() encoded as a JSON string.
$user->toJson(); // '{"id":1,"name":"Ali","is_active":true,...}'
(string) $user; // same as toJson()Model and Collection both implement JsonSerializable, so passing either directly to json_encode() (or returning them inside an array from a controller) produces the same result as toJson() / toArray().
$user = User::where('email', 'ali@example.com')->first();
json_encode($user); // same as $user->toJson()
return ['ok' => true, 'user' => $user]; // 'user' serializes correctlyWhen toArray() is called on a Collection, each item's own toArray() is used — so hidden fields, casts, and relations are correctly applied per item, not just on the outer collection.
$users = User::with('posts')->get();
$users->toArray(); // array of per-model arrays
json_encode($users); // same resultNever cast a model directly with (array):
$arr = (array) $user; // WRONGPHP's object cast exposes the model's internal protected properties, producing keys with null-byte prefixes (e.g. "\0*\0attributes"). This is not valid output and will corrupt JSON responses. Always use ->toArray() or ->toJson() instead.
$arr = $user->toArray(); // correctA typical API endpoint combines casting and serialization without any manual conversion:
public function show($id)
{
$user = User::with('posts')->findOrFail($id);
return [
'ok' => true,
'user' => $user->toArray(),
];
}Here, is_active is already a boolean, settings is already an array, password is automatically excluded, and posts is included as an array of post arrays — all without any extra code in the controller.