Vapor 3: при возврате модели, как легко также вернуть дочерние объекты - PullRequest
3 голосов
/ 10 марта 2019

У меня есть модель Campaign, которая имеет несколько месяцев:

final class Campaign: Content, SQLiteModel {
  var id: Int?
  var name: String
  var months: Children<Campaign, Month> {
    return children(\.campaignID)
  }
}

Когда я хочу вернуть кампанию самым простым способом, она не включает месяцы, так как вычисляемые свойства не являются кодируемымикак я понимаю.

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 Ответ

3 голосов
/ 10 марта 2019

Вы можете использовать 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)
}
...