Reputation: 5960
I have a model Campaign, that has multiple Months:
final class Campaign: Content, SQLiteModel {
var id: Int?
var name: String
var months: Children<Campaign, Month> {
return children(\.campaignID)
}
}
When I want to return a Campaign in the most basic way, it doesn't include the Months, as computed properties are not Codable as I understand.
func getOneHandler(_ req: Request) throws -> Future<Campaign> {
return try req.parameters.next(Campaign.self)
}
So, I made a new struct to hold the full object I want to return
struct FullCampaignData: Content {
let id: Int
let name: String
var months: [Month]?
}
And then modified my route handler like this:
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)
}
}
}
This does work. However, it seems like a lot of work and a lot of boilerplate to do this. What's the normal Swifty "Vapory" way to deal with child objects, or other computed objects in general? Right now it seems to come down to a bunch of different versions of your models (for creation, for returning, the actual full internal one) and then converting between them, but I am hoping there is something I am missing here? Because it's so easy to forget to add a newly added model property also to that special public model.
Or at the very least, if converting between models in indeed the recommended way, is there a way doesn't need all these nested maps/flatmaps in the route handler?
Upvotes: 3
Views: 967
Reputation: 5656
You could use SwifQL lib for complex queries
I'm not sure that with SQLite it is possible to subquery Months
, but with PostgreSQL
it is really easy cause it supports JSON
So for PostgreSQL your query may look like
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)
}
or
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)
}
Upvotes: 3