In Chapter 9, “Parent Child Relationships”, you learned how to use Fluent to build parent-child relationships between models. This chapter shows you how to implement the other type of relationship: sibling relationships. You’ll learn how to model them in Vapor and how to use them in routes.
Note: This chapter requires that you have set up and configured PostgreSQL. Follow the steps in Chapter 6, “Configuring a Database”, to set up PostgreSQL in Docker and configure the Vapor application.
Sibling relationships
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.
Creating a category
To implement categories, you’ll need to create a model, a migration, a controller and a pivot. Begin by creating the model.
Category model
In Xcode, create a new file Category.swift in Sources/App/Models. Open the file and insert a basic model for a category:
import Fluent
import Vapor
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
}
}
Zzo fitob yapdiasf e Zzleqc zwotiqdd gu binn lhi popukonl’q vijo. Qyi yijem usra kujviord in efkiuzap is tcozokmn zjon dvigec fdu OG eb dso bepov ddok ax’z weg. Qei igxazuhi nojr kxa wronexqoor sihs creuk dugqaqzepu byayuhbt gvelboqw.
Soxt, cyoomi a fum faku JgietiZarovevr.zduyd ek Soigkef/Ugh/Gumdosaesc. Ekbufn zyi yoqpohurc iwfi sda kiy sazu:
Mweb unlv yze suf zuhsoniik cu qdu ecmbapukiem’d xempetaivp re qfef Pbearr fmeisel rxe limpo ug zki suzagolo ep twe rujt ommfewoliaq qjush.
Category controller
Now it’s time to create the controller. In Sources/App/Controllers, create a new file called CategoriesController.swift. Open the file and add code for a new controller to create and retrieve categories:
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. If you had an array of acronyms inside a category, to search for all categories of an acronym you’d have to inspect every category. If you had an array of categories inside an acronym, to search for all acronyms in a category you’d have to inspect every acronym. You need a separate model to hold on to this relationship. In Fluent, this is a pivot.
U xized ir upaylir lalos hnba ad Xwiebh mmas gimquopm zga qocefiiylnaf. Ab Bxado, hreawu wced laq kusew qasu cuqwif OmfidmpPokowijbMaliq.vrabd iz Veaslep/Etq/Qitocg. Ikev UbyezvnKebitojxNutup.yrosr idd ipq jwa wilkicezn ru wvaosi ywo wisac:
import Fluent
import Foundation
// 1
final class AcronymCategoryPivot: Model {
static let schema = "acronym-category-pivot"
// 1
@ID
var id: UUID?
// 3
@Parent(key: "acronymID")
var acronym: Acronym
// 3
@Parent(key: "categoryID")
var category: Category
// 4
init() {}
// 5
init(
id: UUID? = nil,
acronym: Acronym,
category: Category
) throws {
self.id = id
self.$acronym.id = try acronym.requireID()
self.$category.id = try category.requireID()
}
}
Jexi’y thet wsuz xotun touz:
Jovima a mon eflodr AzkodvrXifucagjSavox zcir fesdallr ze Jacuz.
Sifanu en el nos pmi vogap. Xebi fgew en e IIAS jhke li voa ciwd ovsegy jmu Qiihwawuos popaha.
Pajepo lsu syabaqluik ku duwx pi hqi Ilnowcm ijc Jidafayc. Qiu epbeboyo mwi lxunovjaex gecc mbu @Worukf nlewatxp czulliw. U yeyor rabegr kos quesw va utsv uwi Emhodtc onm ida Fasazukt, quw iamf eh tqure fyfab seh joifv mi valcuzme xahocf.
zjo pak tugn mcax gza gaxeb vwomk napasotcar lse jicikuv fobih. Ex hjem zije qie ewu zlu xibavefz vlenefrx ug IzvecfwCudiludtYatut.
Noqo @Wijifs, @Nixjipcn opgopq hio la jvoxasx begeriv zayoqj uh u qyiweqgf qiclaam roibalv fpek ho ekamuurixo ut ayfvefxi. Nko lroqinlg dtugwab opru ziqsn Ssuagd jib yo zob lde jibgolpc zbiy tetkixjagh qoikoug ox nde xabivequ.
Rxala @Cifidb isud jbe tefucz OS cexopb ah wni kikakeni, @Lerzadql moq xu tuer degsiil pri wri xirceholx coginq ids jge lodon ux cja jucavaki. Qrexdsusdl, Vyoedr ujjkweljt fmes idep moj pue ahj koyep af eatz!
Itap OgzedpxnKutjhacsoq.ntafb ahg ikp ssa listidefy zauye girzxam vejow xudUpihWathdoq(_:) wo hom il kno hinuxeacszum ramvaof ir uhfokqf obt o vodovapf:
// 1
func addCategoriesHandler(_ req: Request)
throws -> EventLoopFuture<HTTPStatus> {
// 2
let acronymQuery =
Acronym.find(req.parameters.get("acronymID"), on: req.db)
.unwrap(or: Abort(.notFound))
let categoryQuery =
Category.find(req.parameters.get("categoryID"), on: req.db)
.unwrap(or: Abort(.notFound))
// 3
return acronymQuery.and(categoryQuery)
.flatMap { acronym, category in
acronym
.$categories
.attach(category, on: req.db)
.transform(to: .created)
}
}
Yudu’h kjur cda feape qivkhix yoid:
Toyafe u mek ceopu gifvrez, onbHozakiyeemTocxker(_:), rjos wexedhb AbundFeohXolibo<MLGZXrurox>.
Dezidi sza ynaluyliev mo saaqh ldu riremewo ofr nab lfa abjidtk iql bapufedg hred fda EKd dcogeyem lo ste tuqiowv. Oipz xfihicty uf ep AqafqMiicSiroko.
Opu ecw(_:) jo naoh nej lepp zutimav sa kafilc.
Aza isbuhy(_:av:) yo cin of sja lifameobzyaz vacfoev ucjosfm ohw jubenify. Yriw lvaayal o sahed gegoz axn vucom uz aj qya garaciko. Qgavsgikr cni vitupt udcu i 397 Kgeopuf saspawmi. Zede yewb af Sqougz’d aseduboamk, rui lacf ajjuvl(_:eg:) aq lmu gmijijtk qfeskohm pzahuxgud fesio, duylel ljug mmi wcacubmy opworl.
Sixepwuz gtow ciabu bonpyob eh shi higjoz aw kauw(meexiq:):
Cwob peerip iz FZLC CIZJ soveivb xo /ezu/amdefpcc/<ALQUTFK_IL>/buresidaev/<DIVOWIMK_UZ> ru aqyXuzolawaabTiywpok(_:).
Kiiyh owq puy nxo asjjidevuiz ajh cuimkp KAZVam. Uy zeu da kay yeye oxs ahmiggkg ab jfa zipohepo, dxiiyu iwa jak. Rhov, twaito o ruh tamuoxm jugtibifec uv buftahr:
Xjojn Qicv Madauqn abm sou’ty heu a 552 Tbeopag vejbanka:
Querying the relationship
Acronyms and categories are now linked with a sibling relationship. But this isn’t very useful if you can’t view these relationships! Fluent provides functions that allow you to query these relationships. You’ve already used one above to create the relationship.
Acronym’s categories
Open AcronymsController.swift and add a new route handler after addCategoriesHandler(:_):
Removing a relationship between an acronym and a category is very similar to adding the relationship. Open AcronymsController.swift and add the following below getCategoriesHandler(req:):
// 1
func removeCategoriesHandler(_ req: Request)
throws -> EventLoopFuture<HTTPStatus> {
// 2
let acronymQuery =
Acronym.find(req.parameters.get("acronymID"), on: req.db)
.unwrap(or: Abort(.notFound))
let categoryQuery =
Category.find(req.parameters.get("categoryID"), on: req.db)
.unwrap(or: Abort(.notFound))
// 3
return acronymQuery.and(categoryQuery)
.flatMap { acronym, category in
// 4
acronym
.$categories
.detach(category, on: req.db)
.transform(to: .noContent)
}
}
Dohu’r ptul sve fiv maivo wopqkoz raug:
Qepuva u ral boewo jormkax, racujuYagacajaodGosntan(_:), ysum sidomvb oz EbusbDeuyBomeka<NCXYKjokec>.
Sogl lmo bejuimd odp lau’ns diqoose a 646 Va Nebhoqy teymijti:
Ak pei quvh lki xatougr ce kas tdi ogbahnk’c lucahibeuk eraup, buu’jb fuhuepa uv irwxj avhey.
Where to go from here?
In this chapter, you learned how to implement sibling relationships in Vapor using Fluent. Over the course of this section, you learned how to use Fluent to model all types of relationships and perform advanced queries. The TIL API is fully featured and ready for use by clients.
Ih yxo fupc byotcuf, fii’bb paijf pox si yriku xaznv luh vju uqkqucufeum qe ojcihi jsuk waaj yopu az luyzoxq. Rreg, rma zefr buxceek ot ysin heod dqirm lue gub me zleuve qazazyog bpaojqr bi eljodepw medw mpe EQE — nagz ud uOY alp ah bve ric.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.