Õpetus: kuidas luua suhteid üks-mitmele

Selle õpetuse lõpus teate, kuidas rakendada suhteid üks-mitmele ja üks-ühele work!

Selle õpetuse tulemuse leiate githubist siit

Indeks

1. Looge ja genereerige uus projekt
2. üks-mitmele / üks-ühele / palju-mitmele
3. Hoone mudel: Pokemon (kasutajaga seoses)
4. Muudetav mudel: kasutaja (määratleb seose Pokemonsiga)
5. Hoonekontroller: PokemonController
6. Regulaatori reguleerimine: UserController (kustutage seotud pokemonid)
7. Vaate kohandamine: loetlege kõik kasutaja pokemoonid
8. Vaate kohandamine: rakendage vorm uute pokemoonide loomiseks
9. Kuhu siit edasi minna

1. Looge ja genereerige uus projekt

Uue projekti loomisel kasutame mallina eelnimetatud õpetuse tulemusi:

aur new projectName --template = vaporberlin / minu esimene-kõmu kasutav leht

Enne Xcode'i projekti genereerimist peaksime paketi nime muutma Package.swift:

// swift-tools-version: 4.0
impordi pakettkirjeldus
lase pack = pakett (
  nimi: "projectName",
  sõltuvused: [
    .pakend (URL: "https://github.com/vapor/vapor.git", alates: "3.0.0"),
    .pakend (URL: "https://github.com/vapor/leaf.git", alates: "3.0.0-rc"),
    .pakk (URL: "https://github.com/vapor/fluent-sqlite.git", pärit: "3.0.0-rc")
  ],
  eesmärgid: [
    .target (nimi: "App", sõltuvused: ["Vapor", "Leaf", "FluentSQLite"]),
    .target (nimi: "Run", sõltuvused: ["App"]),
    .testTarget (nimi: "AppTests", sõltuvused: ["App"]),
  ]
)

Nüüd terminalis juurkataloogi projektiNimi / käivitage:

aurude värskendus - ei

Sõltuvuse hankimine võib natuke aega võtta, kuid kui see tehtud, peaks teil olema selline projekti ülesehitus:

projektiNimi /
├── Pakett.swift
├── Allikad /
│ ├── Rakendus /
│ │ ├── Kontrollerid /
│ │. └── UserController.swift
│ │ ├── mudelid /
│ │ │ └── Kasutaja.swift
├── │ ├── rakendus.swift
├── │ ├── boot.swift
├── │ ├── konfigureerimine.swift
└── │ └── marsruudid
│ └── Käivita /
│ └── main.swift
├── Testid /
├── Ressursid /
│ └── Vaated /
│ └── crud.leaf
├── avalik /
├── Sõltuvused /
└── Tooted /

2. üks-mitmele / üks-ühele / palju-mitmele

Esimene samm enne oma suhete mudelite loomist peame välja mõtlema, milliseid suhteid me vajame. Kas vajame suhteid paljude vahel või üks-mitme vastu. Miks nii? Oh, mul on hea meel, et te küsisite! Sest kui meil on suhteid paljudest paljudesse, on vaja kolme andmebaasi tabelit, samas kui ühele mitmele on meil vaja ainult kahte. Kuidas see on? Kõik, ma näen, et sa oled terav. Selle põhjuseks on asjaolu, et näiteks suhetes inimestega on meil kasutaja (poké-treener), kellel võib olla mitu pokémoni. Kuid hurmamehe moodi pokemon ei saa kuuluda rohkem kui ühele kasutajale (poké-treener). Iga pokemon saab kasutaja_ID, nii et seda ID saab kasutada kasutaja tabeli kaudu õige kasutaja toomiseks.

üks-mitmele suhe
MÄRKUS. Kui soovite nüüd teada, kui palju pokemone kasutaja omab, saate pokemon-tabelist otsida kasutaja ID-d.

Lühidalt: kui teil on üks-ühele seos, on see sama andmebaasi struktuur. Kaks lauda. Teil on vaja ainult enne teie minekut kontrollida, kas kasutaja omab Pokemoni areadiat, ja luua talle uus. Ja kui ta juba omab seda, siis ei saa te lihtsalt oma koodiga uut loomist lubada. See lõpeb üks-ühele

üks-ühele suhe

Nii et täielikkuse väljanägemine näeks välja, kui paljudest paljudele:

paljudevaheline suhe

Siin oleks teil kasutajate tabel, klasside tabel ja seoste tabel. Nagu näete, võib kasutaja olla paljudes klassides nagu ka mai. Ta on klassis kuu ja puit. Seega on suhete tabelis tema jaoks kaks kirjet. Üks viitab klassile tunnusega 1 ja teine ​​kirje viitab klassile tunnusega 2. Samuti võite näha, et klassil võib olla palju kasutajaid. Võite näha, et relatsioonitabelil on meil kaks kasutajat (user_id: 1, 2), viidates klassile_id: 1 (moon).

3. Hoone mudel: Pokemon (kasutajaga seoses)

Looge Models-is uus kiire fail ja pange sellele nimi Pokemon.swift

MÄRKUS. Kasutasin terminali: puudutage valikuid Allikad / Rakendus / Mudelid / Pokemon.swift

Võimalik, et peate Xcode'i projekti uuesti genereerima aururikoodiga -y, et Xcode saaks teie uut kataloogi näha.

Models / Pokemon.swift sisaldab järgmist koodi:

importige FluentSQLite
import aur
lõpuklass Pokemon: SQLiteModel {
  var id: Int?
  var nimi: Keel
  var tase: Keel
  selles(
    id: Int? = null,
    nimi: keel,
    tase: Keel
  ) {
    self.id = id
    ise.nimi = nimi
    self.level = tase
  }
}
laiendus Pokemon: ränne {}

Nüüd on see lihtne mudel, millel pole mingit seost kasutajaga. Lisage seos:

importige FluentSQLite
import aur
lõpuklass Pokemon: SQLiteModel {
  var id: Int?
  var nimi: Keel
  var tase: Int
  var userID: User.ID
selles(
    id: Int? = null,
    nimi: keel,
    tase: keskmine,
    userID: User.ID
  ) {
    self.id = id
    ise.nimi = nimi
    self.level = tase
    self.userID = kasutajatunnus
  }
}
laiend Kasutaja: sisu {}
laiendi kasutaja: migratsioon {}
laiend Kasutaja: parameeter {}
pikendus Pokemon {
  var kasutaja: vanem  {
    naasta vanem (\. kasutajatunnus)
  }
}

Kas te tõesti usute, et see on nii lihtne? Me vajame ainult atribuudi lisamist, et salvestada kasutajatunnus ja veel üks atribuut koos seosega.

Oluline osa on suhe, mis on määratletud vanema struktuuriga, kus meie Pokemoni klass on laps ja meie kasutajaklass on lapsevanem. Nüüd atribuudile (siin nimetatakse kasutajaks) juurdepääsul naaseb vanem, kelle võib leida koos kasutajatunnuse võtmeteega. Fluent teab nüüd kõike, mida ta suhte jaoks peab teadma. Ta teab mõlemaid tabeleid: Pokemon ja User. Ja et Pokemonist saame vanema, mille väärtus on vara userID taga.

See tähendab, et kui meil on mõni Pokemoni eksemplar, suudame selle kasutaja vanemaks saades alati selle vanemaks saada.

Nüüd lisage meie Pokemon-mudel rännetele konfigureerimisel.swift:

import aur
importleht
importige FluentSQLite
avaliku funktsiooni seadistamine (
  _ config: inout Config,
  _ env: inout Environment,
  _ teenused: inout teenused
) viskab {
  lase ruuteril = EngineRouter.default ()
  proovida marsruute (ruuter)
  teenused.register (ruuter, näiteks: ruuteri ise)
  lase leafProvider = LeafProvider ()
  proovige teenuseid.register (leafProvider)
  proovige teenuseid.register (FluentSQLiteProvider ())
  config.prefer (LeafRenderer.self, kasutaja jaoks: ViewRenderer.self)
  var andmebaasid = DatabasesConfig ()
  proovige andmebaasid.add (
    andmebaas: SQLiteDatabase (salvestusruum: .mälu),
    kui: .sqlite
  )
  teenused.register (andmebaasid)
  var migrations = MigrationConfig ()
  migrations.add (mudel: User.self, andmebaas: .sqlite)
  migrations.add (mudel: Pokemon.self, andmebaas: .sqlite)
  teenused.register (ränded)
}

Kui nüüd vajutate cmd + r või käivitate, peaks kõik olema ehitatud ilma tõrgeteta .

Märkus. Enne rakenduse käivitamist valige kindlasti nupu kõrval skeem

4. Muudetav mudel: kasutaja (määratleb seose Pokemonsiga)

Nüüd teise osa juurde: määratlege suhe kasutaja ja tema Pokemoni vahel. Läheme meie mudeleid / kasutajat.swifti juurde ja lisage sellele seos Laps, et saaksime ka kõik kasutaja pokemonid:

importige FluentSQLite
import aur
lõpuklassi kasutaja: SQLiteModel {
  var id: Int?
  var kasutajanimi: String
init (id: Int? = null, kasutajanimi: String) {
    self.id = id
    self.username = kasutajanimi
  }
}
laiend Kasutaja: sisu {}
laiendi kasutaja: migratsioon {}
laiend Kasutaja: parameeter {}
laienduse kasutaja {
  var pokemons: Lapsed  {
    tagasta lapsed (\. kasutajatunnus)
  }
}

5. Hoonekontroller: PokemonController

Meie eesmärk on suuta luua kasutajale pokemon. Nii et looge meie kontrollerites / kataloogis uus fail ja pange sellele nimi PokemonController.swift.

MÄRKUS. Kasutasin terminali, täitke lihtsalt:
puudutage allikaid / rakendus / kontrollerid / PokemonController.swift

Võimalik, et peate Xcode'i projekti uuesti genereerima vapor xcode -y abil, et Xcode saaks teie uut faili näha.

Kirjutame loomisfunktsiooni kontrolleritesse / PokemonController.swift:

import aur
lõpuklass PokemonController {
  func loo (_req: Request) visked -> Future  {
    tagasi proovige req.content
      .decode (Pokemon.PokemonForm. Self)
      .flatMap {pokemonForm sisse
        tagasta kasutaja
          .find (pokemonForm.userId, edasi: req)
          .flatMap {kasutaja
            valvur lase userId = proovida kasutajat? .requireID () else {
              viska katkestama (.badRequest)
            }
            lase pokemon = Pokemon (
              nimi: pokemonForm.name,
              tase: pokemonForm.level,
              userID: userId
            )
            return pokemon.save (sisse: req) .map {_ sisse
              tagastama req.redirect (aadressile "/ kasutajad")
            }
        }
      }
  }
}

Peate olema märganud, et kasutan Pokemon.PokemonForm. Self-i selle jaoks, milleks ma sisu lahti mõtesin. Lisasin struktuuri, mis vastab täpselt selle omadustele selle vormi väljadele, mida me mõne minuti pärast uue pokemoni loomiseks kasutame. See on kujunemas kogukonna parimaks tavaks. Pokemon.swiftis lisage:

importige FluentSQLite
import aur
lõpuklass Pokemon: SQLiteModel {
  var id: Int?
  var nimi: Keel
  var tase: Int
  var userID: User.ID
  selles(
    id: Int? = null,
    nimi: keel,
    tase: keskmine,
    userID: User.ID
  ) {
    self.id = id
    ise.nimi = nimi
    self.level = tase
    self.userID = kasutajatunnus
  }
  struct PokemonForm: sisu {
    var nimi: Keel
    var tase: Int
    var userId: Int
  }
}
laiendus Pokemon: ränne {}
pikendus Pokemon {
  var kasutaja: vanem  {
    naasta vanem (\. kasutajatunnus)
  }
}

Lisame nüüd uue marsruudi meie värske loomise funktsiooni sisse marsruudil marsruudid.swift:

import aur
avalikud funktsionaalsed marsruudid (_ ruuter: ruuter) viskab {
  lase userController = UserController ()
  router.get ("kasutajad", kasutage: userController.list)
  router.post ("kasutajad", kasutage: userController.create)
  ruuter.post ("kasutajad", "User.parameter", "värskendus", kasutage: userController.update)
  ruuter.post ("kasutajad", kasutaja.parameeter, "kustuta", kasutage: userController.delete)
  lase pokemonController = PokemonController ()
  ruuter.post ("pokemon", kasutage: pokemonController.create)
}

6. Regulaatori reguleerimine: UserController (kustutage seotud pokemonid)

Nüüd, kui meil on võimalik kasutaja jaoks uusi pokemone luua, peame ka neid kasutaja kustutamisel arvesse võtma. Reguleerige järgmist rida meie kontrolleris / UserController.swift:

import aur
lõpuklassi kasutajakontroller {
  func list (_ req: Request) visked -> Future  {
    ...
  }
  func loo (_req: Request) visked -> Future  {
    ...
  }
  func update (_ req: Request) viskab -> Future  {
    ...
  }
  func delete (_ req: Request) viskab -> Future  {
    tagasi proovida req.parameters.next (Kasutaja ise) .flatMap {kasutaja
      naase proovida user.pokemons.query (sisse: req) .delete (). flatMap {_ in
        return user.delete (sees: req) .map {_ sisse
          tagastama req.redirect (aadressile "/ kasutajad")
        }
      }
    }
  }
}
struct UserForm: sisu {
  var kasutajanimi: String
}

7. Vaate kohandamine: loetlege kõik kasutaja Pokemonid

korrigeerime kõigepealt oma vaadet, et printida ka kõik kasutaja pokemonid jaotisesse Ressursid / Vaated / crud.leaf:

MÄRKUS. Valige crud.leaf ja minge jaotisse Editor> Syntax Coloring> HTML


  
     CRUD 
    
  
  
    

CRUD

    
      
        

Looge

        ...       
      
        

Loe

        #for (kasutaja kasutajaloendis) {           
            
              
                                 #for (pokemon in user.pokemons) {                   
  • # (pokemon.name)                 }               
  •             
              
            }       
          
            

    värskendamine

            ...       
          
            

    Kustuta

            ...       
        
      

    Nüüd näeb see välja ja tundub, nagu see töötaks, kuid see pole harjumus. Ja põhjus on selles, et kasutajal on päring ainult selle atribuudis talletatud seotud pokemoonide kohta, kuid mitte pokemoni esinemisjuhtude endi kohta. Praegu saate:

    {"tõrge": tõene, "põhjus": "iteraatori andmeid massiivi teisendada ei õnnestunud. (/Path/To/yourProject/Resources\/Views\/crud.lehe rida: 28 veerg: 33 vahemik: 1211 .. <1268 ) "}

    Nüüd tahame tuua seotud pokemonid, enne kui mõlemad, nii kasutaja kui ka pokemon vaadesse lähevad. Parimal juhul mähkige need isegi oma konstruktsiooni:

    Kui te seda näete, on see mõistlik, nii et lisage kaustas Controllers / UserController.swift:

    import aur
    lõpuklassi kasutajakontroller {
      func list (_ req: Request) visked -> Future  {
        lase allUsers = User.query (sisse: req) .all ()
        tagasta allUsers.flatMap {kasutajad
          lase userViewList = proovida users.map {kasutaja sisse
            tagastama UserView (
              kasutaja: kasutaja,
              pokemons: proovige user.pokemons.query (sisse: req) .all ()
            )
          }
          lase data = ["userViewlist": userViewList]
          tagasi proovida req.view (). render ("crud", andmed)
        }
      }
      func loo (_req: Request) visked -> Future  {
        ...
      }
      func update (_ req: Request) viskab -> Future  {
        ...
      }
      func delete (_ req: Request) viskab -> Future  {
        ...
      }
    }
    struct UserForm: sisu {
      var kasutajanimi: String
    }
    struct UserView: kodeeritav {
      var kasutaja: kasutaja
      var pokemons: tulevik <[Pokemon]>
    }

    Lõime struktuuri, mis vastab kodeeritavale. See konstruktsioon hoiaks kasutajat ja massiivi tema pokemone. See on tuleviku massiiv, kuid see ei häiri meid üldse. Te ei pane seda isegi tähele, näete . Olgu ja meie funktsioonis on kõik, mida me tegelikult teeme, iga kasutaja kaardistamine UserView eksemplariks. Kaardi sees edastame kasutaja ja teostatud pokemoni päringu tulemuse uude UserView eksemplari.

    Olgu, kohandame nüüd meie curd.leaf-faili uue andmestruktuuri haldamiseks:

    
    
      
         CRUD 
        
      
      
        

    CRUD

        
          
            

    Looge

            ...       
          
            

    Loe

            #for (userView in userViewlist) {           
                
                  
                                     #for (pokemon kasutajaView.pokemons-is) {                   
  • # (pokemon.name)                 }               
  •             
              
            }       
          
            

    värskendamine

            #for (userView in userViewlist) {           
                
                                                                              
              
            }       
          
            

    Kustuta

            #for (userView in userViewlist) {                        
                                                                              
                       }       
        
      

    Me tagame juurdepääsu kasutajale ainult meie UserView ümbrises ja ka sellele, et pääseksime oma UserView ümbrises olevate pokemonidega. Kui nüüd jooksete ja proovite uut kasutajat luua, töötab see täpselt nii, nagu oodata. Kuid me ei näe veel ühtegi pokemoni. Lisage vorm, et luua pokemon!

    8. Vaate kohandamine: rakendage vorm uute pokemoonide loomiseks

    Järgmises etapis loome vormi, kus meil on esiteks: rippmenüü kasutaja valimiseks, kelle jaoks pokemon luuakse, ja teiseks: väljad (nimi ja tase), mida me uue pokemoni jaoks vajame:

    
    
      
         CRUD 
        
      
      
        

    CRUD

        
          
            

    Looge

            ...       
          
            

    Loe

            ...       
          
            

    värskendamine

            ...       
          
            

    Kustuta

            ...       
        

    Pokemon

        
          
                     
                         
                

    kasutaja

                               #for (userView in userViewlist) {                                }                        
              
                

    nimi

                           
              
                

    tase

                           
              
            
          
        
      
    

    Selgitan, mida me siin tegime - mõned asjad on tõesti olulised! Ma eeldan, et olete juba tuttav sellest, kuidas vormimärgid töötavad saidist Kuidas kirjutada CRUDi kasutades Leafi, kuid meil on siin midagi uut. Oleme oma vormimärgendile andnud id ja kasutasime märgendit, kus viitame vormile, kasutades seda id. See on ülitähtis, vastasel juhul ei saadeta meie rippmenüüst väärtusi! See oli minu jaoks uus - kuigi see töötab vaid seni, kuni see on vormis. Täpselt nii, kuidas sisestussildid töötavad. Oh kui naiivne ... Selgus, et sul on oma valitav silt ükskõik kus! Kuni viitate oma vormile vormiga = “vormi vorm”, saadetakse väärtused kohe, kui vorm on esitatud!

    MÄRKUS. Vormi saatmiseks vormist rippmenüüst (vali silt) peate andma vormile ID ja viima oma valitud sildilt sellele vormile selle vormi abil!

    Meie viimane cmd + r või käivitage ja värskendage oma saiti ja see on! Olete edukalt rakendanud suhteid üks-mitmele !!

    9. Kuhu siit edasi minna

    Kõigi Githubi näidisprojektide õpetuste loendi leiate siit:
    https://github.com/vaporberlin/vaporschool

    Mul on tõesti hea meel, et lugesite minu artiklit! Kui teil on mingeid ettepanekuid või parandusi, andke mulle sellest teada! Mulle meeldiks teid kuulda!

    Twitter / Github / Instagram

    Vaata ka

    Kuidas muuta oma sisuturundus tõhusaks digitaalstrateegiaks4 ootamatut stressijat, mis võivad teid aeglaselt tappa (ja kuidas neid parandada)Kuidas suurendada nii teie veebisaidi juurdepääsetavust + SEOKuidas olla kasside toidublogijaNegatiivse mõtteviisi parandamine