Vapor 3:当 return 创建模型时,如何轻松地 return 子对象
Vapor 3: when returning a model, how to easily also return child objects
我有一个模型广告系列,它有多个月份:
final class Campaign: Content, SQLiteModel {
var id: Int?
var name: String
var months: Children<Campaign, Month> {
return children(\.campaignID)
}
}
当我想以最基本的方式 return 广告系列时,它不包括月份,因为据我所知,计算属性不可编码。
func getOneHandler(_ req: Request) throws -> Future<Campaign> {
return try req.parameters.next(Campaign.self)
}
所以,我创建了一个新结构来保存我想要的完整对象 return
struct FullCampaignData: Content {
let id: Int
let name: String
var months: [Month]?
}
然后像这样修改我的路由处理程序:
func getOneHandler(_ req: Request) throws -> Future<FullCampaignData> {
return try req.parameters.next(Campaign.self).flatMap(to: FullCampaignData.self) { campaign in
return try campaign.months.query(on: req).all().map(to: FullCampaignData.self) { months in
return try FullCampaignData(id: campaign.requireID(), name: campaign.name, months: months)
}
}
}
这确实有效。然而,要做到这一点似乎需要大量工作和大量样板文件。一般 Swifty "Vapory" 处理子对象或其他计算对象的正常方式是什么?现在它似乎归结为一堆不同版本的模型(用于创建,用于 returning,实际的完整内部模型)然后在它们之间进行转换,但我希望我缺少一些东西这里?因为很容易忘记将新添加的模型 属性 也添加到那个特殊的 public 模型。
或者至少,如果确实以推荐的方式在模型之间进行转换,是否有一种方法不需要在路由处理程序中嵌套所有这些 maps/flatmaps?
您可以使用 SwifQL 库进行复杂查询
我不确定使用 SQLite 是否可以子查询 Months
,但是使用 PostgreSQL
这真的很容易,因为它支持 JSON
所以对于 PostgreSQL,您的查询可能看起来像
func getOneHandler(_ req: Request) throws -> Future<FullCampaignData> {
let monthsSubquery = SwifQL.select(Fn.array_agg(Fn.to_jsonb(Month.table)))
let query = SwifQL.select(Campaign.table.*, |monthsSubquery| => "months")
.from(Campaign.table)
.join(.leftOuter, Month.table, on: \Month.campaignID == \Campaign.id)
.execute(on: req, as: .psql)
.all(decoding: FullCampaignData.self)
}
或
func getOneHandler(_ req: Request) throws -> Future<FullCampaignData> {
let monthsSubquery = SwifQL.select(Fn.array_agg(Fn.to_jsonb(Month.table)))
.from(Month.table)
.where(\Month.campaignID == \Campaign.id)
let query = SwifQL.select(Campaign.table.*, |monthsSubquery| => "months")
.from(Campaign.table)
.execute(on: req, as: .psql)
.all(decoding: FullCampaignData.self)
}
我有一个模型广告系列,它有多个月份:
final class Campaign: Content, SQLiteModel {
var id: Int?
var name: String
var months: Children<Campaign, Month> {
return children(\.campaignID)
}
}
当我想以最基本的方式 return 广告系列时,它不包括月份,因为据我所知,计算属性不可编码。
func getOneHandler(_ req: Request) throws -> Future<Campaign> {
return try req.parameters.next(Campaign.self)
}
所以,我创建了一个新结构来保存我想要的完整对象 return
struct FullCampaignData: Content {
let id: Int
let name: String
var months: [Month]?
}
然后像这样修改我的路由处理程序:
func getOneHandler(_ req: Request) throws -> Future<FullCampaignData> {
return try req.parameters.next(Campaign.self).flatMap(to: FullCampaignData.self) { campaign in
return try campaign.months.query(on: req).all().map(to: FullCampaignData.self) { months in
return try FullCampaignData(id: campaign.requireID(), name: campaign.name, months: months)
}
}
}
这确实有效。然而,要做到这一点似乎需要大量工作和大量样板文件。一般 Swifty "Vapory" 处理子对象或其他计算对象的正常方式是什么?现在它似乎归结为一堆不同版本的模型(用于创建,用于 returning,实际的完整内部模型)然后在它们之间进行转换,但我希望我缺少一些东西这里?因为很容易忘记将新添加的模型 属性 也添加到那个特殊的 public 模型。
或者至少,如果确实以推荐的方式在模型之间进行转换,是否有一种方法不需要在路由处理程序中嵌套所有这些 maps/flatmaps?
您可以使用 SwifQL 库进行复杂查询
我不确定使用 SQLite 是否可以子查询 Months
,但是使用 PostgreSQL
这真的很容易,因为它支持 JSON
所以对于 PostgreSQL,您的查询可能看起来像
func getOneHandler(_ req: Request) throws -> Future<FullCampaignData> {
let monthsSubquery = SwifQL.select(Fn.array_agg(Fn.to_jsonb(Month.table)))
let query = SwifQL.select(Campaign.table.*, |monthsSubquery| => "months")
.from(Campaign.table)
.join(.leftOuter, Month.table, on: \Month.campaignID == \Campaign.id)
.execute(on: req, as: .psql)
.all(decoding: FullCampaignData.self)
}
或
func getOneHandler(_ req: Request) throws -> Future<FullCampaignData> {
let monthsSubquery = SwifQL.select(Fn.array_agg(Fn.to_jsonb(Month.table)))
.from(Month.table)
.where(\Month.campaignID == \Campaign.id)
let query = SwifQL.select(Campaign.table.*, |monthsSubquery| => "months")
.from(Campaign.table)
.execute(on: req, as: .psql)
.all(decoding: FullCampaignData.self)
}