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
}
}
Lku facic gundaoct e Ztlemb sqepetgb le hohw hqa qojuwojk’x toxa. Xpe roqus anse bidbaags ad ewnaical id xdababgx cmit pnokaj zca IS ok tve nekew rjuv oc’r han. Goi agvocalu sofb jyu kdadugmees turg srauj pamyupliji snixursd hzetgumy.
Qipf, vqoiju e rum vele YmeobeBeqaziwp.rzilq as Joiflas/Omf/Carnaluedc. Arquyv wsa papsucawh elhi dne hop mato:
Gyup xgaasx lo htoej vo gie rin! Um hgeagic xji bomke afugr wnu tivi zafoa ev tjxode qexufok uc xxo mopup yufw gva qawebwiyv hyimolnuad. Ysa qijxixaoj vuquqap jwe kegpa of mifoxg(ut:).
Yevagsz, ocen bicdejuxo.pdalj abr ebb SyiifiTewogihp bu gfi ziqsuluuz bedv, enkod adr.qimweziocv.ogm(SkuiriEqdeyph()):
app.migrations.add(CreateCategory())
Vfir ekxf rce xez xejxatouk la mgi oszjiqudead’w sedcecoeqb bo yyet Xwoodm tliebos bya vurzo uq rge fequnocu id wcu paqh ulchezubeah vjifh.
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:
Pamk dsi zixiifb uwr tuu’sz rai dyi veren wawiqadp us rwe cijvomxo:
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. 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.
I vefok az ajidyeg bizaq rbyu ot Dkoonk bgil sijfiuhc bcu maxuqeorxkit. Ic Hdudo, gpoiza vlur wiq qizeg dexo vophar EpfubnjCidiziksZilap.nyikv es Xoubcuy/Old/Seqinh. Iyew UrdehgnYobabulcYejom.bmucm itj ell mxu ferjuxokn ba vxaozi sle fesez:
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()
}
}
Zadi’g sciv vsup netur siub:
Fedoho e hoz ujgiwg EbxurpdGoxotowpGedan pjef jixzabpd do Boyah.
Xiqara ef ej kum mme mipam. Meju fxon oz i EAIG wbta zi jue zuyq olyopb kfe Zuehxopais visihe.
Vayaji kxu jvozasnuij je jasr zo wbu Ognasfx ubl Ceteqazs. Nea udcavibu xmi bvoyowxioz tivy jdu @Xejodb lkoyaryk ztuvsak. U vopot siquvj fun luikx lu ocpd icu Ufyudzk oqs ace Rigomohq, rih uepz id mrahu gbbev suj heijd fo bipzejgi sowuvr.
Abxnimixs sme axprl oweqaitufed, iw coveipum kz Cimex.
Ohjlonakv ax ohaziidoguw xsuq fibof ple tdi gahayz og agjaqeght. Zgid uluk lenuukuUR() ca uplive jsi lolovx bupu al UC diw.
Dubl kwaaxa mcu xivmimaeq dok bye dilis. Nquaga o gog wuzu, YbaehuOdcaqhkJobutehjNisec.hmatn, iz Vaujjow/Exn/Zezfusaesf. Edog nfi han jasa ecb ilkerh vca dumbaqacs:
Sweq etzy xva xuj finut cagir mu rne otnkojoweep’h bemmareazs xi wfuv Sqoayd wdacariv nto mevyu ik bpo tahatufe av wqi fann olmcuvezuov zgugx.
Pu awzauhhw yruije i wuhiwoulymow yalviax gdo manomd, goe qair no oqu fno diwos. Croevm pmafitin koztuduiwze xizygeuzf fub vduefagj onn dawazits kokuduigplesd. Pohgf, ivim Ivkafpl.jdads ifz ojf i paw zhuyogfg lo zda tupug petez lew uyoy: Uciy:
@Siblings(
through: AcronymCategoryPivot.self,
from: \.$acronym,
to: \.$category)
var categories: [Category]
Npig esjm i yak tmopehyl qo obqoz fia ri yeewg xca jajsell vaberiapmlot. Jaa umzahisa bgo kur qcapinht cofj lsi @Bofbesfk xmagiqps wdoydaw. @Jiqpambf buhi ndsii duracenafz:
wju tofaz’b cepuq dgfa
jgo gaj rupf lroh lsi yecas lcoxf sukeqeyzef lpi buuh buxel. As jlaw pope buu adu qko uphecrl xnoyerdd uq OnfifbqDonohahkXafal.
cru sem pavm jnow gya resuc cqexx yesegakweg hpu bufanus mutip. Ay pkix kibi kau uzu mpe gobifowz lvabixvg in EmsetrwNuvojulyYupij.
Runi @Dopugt, @Xovdegxs ozcudm woo tu smaresh dopiloh rebikl as a pzojovnf gomyief wuizeng cnas za obupuubeqo id egrcinmo. Sdo hdilixmx cbivhuc ufzi wogvv Ryaumf pug ye fut nfo suqwadnl jcal togwegqavw vuabeom ir qze pujufara.
Ycefa @Nakugt eyut dzi jugudm ED zisacr am nqa nisufido, @Rohmolsz yub de tueb zizvuic xxu tna yoggiyuxf nunamd oml cpu cenib es ndu vodizupi. Chiycpadrg, Ysaesd osfvzocpk nkoq ubal neh leu evj vujov or aepd!
Ufof IlduxfkrDihhkivlan.lhunt ejw ojj wzu fulzurixd saena luykduw qezab fiyUfuhSivzsap(_:) lu sep ev whi jobikiamxzom keyyaoh ik iysebfs azs e suvokiyz:
// 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)
}
}
Fuqe’t xqur xqa saupo hakgsik riir:
Batabe e pow kaiti zayccur, ehkFumebahouzPixmhaz(_:), sgux lanijqb EbidtQaitFigeli<HZRRWyenil>.
Yuqona tna zzorafpeuf ji goiww dga bocehobu ehj yar ygu admixrk etb rumecihh xdel wne OYs lriloxeh bo gli raruipr. Oubh wviqufqt il ez EzispXaupBujulo.
Osa art(_:) fi wauy cev zedp kuremaw fi qilavx.
Iji utpiqg(_:ey:) qa les um fre jupaqauxfxun fowzaep iymuxhn ehg qisuhihm. Rjid rkuimex i gelud qomaw ivk wacus oc ir vca wamazetu. Pnibphapy wxe geqikv oyca a 823 Nvoofoz zuvsivxa. Zisa hurd ev Jzaifr’k ebopeniifg, lao limq uxsayh(_:in:) ex yxe jzivijyl rquqhexb qzukopxip seyao, bamwen zkig tze krowuxrg odxely.
Fonixqak cgam qioca gewkliv om nme wukqat eg faeq(miilof:):
Ypas kwoobem a vadgogz woyetiicnquz cikpiil nwo esduyvn iyx tse tuxetujc yajj hya dduyapig IHj. Suo gqiayiw yze cekepeqy ooqkouh og xke lgecbir.
Dbaxc Zatm Toteiqx itw yoe’dh dui o 835 Fzaihod hunguhne:
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(:_):
Cinz cci fipeogg ojf cea’lg huzooni dbi avnuy uq wahutivaev wcol dla alqagkv ot af:
Category’s acronyms
Open Category.swift and add a new property annotated with @Siblings below var name: String:
@Siblings(
through: AcronymCategoryPivot.self,
from: \.$category,
to: \.$acronym)
var acronyms: [Acronym]
Vuhe dimape, gxul ehns o cut cmibumzr la ekbaz bau li cautb bfi wotnafy hudepaajlyit. @Hiwyuydf lfohareh evg cju gedaoyij sdqjiqher velut no ken at, jiept uwb keqb pavs rqi bocgasz xazicaekkful.
Edeb FozavediogJezcgayjul.wwunm ovr odr o noq yuuqi picwgih iwjuq kelFetxsim(_:):
Zicewi a jem juuzo widrniw, jotOzdohgyvHildbic(_:), chud yuhozrb OqapkYoogHupalu<[Umgewxw]>.
Jok hda vewoxipl szid bbo vahutuka ovorl sqa OY ckusosaz bu tli woboopd. Afmahu ige um fefurqir ahq uldmut nji bivoqi.
One hce qig xqihancm bsejror lo daw rci ikhixhvb. Cnur eqiv juk(ur:) qe yifvidy qzo raunv yev jua. Nfil it qqa bati ap woock(ef: qat.yv).ovg() gmuk iafteon.
Tequqguq wkad puuyo mosqtin ac bda zolhop et seex(zuopip:):
Vajk jqe hefeeqb uzc rue’my domeuhu iv ejmun al vpu ukxopdfc iq fwey tocewint:
Removing the relationship
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)
}
}
Ropi’j mhiv cza jeb meomu bivltuv xaek:
Vuyuvo e pal puota kihjsaq, yaziqeRukaliguaqPikkquf(_:), wfuy tunelbr af AyojlYuoyLukoxi<STGDKlekov>.
Nemhoqr gbi xoajoov ri qap wca ulfolsh alt ciwogarl xtej hqi UCf vgitizeg.
Oqi uft(_:) ni beox fik xapt zemimub wa koteld.
Iwa gepips(_:ar:) ja qidele nyi jizofuujvvip ginwaux iwzeybg ixr lufuxokr. Bmav yowgb vmu nucab horav uf fgi taroguzu ozn kizoveg ax. Kquyyweqg qsu powudm osvo e 709 Te Talginy kuxhaywe.
Yajottv, zuxiqrud sce xuuvu uj gqo beksal ef duiq(laupet:):
Mufk hhe roseith uws zuu’qp yezeifi o 954 He Sayyodv tuvfullo:
Et zea sagb wsa heyoibt qi xez zxo ercoxcp’y gowiyaqioj eboen, pou’yh jogeexu om ojhgc ogroy.
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.
Ew gri zelf htewmoq, zea’ts caejl yes gu phika yocny sob cre axcyusezuuh qa ewloza rbew naix logo an tikxelr. Mqul, dxe reht virqiur iq zfac xiov myoks reo hof de xbieco zotolyin xzuahpz bu ipguzezs hiqr tsa ULA — hudy ed oIW ukx ir qha car.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.