creating a parent child relationship in vapor 3

Viewed 595

I am using Vapor 3 to try and create just a sample project where I have a dish, the parent, and the reviews for the dish, the child. All the tutorials that I have been seeing haven't been very clear on how to create the relationship or they are using it in conjecture with leaf. I do not want to use leaf for this, I just want to be able to show all the reviews when for the dish when I give it's id, and it seems that it is different than it was for vapor 2. My 2 models are Dish and Review Dish.swift: The parent,

import Foundation
import Vapor
import FluentSQLite

final class Dish: Content {
    var id: Int?
    var name: String
    var course: String
    var price: Double
    var imageURL: String
    var description: String

    init(name: String, course: String, price: Double, imageURL: String, description: String) {
        self.name = name
        self.course = course
        self.price = price
        self.imageURL = imageURL
        self.description = description

    }

}

extension Dish {
    var reviews: Children<Dish, Review> {
        return children(\.dishId)
    }
}

extension Dish: Parameter { }

extension Dish: SQLiteModel {
    static let entity: String = "Dishes"
}

extension Dish: Migration { }

Review.swift, the child,

import Foundation
import Vapor
import FluentSQLite

final class Review: Content {

    var id: Int?
    var title: String
    var body: String
    var dishId: Dish.ID

    init(title: String, body: String, dishId: Dish.ID) {
        self.title = title
        self.body = body
        self.dishId = dishId
    }

}

extension Review {
    var dish: Parent<Review, Dish> {
        return parent(\.dishId)
    }
}

extension Review: Migration { }

extension Review: SQLiteModel {
    static let entity: String = "Reviews"
}

extension Review: Parameter { }

the controller for Dish, DishController,

import Foundation
import Vapor
import FluentSQLite

class DishesController: RouteCollection {

    func boot(router: Router) throws {
        let dishesRoutes = router.grouped("api/dishes")
        dishesRoutes.get("/", use: getAll)
        dishesRoutes.get(Dish.parameter, use: getById)
        dishesRoutes.post(Dish.self, at: "/", use: createDish)
        dishesRoutes.delete(Dish.parameter, use: deleteDish)
    }

    func deleteDish(req: Request) throws -> Future<Dish> {
        return try req.parameters.next(Dish.self).delete(on: req)
    }


    func createDish(req: Request, dish: Dish) -> Future<Dish> {
        return dish.save(on: req)
    }

    func getAll(req: Request) -> Future<[Dish]> {
        return Dish.query(on: req).all()
    }

    func getById(req: Request) throws -> Future<Dish> {
        return try req.parameters.next(Dish.self)
    }


}

and the controller for reviews. ReviewController,

import Foundation
import Vapor
import FluentSQLite

class ReviewController: RouteCollection {
    func boot(router: Router) throws {
        let reviewRoutes = router.grouped("api/reviews")


        reviewRoutes.get("/", use: getAll)
        reviewRoutes.get(Review.parameter, use: getById)
        reviewRoutes.post(Review.self, at: "/", use: createReview)
        reviewRoutes.delete(Review.parameter, use: deleteReview)

    }


    func deleteReview(req: Request) throws -> Future<Review> {
        return try req.parameters.next(Review.self).delete(on: req)
    }

    func createReview(req: Request, review: Review) -> Future<Review> {
        return review.save(on: req)
    }


    func getAll(req: Request) -> Future<[Review]> {
        return Review.query(on: req).all()
    }

    func getById(req: Request) throws -> Future<Review> {
        return try req.parameters.next(Review.self)
    }
}

this is the routes.swift,

import Vapor


/// Register your application's routes here.
public func routes(_ router: Router) throws {


    router.get("/reviews", Dish.parameter,"dish") { request -> Future<Dish> in

        return try request.parameters.next(Review.self).flatMap(to: Dish.self) { review in

            return review.dish.get(on: request)
        }
    }


    let dishesController = DishesController()
    try router.register(collection: dishesController)
    let reviewController = ReviewController()
    try router.register(collection: reviewController)

}

I just want a simple one to many relationship where one dish can have many reviews, but when I use postman to try and access the reviews for the particular dish, all I get is an error. I know that I used the correct syntax in postman because I can use all the other requests from the controllers just fine, just not any for the relationships. Please tell me what i am missing, because I am getting confused as to what I am doing wrong. If there is anything else I can add please ask. Thank you very much

1 Answers

If you want to access all reviews for the particular dish, try the following code.

router.get("/dish", Dish.parameter,"reviews") { request -> Future<[Review]> in
        return try request.parameters.next(Dish.self).flatMap(to: [Review].self) { (dish) in
                    return try dish.reviews.query(on: request).all()
                }
}

Now In postman, pass a dish id as below:

GET: http://localhost:8080/dish/1/reviews

Related