Files
TinyORM/docs/tinyorm/serialization.mdx
silverqx 978f9160fb docs bugfix class vs className 😱
[skip ci]
2024-08-16 20:40:22 +02:00

293 lines
10 KiB
Plaintext

---
sidebar_position: 4
sidebar_label: Serialization
description: TinyORM models serialization allows you to serialize models and collection of models including all nested relations to JSON. It also supports converting to vectors or maps and allows controlling a custom date format during serialization.
keywords: [c++ orm, orm, serialization, json, toJson, serializing models, serializing relations, serializing collections, converting, toList, toMap]
---
import Link from '@docusaurus/Link'
# TinyORM: Serialization
- [Introduction](#introduction)
- [Serializing Models & Collections](#serializing-models-and-collections)
- [Serializing To Vectors & Maps](#serializing-to-vectors-and-maps)
- [Serializing To JSON](#serializing-to-json)
- [Hiding Attributes From JSON](#hiding-attributes-from-json)
- [Appending Values To JSON](#appending-values-to-json)
- [Date Serialization](#date-serialization)
## Introduction
<div className="api-stability alert alert--success">
<Link to='/stability#stability-indexes'>__Stability: 2__</Link> - Stable
</div>
When building APIs using TinyORM, you will often need to convert your models and relationships to vectors, maps, or JSON. TinyORM includes convenient methods for making these conversions, as well as controlling which attributes are included in the serialized representation of your models.
## Serializing Models & Collections {#serializing-models-and-collections}
### Serializing To Vectors & Maps {#serializing-to-vectors-and-maps}
To convert a model and its loaded [relationships](tinyorm/relationships.mdx) to a vector, you should use the `toList` or `toMap` methods. This methods are recursive, so all attributes and all relations (including the relations of relations) will be converted to vectors:
```cpp
using Models::User;
auto user = User::with("roles")->first();
return user->toList();
return user->toMap();
```
The `attributesToList` or `attributesToMap` methods may be used to convert a model's attributes to a vector or map but not its relationships:
```cpp
auto user = User::first();
return user->attributesToList();
return user->attributesToMap();
```
You may also convert entire [collections](tinyorm/collections.mdx) of models to vectors or maps by calling the [`toList`](tinyorm/collections.mdx#method-tolist) or [`toMap`](tinyorm/collections.mdx#method-tomap) methods on the collection instance:
```cpp
ModelsCollection<User> users = User::with("roles")->all();
return users.toList();
return users.toMap();
```
### Serializing To JSON
To convert a model to JSON, you should use the `toJson` method. Like `toList` or `toMap`, the `toJson` method is recursive, so all attributes and relations will be converted to JSON. You may also specify any JSON encoding options that are supported by [QJsonDocument::toJson](https://doc.qt.io/qt/qjsondocument.html#toJson):
```cpp
using Models::User;
auto user = User::with("roles")->find(1);
return user->toJson();
return user->toJson(QJsonDocument::Indented);
```
You may also convert entire [collections](tinyorm/collections.mdx) of models to JSON by calling the [`toJson`](tinyorm/collections.mdx#method-tojson) method on the collection instance:
```cpp
ModelsCollection<User> users = User::with("roles")->findMany({1, 2});
return users.toJson();
```
You can also convert models to the [`QJsonObject`](https://doc.qt.io/qt/qjsonobject.html) and [`QJsonDocument`](https://doc.qt.io/qt/qjsondocument.html) using the `toJsonArray` and `toJsonDocument` methods and collection of models to [`QJsonArray`](https://doc.qt.io/qt/qjsonarray.html) and [`QJsonDocument`](https://doc.qt.io/qt/qjsondocument.html) using the [`toJsonArray`](tinyorm/collections.mdx#method-tojsonarray) and [`toJsonDocument`](tinyorm/collections.mdx#method-tojsondocument) methods.
#### Relationships
When a TinyORM model is converted to JSON, its loaded relationships will automatically be included as attributes on the JSON object. Also, though TinyORM relationship methods are defined using "camelCase" method names, a relationship's JSON attributes will be "snake_case".
This behavior is affected and can be overridden by the `u_snakeAttributes` static data member:
```cpp
#include <orm/tiny/model.hpp>
using Orm::Tiny::Model;
class Album final : public Model<Album, AlbumImage>
{
friend Model;
using Model::Model;
/*! Indicates whether attributes are snake_cased during serialization. */
inline static const bool u_snakeAttributes = false;
};
```
## Hiding Attributes From JSON
Sometimes you may wish to limit the attributes, such as passwords, that are included in your model's vector, map, or JSON representation. To do so, add a `u_hidden` static data member to your model. Attributes that are listed in the `u_hidden` data member set will not be included in the serialized representation of your model:
```cpp
#include <orm/tiny/model.hpp>
using Orm::Tiny::Model;
class User final : public Model<User>
{
friend Model;
using Model::Model;
/*! The attributes that should be hidden during serialization. */
inline static std::set<QString> u_hidden {"password"};
};
```
:::note
To hide relationships, add the relationship's method name to your TinyORM model's `u_hidden` static data member.
:::
Alternatively, you may use the `u_visible` static data member to define an "allow list" of attributes that should be included in your model's vector, map, and JSON representation. All attributes that are not present in the `u_visible` set will be hidden during serialization:
```cpp
#include <orm/tiny/model.hpp>
using Orm::Tiny::Model;
class User final : public Model<User>
{
friend Model;
using Model::Model;
/*! The attributes that should be visible during serialization. */
inline static std::set<QString> u_visible {
"first_name", "last_name",
};
};
```
#### Temporarily Modifying Attribute Visibility
If you would like to make some typically hidden attributes visible on a given model instance, you may use the `makeVisible` method. The `makeVisible` method returns a model reference:
```cpp
return user.makeVisible("attribute").toMap();
return user.makeVisible({"id", "name"}).toMap();
```
Likewise, if you would like to hide some attributes that are typically visible, you may use the `makeHidden` method.
```cpp
return user.makeHidden("attribute").toList();
return user.makeHidden({"id", "name"}).toList();
```
If you wish to temporarily override all of the visible or hidden attributes, you may use the `setVisible` and `setHidden` methods respectively:
```cpp
return user.setVisible({"id", "name"}).toMap();
return user.setHidden({"email", "password", "note"}).toJson();
```
You can also clear all visible and hidden attributes or determine whether a visible / hidden attribute is defined:
```cpp
user.clearVisible();
user.clearHidden();
return user.hasVisible("name");
return user.hasHidden("password");
```
## Appending Values To JSON
Occasionally, when converting models to vector, map, or JSON, you may wish to add attributes that do not have a corresponding column in your database. To do so, first define an [accessor](tinyorm/casts.mdx#accessors) for the value:
```cpp
#include <orm/tiny/model.hpp>
using Orm::Tiny::Model;
class User final : public Model<User>
{
friend Model;
using Model::Model;
protected:
/*! Accessor to determine if the user is an administrator. */
Attribute isAdmin() const noexcept
{
return Attribute::make(/* get */ []() -> QVariant
{
return QStringLiteral("yes");
});
}
};
```
If you would like the accessor to always be appended to your model's vector, map, and JSON representations, you may add the attribute name to the `u_appends` data member set of your model. Note that attribute names are typically referenced using their "snake_case" serialized representation, even though the accessor's method name is defined using "camelCase":
```cpp
#include <orm/tiny/model.hpp>
using Orm::Tiny::Model;
class User final : public Model<User>
{
friend Model;
using Model::Model;
/*! Map of mutator names to methods. */
inline static const QHash<QString, MutatorFunction> u_mutators {
{"is_admin", &User::isAdmin},
};
/*! The attributes that should be appended during serialization. */
std::set<QString> u_appends {"is_admin"};
};
```
Once the attribute has been added to the `u_appends` set, it will be included in both the model's vector, map, and JSON representations. Attributes in the `u_appends` set will also respect the `u_visible` and `u_hidden` attribute settings configured on the model.
Special note should be given to the `u_mutators` static data member map, which maps accessors' attribute names to its methods. This data member is __required__ because C++ does not currently support reflection.
#### Appending At Run Time
At runtime, you may instruct a model instance to append additional attributes using the `append` method. Or, you may use the `setAppends` method to override the entire set of appended attributes for a given model instance:
```cpp
return user.append("is_admin").toList();
return user.append({"is_admin", "is_banned"}).toMap();
return user.setAppends({"is_admin"}).toJson();
```
And you can also clear all appends or determine whether an append is defined:
```cpp
user.clearAppends();
return user.hasAppend("is_admin");
```
## Date Serialization
#### Customizing The Default Date Format {#customizing-the-default-date-format}
You may customize the default serialization format by overriding the `serializeDate` and `serializeDateTime` methods. These methods do not affect how your dates are formatted for storage in the database:
```cpp
/*! Prepare a date for vector, map, or JSON serialization. */
QString serializeDate(const QDate date)
{
return date.toString("yyyy-MM-dd");
}
/*! Prepare a datetime for vector, map, or JSON serialization. */
QString serializeDateTime(const QDateTime &datetime)
{
return datetime.toUTC().toString("yyyy-MM-ddTHH:mm:ssZ");
}
```
#### Customizing The Date Format Per Attribute
You may customize the serialization format of individual TinyORM date attributes by specifying the date format in the model's [cast declarations](tinyorm/casts.mdx#attribute-casting):
```cpp
/*! The attributes that should be cast. */
inline static std::unordered_map<QString, CastItem> u_casts {
{"birthday", {CastType::CustomQDate, "yyyy-MM-dd"}},
{"joined_at", {CastType::CustomQDateTime, "yyyy-MM-dd HH:00"}},
};
```