Return Children and Siblings values in response

214 views Asked by At

It's probably a trivial question, but I couldn't solve it and didn't find the answer.
I have 3 tables related to each other as Parent-Child and Siblings. And I want to return some values form all of the tables and the same JSON response.

final class ClimbingGym: Model, Content {

    static let schema = "climbing_gyms"

    @ID(key: .id)
    var id: UUID?

    @Field(key: "name")
    var name: String

    @Field(key: "description")
    var description: String

    @Siblings(through: ClimbingDisciplineClimbingGym.self, from: \.$gym, to: \.$discipline)
    var disciplines: [ClimbingDiscipline]

    @Children(for: \.$gym)
    var socialNetworks: [SocialNetwork]

    init() { }

    init(
        id: UUID? = nil,
        name: String,
        description: String,
    ) {
        self.id = id
        self.name = name
        self.description = description
    }
}

enum ClimbingDisciplineType: String, Codable, CaseIterable, Equatable {
    static let schema = "climbing_discipline_type"

    case lead
    case boulder
    case speed
}

final class ClimbingDiscipline: Model, Content {

    static let schema = "climbing_disciplines"

    @ID(key: .id)
    var id: UUID?

    @Enum(key: "type")
    var type: ClimbingDisciplineType

    init() { }

    init(
        id: UUID? = nil,
        type: ClimbingDisciplineType
    ) {
        self.id = id
        self.type = type
    }
}

final class SocialNetwork {
    static let schema = "social_networks"

    @ID(key: .id)
    var id: UUID?

    @Field(key: "link")
    var link: String

    @Parent(key: "gym_id")
    var gym: ClimbingGym

    init() { }

    init(
        id: UUID? = nil,
        link: String,
        gym: ClimbingGym
    ) throws {
        self.id = id
        self.link = link
        self.$gym.id = try gym.requireID()
    }
}

And I want to return that model:

struct ClimbingGymResponse: Codable, Content {
    let id: UUID
    let name: String
    let description: String
    let disciplines: [ClimbingDisciplineType]
    let socialNetworks: [String]
}

so the query that I'm using now looks like that

func getAll(req: Request) throws -> EventLoopFuture<[ClimbingGymResponse]> {
   ClimbingGym
      .query(on: req.db)
      .join(children: \ClimbingGym.$socialNetworks)
      .join(siblings: \ClimbingGym.$disciplines)
      .all()    
}

and it obviously doesn't work, because it returns [ClimbingGym] instead of [ClimbingGymResponse].

So how can I transform one to another?
I have problems with filling disciplines: [ClimbingDisciplineType] and socialNetworks: [String] field for each gym

Thank you!

1

There are 1 answers

2
0xTim On BEST ANSWER

You can map the array of results into the type you want. So

ClimbingGym
  .query(on: req.db)
  .with(\.$socialNetworks)
  .with(\.$disciplines)
  .all().flatMapThrowing { gyms in
    try gyms.map { try ClimbingGymResponse(id: $0.requireID(), ...) }
}