如何从 Vapor 3 中的 JSON 响应中保存父子关系

Posted

技术标签:

【中文标题】如何从 Vapor 3 中的 JSON 响应中保存父子关系【英文标题】:How to save Parent-Child relation from a JSON response in Vapor 3 【发布时间】:2019-10-09 19:31:41 【问题描述】:

我正在使用 Vapor 3 开发一个 REST API。此 API 使用另一个 API 创建稍后将由应用程序使用的内容。 所以我创建了一个从这个 API(联赛和赛季)获取内容并将它们存储在我的 mysql 数据库中的函数。 API 的响应还包含我也想存储的嵌套对象,如果可能的话都在同一个请求中。以下是 API 响应:


"data": [
         
             "id": 271,
             "name": "Superliga",
             "current_season_id": 16020,
             "season": 
                 "data": 
                     "id": 16020,
                     "name": "2019/2020",
                     "league_id": 271,
                 
             
        
    ]

这是模型:

final class League: MySQLModel 

    var id: League.ID?

    var name: String

    var current_season_id: Season.ID

    var currentSeason: Parent<League, Season> 
        return parent(\League.current_season_id)
    


final class Season: MySQLModel 

    var id: Season.ID?

    var name: String

    var league_id: League.ID

    var league: Parent<Season, League> 
        return parent(\.league_id)
    

这是执行请求并存储在数据库中的函数。

func getLeagues(using context: CommandContext) throws -> EventLoopFuture<Void> 
    guard let url = URL(string: "SOME_API_URL") else  return .done(on: context.container) 

    let client = try context.container.client()
    return client.get(url).flatMap( (response) -> EventLoopFuture<Void> in // do the request
        let leagues = response.content.get([League].self, at: "data") // get the array of EventLoopFuture<[League]>

        return context.container.requestPooledConnection(to: .mysql).flatMap( (connection) -> EventLoopFuture<Void> in // connecto to DB

            let savedLeagues = leagues.flatMap(to: [League].self,  (flattenLeagues) -> EventLoopFuture<[League]> in
                return flattenLeagues.map  (league) -> EventLoopFuture<League> in

                    return league.create(orUpdate: true, on: connection) // save on the DB

                .flatten(on: context.container)
            )

            return savedLeagues.flatMap  (_) -> EventLoopFuture<Void> in
                return .done(on: context.container)
            
        )
    )

问题是:可以保存父子关系吗?我必须使用解码/编码功能手动完成吗? 我确实实现了编码/解码并创建了联赛,但不知道如何创建赛季以及在执行时如何保存所有内容league.create(orUpdate: true, on: connection)

如有任何帮助,将不胜感激。

【问题讨论】:

【参考方案1】:

在我看来,您可以先解码 APIModel,然后像这样将两个对象保存在 flatten 循环中

struct APILeague: Content 
    let id: League.ID
    let name: String
    let current_season_id: Season.ID
    struct _Season: Codable 
        let data: Season
    
    let season: _Season

final class League: MySQLModel 
    var id: League.ID?
    var name: String
    var current_season_id: Season.ID

    var currentSeason: Parent<League, Season> 
        return parent(\League.current_season_id)
    

    init (_ data: APILeague) 
        self.id = data.id
        self.name = data.name
        self.current_season_id = data.current_season_id
    


func getLeagues(using context: CommandContext) throws -> Future<Void> 
    guard let url = URL(string: "SOME_API_URL") else  return .done(on: context.container) 

    let client = try context.container.client()
    return client.get(url).flatMap  response in // do the request
        return response.content.get([APILeague].self, at: "data").flatMap  leagues in
            return context.container.requestPooledConnection(to: .mysql).flatMap  connection in // connecto to DB
                let operations = leagues.map  league in
                    return League(league).create(orUpdate: true, on: connection).flatMap  _ in
                        return league.season.data.create(orUpdate: true, on: connection).transform(to: ()) // transforming to Void
                     
                
                return operations.flatten(on: context.container)flatMap 
                    return .done(on: context.container)
                
            
        
    

【讨论】:

成功了,感谢您的回答。不过,为了处理映射而复制所有字段似乎是一种过度杀戮。我可能还会有我的模型的公共对象以及返回应用程序......所以可能有 3 个重复的对象。 一个问题,最后的返回不能只是return operations.flatten(on: context.container)而不是return operations.flatten(on: context.container).flatMap return .done(on: context.container) @Lucho 是的,它可能只是return operations.flatten(on: context.container) @Lucho 和这条线 guard let url = URL(string: "SOME_API_URL") else return .done(on: context.container) 我宁愿改成 guard let url = URL(string: "SOME_API_URL") else throw Abort(.internalServerError, reason: "API not available") @Lucho btw 我可以在 Discord 上以 imike#3049 的身份随时聊天

以上是关于如何从 Vapor 3 中的 JSON 响应中保存父子关系的主要内容,如果未能解决你的问题,请参考以下文章

Vapor `client.get`,转换并返回 json

将大型 JSON 直接从 Java 中的 REST API 响应流保存为文本

从 Vapor 3 到 Vapor 4 的 LingoVapor 包

从数据库加载数据并将其加载到 Vapor 3 中的视图的正确方法?

如何使用 Vapor 3 处理多部分请求

使用不同的数据库提供程序测试 Vapor 3