Vapor 3: при возврате модели, как легко также вернуть дочерние объекты
У меня есть модельная кампания с несколькими месяцами:
final class Campaign: Content, SQLiteModel {
var id: Int?
var name: String
var months: Children<Campaign, Month> {
return children(\.campaignID)
}
}
Когда я хочу вернуть Campaign самым простым способом, она не включает месяцы, так как вычисляемые свойства не являются Codable, как я понимаю.
func getOneHandler(_ req: Request) throws -> Future<Campaign> {
return try req.parameters.next(Campaign.self)
}
Итак, я создал новую структуру для хранения полного объекта, который я хочу вернуть
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 для работы с дочерними объектами или другими вычисляемыми объектами в целом? Прямо сейчас кажется, что все сводится к куче разных версий ваших моделей (для создания, для возврата, к фактической полной внутренней), а затем к конвертации между ними, но я надеюсь, что здесь что-то не хватает? Потому что так легко забыть добавить недавно добавленное свойство модели к этой специальной общедоступной модели.
Или, по крайней мере, если преобразование между моделями действительно рекомендуемым способом, есть ли способ, которому не нужны все эти вложенные карты / плоские карты в обработчике маршрута?
1 ответ
Вы можете использовать SwifQL lib для сложных запросов
Я не уверен, что с 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)
}