Vapor 4:如何将急切加载的父关系映射到不同的格式?
Vapor 4: how to map an eagerly loaded parent relation into a different format?
我正在为如何 return 一个包含父关系的模型,同时将急切加载的模型映射到不同的形式而苦苦挣扎。
让我们考虑以下 2 个模型:Course
和 User
。
final class Course: Model, Content {
static let schema = "courses"
@ID(key: .id)
var id: UUID?
@Field(key: "name")
var name: String
@Parent(key: "teacher_id")
var teacher: User
init() { }
}
final class User: Model, Content {
static let schema = "users"
@ID(key: .id)
var id: UUID?
@OptionalField(key: "avatar")
var avatar: String?
@Field(key: "name")
var name: String
@Field(key: "private")
var somePrivateField: String
init() { }
}
我有一条这样的路线,其中 return 是一系列课程:
func list(req: Request) throws -> EventLoopFuture<[Course]> {
return Course
.query(on: req.db)
.all()
}
结果 JSON 看起来像这样:
[
{
"id": 1,
"name": "Course 1",
"teacher": {
"id": 1
}
]
我想要的是教师对象是 returned,通过在查询中添加 .with(\.$teacher)
就很容易了。 Vapor 4 确实让这一切变得非常简单!
[
{
"id": 1,
"name": "Course 1",
"teacher": {
"id": 1,
"name": "User 1",
"avatar": "https://www.example.com/avatar.jpg",
"somePrivateField": "super secret internal info"
}
]
我的问题是:整个 User
对象都是 returned,几乎所有字段,甚至是我不想创建的字段 public.
将教师信息转换为不同版本的 User
模型(例如 PublicUser
)的最简单方法是什么?这是否意味着我必须为 Course
创建一个 DTO,将我的数组从 [Course]
映射到 [PublicCourse]
,复制所有属性,并在 Course
模型更改时使它们保持同步等等?
这似乎是很多样板文件,未来有很大的错误余地。很想听听是否有更好的选择。
您可以通过先对原始模型进行编码,然后将其解码为具有较少字段的结构来实现。因此,要将存储在 course
中的 Course
实例转换为 PublicCourse
,您可以执行以下操作:
struct PublicCourse: Decodable {
//...
let teacher: PublicUser
//...
}
let course:Course = // result of Course.query including `with(\.$teacher)`
let encoder = JSONEncoder()
let decoder = JSONDecoder()
let data = try encoder.encode(course)
let publicCourse = try decoder.decode(PublicCourse.self, from: data)
注意结构中的 PublicUser
字段。如果这是精简版,你可以一次性生成你的最小JSON。
好的,这个方法怎么样?创建第二个 Model
,称为 Teacher
,例如,它被定义为要在 API/JSON 中公开的 User
字段的子集,并且具有相同的 schema/table 名称为 User
:
final class Teacher: Model, Content {
static let schema = "users"
// public fields
}
然后将 Course
中的关系更改为:
@Parent(key: "teacher_id")
var teacher: Teacher
您的原始查询将保持不变,只是返回减少的字段集。如果您使用 Teacher
只读,它肯定会起作用。无需为 Teacher
创建 Migrations
,因为基础 table 存在。我看不到避免包含 ID 的方法,但这可能不是问题。
我正在为如何 return 一个包含父关系的模型,同时将急切加载的模型映射到不同的形式而苦苦挣扎。
让我们考虑以下 2 个模型:Course
和 User
。
final class Course: Model, Content {
static let schema = "courses"
@ID(key: .id)
var id: UUID?
@Field(key: "name")
var name: String
@Parent(key: "teacher_id")
var teacher: User
init() { }
}
final class User: Model, Content {
static let schema = "users"
@ID(key: .id)
var id: UUID?
@OptionalField(key: "avatar")
var avatar: String?
@Field(key: "name")
var name: String
@Field(key: "private")
var somePrivateField: String
init() { }
}
我有一条这样的路线,其中 return 是一系列课程:
func list(req: Request) throws -> EventLoopFuture<[Course]> {
return Course
.query(on: req.db)
.all()
}
结果 JSON 看起来像这样:
[
{
"id": 1,
"name": "Course 1",
"teacher": {
"id": 1
}
]
我想要的是教师对象是 returned,通过在查询中添加 .with(\.$teacher)
就很容易了。 Vapor 4 确实让这一切变得非常简单!
[
{
"id": 1,
"name": "Course 1",
"teacher": {
"id": 1,
"name": "User 1",
"avatar": "https://www.example.com/avatar.jpg",
"somePrivateField": "super secret internal info"
}
]
我的问题是:整个 User
对象都是 returned,几乎所有字段,甚至是我不想创建的字段 public.
将教师信息转换为不同版本的 User
模型(例如 PublicUser
)的最简单方法是什么?这是否意味着我必须为 Course
创建一个 DTO,将我的数组从 [Course]
映射到 [PublicCourse]
,复制所有属性,并在 Course
模型更改时使它们保持同步等等?
这似乎是很多样板文件,未来有很大的错误余地。很想听听是否有更好的选择。
您可以通过先对原始模型进行编码,然后将其解码为具有较少字段的结构来实现。因此,要将存储在 course
中的 Course
实例转换为 PublicCourse
,您可以执行以下操作:
struct PublicCourse: Decodable {
//...
let teacher: PublicUser
//...
}
let course:Course = // result of Course.query including `with(\.$teacher)`
let encoder = JSONEncoder()
let decoder = JSONDecoder()
let data = try encoder.encode(course)
let publicCourse = try decoder.decode(PublicCourse.self, from: data)
注意结构中的 PublicUser
字段。如果这是精简版,你可以一次性生成你的最小JSON。
好的,这个方法怎么样?创建第二个 Model
,称为 Teacher
,例如,它被定义为要在 API/JSON 中公开的 User
字段的子集,并且具有相同的 schema/table 名称为 User
:
final class Teacher: Model, Content {
static let schema = "users"
// public fields
}
然后将 Course
中的关系更改为:
@Parent(key: "teacher_id")
var teacher: Teacher
您的原始查询将保持不变,只是返回减少的字段集。如果您使用 Teacher
只读,它肯定会起作用。无需为 Teacher
创建 Migrations
,因为基础 table 存在。我看不到避免包含 ID 的方法,但这可能不是问题。