天天看點

【Vapor】09 Chapter 10:Sibling Relationships

0x00 Chapter 10 Sibling Relationships

1.​

​Sibling relationships​

​​ describe a relationship that links ​

​two models​

​​ to each other

They are also known as ​​

​many-to-many​

​ relationships

Unlike ​

​parent-child​

​​ relationships,

there are no ​

​constraints​

​​ between models in a ​

​sibling​

​ relationship

For instance, if you model the relationship between ​

​pets​

​​ and ​

​toys​

​​ a pet can have ​

​one or more​

​ toys

and a toy can be used by ​

​one or more​

​ pets

In the TIL application, you’ll be able to categorize acronyms.

An acronym can be part of ​

​one or more​

​ categories

and a category can contain ​

​one or more​

​ acronyms

2.Category model

create a new file ​​

​Category.swift​

​ in Sources/App/Models

import Vapor
import Fluent

final class Category: Model, Content {
    static let schema = "categories"
    
    @ID
    var id: UUID?
    
    @Field(key: "name")
    var name: String
    
    init() {}
    
    init(id: UUID? = nil, name: String) {
        self.id = id
        self.name = name
    }
}
      

3.Migration

create a new file ​​

​CreateCategory.swift​

​ in Sources/App/Migrations

import Fluent

struct CreateCategory: Migration {
    func prepare(on database: Database) -> EventLoopFuture<Void> {
        database.schema("categories")
            .id()
            .field("name", .string, .required)
            .create()
    }
    
    func revert(on database: Database) -> EventLoopFuture<Void> {
        database.schema("categories").delete()
    }
}      

open ​

​configure.swift​

​​ and add CreateCategory to the migration list, after

​​

​app.migrations.add(CreateAcronym())​

app.migrations.add(CreateCategory())      

4.Category controller

create a new file called ​​

​CategoriesController.swift​

​ In Sources/App/Controllers

import Fluent
import Vapor

struct CategoriesController: RouteCollection {
    func boot(routes: RoutesBuilder) throws {
        let categoriesRoute = routes.grouped("api", "categories")
        categoriesRoute.post(use: createHandler)
        categoriesRoute.get(use: getAllHandler)
        categoriesRoute.get(":categoryID", use: getHandler)
        categoriesRoute.get(":categoryID", "acronyms", use: getAcronymsHandler)
    }
    
    func createHandler(_ req: Request) throws -> EventLoopFuture<Category> {
        let category = try req.content.decode(Category.self)
        return category.save(on: req.db).map { category }
    }
    
    func getAllHandler(_ req: Request) throws -> EventLoopFuture<[Category]> {
        Category.query(on: req.db).all()
    }
    
    //
    func getHandler(_ req: Request) throws -> EventLoopFuture<Category> {
        Category.find(req.parameters.get("categoryID"), on: req.db)
            .unwrap(or: Abort(.notFound))
    }
    
    // http://127.0.0.1:8080/api/categories/<categoryID>/acronyms
    func getAcronymsHandler(_ req: Request) throws -> EventLoopFuture<[Acronym]> {
        Category.find(req.parameters.get("categoryID"), on: req.db)
            .unwrap(or: Abort(.notFound))
            .flatMap { category in
                category.$acronyms.get(on: req.db)
            }
    }
}
      

open ​

​routes.swift​

​​ and register the controller by adding the following to the

end of ​​

​routes(_:)​

app.migrations.add(CreateCategory())      

通過 Rested 送出 category 資料

url:​

​http://127.0.0.1:8080/api/categories​

​​ method:​

​POST​

parameter:​

​{"name": "Teenager"}​

【Vapor】09 Chapter 10:Sibling Relationships

5.Creating a pivot

In Chapter 9, “Parent Child Relationships”, you added a ​​

​reference​

​​ to the ​

​user​

​​ in the ​

​acronym​

​​ to create the ​

​relationship​

​ between an acronym and a user

However, you ​

​can’t​

​ model a sibling relationship like this as it would be too inefficient to query

You need a separate model to hold on to this relationship. In Fluent, this is a ​

​pivot​

create file called ​

​AcronymCategoryPivot.swift​

​ in Sources/App/Models

import Vapor
import Fluent

final class AcronymCategoryPivot: Model {
    static let schema = "acronym-category-pivot"
    
    @ID
    var id: UUID?
    
    @Parent(key: "acronymID")
    var acronym: Acronym
    
    @Parent(key: "categoryID")
    var category: Category
    
    init() {}
    
    init(id: UUID? = nil, acronym: Acronym, category: Category) throws {
        self.id = id
        self.$acronym.id = try acronym.requireID()
        self.$category.id = try category.requireID()
    }
}
      

Create ​

​CreateAcronymCategoryPivot.swift​

​ in Sources/App/Migrations

import Fluent

struct CreateAcronymCategoryPivot: Migration {
    func prepare(on database: Database) -> EventLoopFuture<Void> {
        database.schema("acronym-category-pivot")
            .id()
            .field("acronymID", .uuid, .required, .references("acronyms", "id", onDelete: .cascade))
            .field("categoryID", .uuid, .required, .references("categories", "id", onDelete: .cascade))
            .create()
    }
    
    func revert(on database: Database) -> EventLoopFuture<Void> {
        database.schema("acronym-category-pivot").delete()
    }
}
      

As in Chapter 9, “Parent Child Relationships,” it’s good practice to use ​

​foreign key​

​ constraints with sibling relationships

The migration also sets a ​

​cascade​

​​ schema reference action when you ​

​delete​

​​ the model.

This causes the database to remove the relationship ​​

​automatically​

​ instead of throwing an error

open ​

​configure.swift​

​​ and add CreateAcronymCategoryPivot to the

​​

​migration list, after app.migrations.add(CreateCategory())​

app.migrations.add(CreateAcronymCategoryPivot())      

0x01 建立關聯關系(多對多)

0x02 我的作品