Eloquent: Serialization
Eloquent models can be serialised to maps and JSON strings with a single call, with support for hiding sensitive attributes and appending computed values to every serialised output.
On this page
Introduction#
When building APIs or passing data to JavaScript frontends, you will often need to convert your models to arrays or JSON. Magic includes convenient methods for these conversions while controlling which attributes are included.
Serializing to Arrays#
Convert a model to an array using toMap():
final user = await User.find(1);
final array = user?.toMap();
// {
// 'id': 1,
// 'name': 'John Doe',
// 'email': '[email protected]',
// 'created_at': '2024-01-15T10:30:00.000Z',
// 'updated_at': '2024-01-15T10:30:00.000Z',
// }
Converting Collections#
final users = await User.all();
final array = users.map((u) => u.toMap()).toList();
Serializing to JSON#
Convert a model to JSON using toJson():
final user = await User.find(1);
final json = user?.toJson();
// '{"id":1,"name":"John Doe","email":"[email protected]",...}'
Hiding Attributes#
Model-Level Hidden Attributes#
Define attributes that should never be serialised:
class User extends Model {
@override
List<String> get hidden => ['password', 'remember_token', 'api_key'];
}
Now when you call toMap() or toJson(), these attributes are excluded:
final user = await User.find(1);
print(user?.toMap());
// {
// 'id': 1,
// 'name': 'John Doe',
// 'email': '[email protected]',
// // password, remember_token, api_key are NOT included
// }
Runtime Hidden Attributes#
Hide additional attributes on one model instance:
final user = await User.find(1);
final array = user?.makeHidden(['email', 'phone']).toMap();
// email and phone are hidden for this instance
makeHidden, makeVisible and append return the same model and change it: every later toMap() or toJson() on that instance keeps the change, while other instances are untouched.
Making Hidden Attributes Visible#
Show attributes the model hides by default:
final user = await User.find(1);
final array = user?.makeVisible(['password']).toMap();
// password is included for this instance
Appending Attributes#
Name attributes that toMap() must include even when the model's visible whitelist leaves them out:
class User extends Model {
@override
List<String> get visible => ['id', 'name'];
@override
List<String> get appends => ['is_admin'];
}
An appended name is read with getAttribute, so it is included only when the attribute exists and is not null, and a hidden attribute is never appended. A Dart getter such as bool get isAdmin is not called: Dart has no reflection, so to serialise a computed value, store it with setAttribute first.
The stored value is a real attribute of the model, not an output-only field: toArray() is toMap(), so a later save() includes it in the payload it sends to the API and the local store. Build output-only values on a separate map when the backend must not receive them.
final user = await User.find(1);
user?.setAttribute('is_admin', true);
print(user?.toMap());
// {
// 'id': 1,
// 'name': 'John Doe',
// 'is_admin': true, // Appended
// }
Runtime Appends#
Append additional attributes on one model instance:
final user = await User.find(1);
final array = user?.append(['avatar_url']).toMap();
API Response Example#
Combine serialisation options for a clean API payload:
final users = await User.all();
final payload = users
.map((user) => user.makeHidden(['password', 'remember_token']).toMap())
.toList();
makeHidden only hides these names when the model's visible list does not name them (see Hiding Attributes). With no visible list on User, this returns:
[
{
"id": 1,
"email": "[email protected]",
"first_name": "John",
"last_name": "Doe"
}
]