WWW-ohjelmointi ANTTI SAND TIETA12
2 MVC Model, View, Controller
Model, View, Controller 3 Yleisesti käytetty malli ohjelman rakenteelle Kullakin osalla on melko hyvin rajattu vastuunsa Ohjaa suunnittelua kohti SRP:tä Myös useita variaatioita, MV*, MVMC Ei täydellinen malli WWW-ohjelmointiin (kuka vastaa esim. Headereista??), mutta varmasti yleisin
Model, View, Controller 4 Model Controller Selain View
Controller - työnjohtaja 5 Käskyttää tietomallia Vaatii olioinstanssin tai instanssikokoelman, käskee päivittämään tietonsa, käskee poistamaan itsensä Käskyttää näkymää Vaatii halutun näkymän ja tarjoilee sille halutut tiedot Vastaa logiikasta korkealla tasolla, ei ota kantaa käytännön toteutukseen On agnostinen sen suhteen, miten data tallennetaan tai esitetään Pitäisi nopealla vilkaisulla kuvata ohjelman toiminta Skinny controllers, fat models Tosin fat models voi johtaa jumalobjekteihin (yleensä käyttäjätietomalli saa liikaa vastuita, koska useimmat toiminnot liittyvät käyttäjään)
Model - tietomalli 6 Esimerkin Task luokka Kontrolleri käskee tietomalliluokan tehdä jotain, tietomalliluokka vastaa tavasta, jolla se tehdään Ei tiedä, mitä ohjelmalla tehdään Ei tiedä, miten data esitetään käyttäjälle
View 7 Näkymä vastaa vain tiedon esittämisestä näytöllä Saa kontrollerilta tietoa tietomalliluokasta ja esittää sen haluamallaan tavalla Ei ohjaa ohjelman suoritusta, mutta tarjoaa käyttöliittymän toiminnoille Ei ole täysin aivoton, voi esimerkiksi näyttää tietoja ehdollisesti Näytetään uloskirjautumislinkki, jos käyttäjä on kirjautuneena sisään Kutsutaan apufunktiota, joka palauttaa halutun tekstin asetetulla kielellä
Vastuualueet 8 Task luokka ei palauta HTML merkattua tehtävälistaa (ul>li>task->desc) Tämä on näkymän vastuualue. Task luokan kannalta on merkityksetöntä, tuleeko tiedot selainikkunaan vai komentoriville vai Excel taulukkoon Kontrolleri ei suorita SQL komentoja Tämä on tietomallin vastuualue. Kontrollerin kannalta on merkityksetöntä, onko taustalla SQL tietokanta, vai nosql kanta, vai rajapinta, vai mitään Näkymä ei tee suorituksen kannalta merkittäviä päätöksiä Nämä ovat kontrollerin vastuulla. Kontrolleria vilkaisemalla pitää saada oikea kuva sovelluksen toimintoketjusta Valitettavasti, nämä ovat ideaaleja, joita rikotaan.
CLEAN arkkitehtuuri 9 Enterprise business rules Task luokka sisältää tehtävälistan yhden tehtävän kuvauksen Application business rules Tehtävälistan tehtäviä voidaan lisätä ja poistaa Interface adapters Tietomalliluokka pystyy tallentamaan tietonsa johonkin tietovarantoon Framework PDO luokka tarjoaa mahdollisuuden tallentaa tietoa SQL kantaan, kun reitityskomponentti vastaanottaa tietyn pyynnön
Model - tietomalliluokka 10
Interface -kerros 11 Enterprise luokan tiedot tulisi voida varastoida tietokantaan Luokan ei tule kuitenkaan itse ottaa kantaa siihen, miten tiedot varastoidaan (tai varastoidaanko niitä) Application business rules kuitenkin vaatii, että meidän on saatava lista olioinstansseista Voidaan lisätä tiedon varastointikerros joko erillisen Entity manager rajapinnan kautta, tai periyttämällä varastointitoteutus Entity manager rajapinta tarjoaa paremman segrekaation (kontrolleri pyytää rajapinnan toteuttavalta luokalta kokoelman olioinstansseja) Toinen vaihtoehto on Active record malli, jossa Enterprise luokka saa varastointikyvyn yläluokalta (kontrolleri käskee tietomalliluokkaa palauttamaan instanssikokoelman)
Tavoitetila 12 Task::create($data) luodaan uusi olioinstanssi samalla tallentaen sen tiedot tietovarantoon Task::all() palauttaa tietovarannosta olioinstanssit Task::find($id) palauttaa tietovarannosta olioinstanssin Task::delete($id) poistaa tietovarannosta olioinstanssin $task->save() tallentaa tämän instanssin tiedot $task->update($data) päivittää tämän instanssin tiedot $task->destroy() poistaa tämän instanssin tiedot API säilyy luettavana ja on nopeasti ymmärrettävissä Active record implementaatiossa Entity manager -implementaatiossa ei työskennellä luokan kanssa, vaan esimerkiksi $manager->create( task, $data), $manager->all( task )
Tietomalliluokan toiminnan esimerkki 13 Halutaan, että tietomalliluokka hallitsee datan käsittelyn tietokannassa Esimerkiksi Task luokka sisältäisi mahdollisuuden Task::all() ja Task::find($id) Meillä ei ole luokan instanssia, koska luokan metodin tulisi palauttaa luokan instanssi tai kokoelma luokan instansseja Kirjoitetaan staattiset metodit Tietomalliluokkia tulisi kuitenkin olla yksi jokaiselle tietomallille Task, User, BlogPost, Comment, Miten jaetaan perustoiminnallisuus kaikille kirjoittamatta sitä uudelleen jokaiseen luokkaan?
Abstrakti luokka ja 14 periminen Abstrakti luokka on abstrakti Siitä ei voida luoda luokan instanssia Se voi kuitenkin sisältää toiminnallisuutta, jota voidaan perimisen kautta jakaa aliluokille Voidaan siis toteuttaa all ja find metodit staattisina abstraktiin luokkaan ja käyttää niitä sitten kaikissa abstraktin luokan perivissä luokissa
Tietomalliluokka perii geneerisen Model -luokan 15 Task luokka perii Model luokan ja saa käyttöönsä all() ja find() metodit Voidaan kirjoittaa Task::all() ja Task::find($id)
Muuttujien asettaminen 16 Model vastaa tietokannan kanssa keskustelusta. Se ei kuitenkaan tiedä esimerkiksi mistä tietokantataulusta kunkin luokan tiedot löytyvät Kukin tietomalliluokka voi haluta määritellä kohdalleen ainakin tietokantataulun nimen ja pääavaimen nimen Jos pääavain ei olekaan aina id, vaan vaikkapa OP_NRO, halutaan antaa mahdollisuus sen vaihtamiseen Voitaisiin kirjoittaa muuttujat jokaiseen tietomalliluokkaan, mutta mitä jos niitä ei aina muista kirjoittaa? Pääavaimelle voidaan antaa luonteva default arvo (id), mutta tietokantataulun nimelle ei ole mitään järkevää default arvoa.
$this, self ja static 17 $this viittaa olion instanssiin Jos meillä on Task luokan instanssi, sen sisällä voidaan viitata itseen termillä $this Self viittaa itse luokkaan Jos Model luokassa sanotaan self, se viittaa itse Model luokkaan. Eli Model luokan staattisia metodeja voidaan kutsua self::staticmethod() Static viittaa itse luokkaan Jos Model luokassa sanotaan static se viittaa kutsuvaan luokkaan. Eli Task luokka voi käyttää Model luokan perittyjä muuttujia static::$staticvar Static voidaan siis sitoa ajoaikana, kun taas self viittaa aina kyseiseen luokkaan http://www.programmerinterview.com/index.php/php-questions/php-self-vsstatic/
Esimerkki self ja 18 static Koska viittauksena on self, se kutsuu aina yläluokan getmodel metodia Jos viittauksen muuttaa muotoon static, se sidotaan vasta ajoaikana ja se kutsuisi aliluokan getmodel - metodia TIETA12-2017 - A.S.
Lisätään pääavain muuttujaksi Model - luokkaan 19 TIETA12-2017 - A.S.
Pääavain on nyt muutettavissa 20 Jos Task luokka ei määritä pääavainta $primarykey muuttujaan, käytetään Model luokan $primarykey muuttujaa Eli pääavain on id, jos muuta ei määritetä Mutta se voidaan tietomalliluokkakohtaisesti muuttaa toiseksi, jos tarvitaan Tämä kelpaa, koska muuttujalle oli löydettävissä järkevä default arvo (id) Mutta tietokantataulun nimelle ei ole järkevää default arvoa
Convention over configuration 21 Sovitaan, että jos tietokantataululle ei anneta nimeä, käytetään käytäntöä, jossa tietokantataulun nimi päätellään tietomalliluokan nimestä Halutaan kuitenkin jättää mahdollisuus poiketa tästä käytännöstä antamalla eksplisiittisesti tietokantataulun nimi tietomalliluokassa Luokan nimi Task User Comment Taulun nimi tasks users comments
Taulun nimen määrittäminen 22 Jotta tämä toimisi, meidän tulee tietää kutsuvan tietomalliluokan nimi ajoaikana Jos tietomalliluokka määrittää tietokantataulun nimen, käytetään sitä Jos ei määritä, määritellään tietokantataulun nimi kutsuvan luokan nimestä Kutsuvan luokan nimeä ei tiedetä ennen kuin ohjelmaa suoritetaan
Taulun nimen määrittäminen 23
Get_called_class() 24 Get_called_class() funktio palauttaa kutsuneen luokan nimen Lcfirst() muuta ensimmäinen kirjain pieneksi kirjaimeksi Get_called_class() late static binding Sidotaan myöhäisessä vaiheessa, joten palauttaa sen luokan, joka kutsun teki Tässä tapauksessa siis Task -> task Voitaisiin kirjoittaa myös CLASS - avainsanalla http://php.net/manual/en/language.oo p5.late-static-bindings.php
Ylikirjoitetaan 25
Taulun nimi 26 Taulun nimi on nyt ylikirjoitettavissa jos halutaan, mutta sitä ei ole pakko antaa, jos noudatetaan sovittua käytäntöä
Lisätään luokka tietokantahakuihin 27
Model luokan all() ja find() 28 Tietomalliluokat voivat vastata omien tietojensa hakemisesta Ja haetut tiedot palautetaan kutsuvan luokan instansseina Luokan metodit käytettävissä palautetuissa tuloksissa $asclass
Implode() ja explode() 29 Taulukosta merkkijonoksi ja merkkijonosta taulukoksi $names = [ John, Jeff ]; Echo implode(,, $names); // John, Jeff $names = John, Jeff ; Var_dump(explode(,, $names)); //[0] => John, [1] => Jeff
30 Määritetään valittavat kentät haussa SELECT * palauttaa kaikki kentät Lisätään mahdollisuus määrittää haettavat kentät Lisätään optionaalinen parametri $fields Model luokan hakumetodiin Jos $fields annettiin, käytetään sitä haussa, Muuten käytetään SELECT * TIETA12-2017 - A.S. $selectfields on tarpeeton muuttuja, joita tulisi välttää. Tässä vain helpottamassa koodin ymmärrettävyyttä.
Uuden rivin lisääminen tietokantaan 31 Tietomalliluokan olisi hyvä voida myös luoda uusi rivi tietokantaan Halutaan käyttää samaa käytäntöä: Task::create( ) Model luokkaan uusi staattinen metodi, joka ottaa parametrina assosiatiivisen taulukon
Luontimetodi 32 Tässä kohtaa monia mahdollisia poikkeuksia, joihin ei nyt varauduta Todellisuudessa ongelma on kompleksinen ja kannattaisi hyödyntää kypsempää ohjelmointikehystä Mutta esimerkin vuoksi pieni, toimiva toteutus
33 Poistometodi Rivin poistaminen tietokannasta voidaan toteuttaa staattisella metodilla Model luokassa Metodi saa parametrinaan tietueen tunnisteen Task::delete(5); TIETA12-2017 - A.S.
34 Instanssin poistaminen Voidaan haluta poistaa myös tietty instanssi Meillä on Task luokan instanssi, joka halutaan poistaa Muuten vastaava, mutta ei staattinen metodi Ja tunniste saadaan instanssin muuttujasta $this->id TIETA12-2017 - A.S.
Instanssin poistaminen 35 Jos meillä on jo luokan instanssi, voidaan käskeä instanssia poistamaan itsensä tietokannasta Huom! Itse luokan instanssi on edelleen käytössä, vaikka sitä ei enää ole tietokannassa Voitaisiin jatkaa edelleen luomalla instanssin tallennusmetodi $task->save() TIETA12-2017 - A.S.
Päivitysmetodi 36 Voi olla staattinen, mutta kuten poistometodissakin voisi olla järkevämpää toteuttaa instanssin metodina, koska liittyy käsiteltävään instanssiin Tällöin kuitenkin pitää mapata olion muuttujat tietokantataulun sarakkeisiin Koska tietomalliluokassa on (voi olla) toiminnallisuutta, johon voi liittyä omia muuttujia, joita ei ole tietokantataulussa, voitaisiin sopia, että olion julkiset muuttujat ovat ne, jotka halutaan mapata tietokantaan Jotta voitaisiin ajoaikana selvittää, mitä julkisia muuttujia luokalla on, meidän tulisi käyttää reflektiota (new ReflectionObject($this)) ->getproperties(reflectionproperty::is_public);
Päivitysmetodi 37 Tämä menisi kuitenkin paljon syvemmälle, kuin tässä kohtaa on järkevää, joten todettakoon, että ratkaisuja on useita Voidaan määrittää luokan muuttujaan mitä kenttiä tulisi tallentaa tietokantaan Tämä vaatisi kuitenkin aina sen tiedon lisäämisen jokaiseen tietomalliluokkaan Tai voidaan sopia, että julkiset muuttujat tallennetaan ja käyttää reflektiota niiden selvittämiseen Tai voidaan sopia, että päivitysmetodi on vain staattinen Tiedon mappaaminen tietokantaan on yleinen ongelma ja valmiita, hyviä, toteutuksia on saatavilla. On hyvä tietää, miten jokin ongelma ratkaistaisiin, mutta aina ei kannata toteuttaa kaikkea uudestaan Koska ongelmakenttä on yllättävän kompleksinen
Staattinen päivitysmetodi 38
Tietomalliluokka vs. Repository pattern 39 Riippuen koulukunnasta tiedon tallentaminen (persistance) voidaan asettaa tietomalliluokan taakse, tai toteuttaen puhtaampaa repository patternia (Entity manager) Jokaiselle tietomallille voidaan luoda esimerkiksi UsersRepository, joka toteuttaa RepositoryInterface -rajapintaa, joka puolestaan tarjoaa metodit luomiselle, noutamiselle, tallentamiselle, Näin tietomalliluokka ei vastaisi keskustelusta tietokannan kanssa, vaan määrittäisi vain tietomallin metodit ja muuttujat, joita business logiikka tarvitsee Voidaan argumentoida, että tietomalliluokka rikkoo SRP:tä, jos se tarjoaa luokkaan liittyviä metodeja $user->getfullname() ja tiedon tallennuksen $user->save() Toinen ajattelutapa on kerroksittainen rakenne. Model luokka tarjoaa persistance layerin, siitä peritty User luokka tarjoaa business logic kerroksen. User -luokka on silloin Domain Model ja Model luokka on Domain Service
CLEAN -arkkitehtuuri 40 Tavoite molemmissa on eriyttää riippuvuudet sovelluksen kerroksiin Entities kerros sisältää pelkkiä objekteja, jotka eivät riipu mistään ulkopuolisesta. Tämä on business logiikka se toiminnallisuus, jolla ongelma ratkaistaan Business logiikka ei riipu käytetystä ohjelmistokehyksestä, tietokannasta, ympäristöstä, Ulommat kerrokset tuovat riippuvuuksia, mutta niiden pitää olla vaihdettavissa Esimerkiksi UI ei liity ohjelman toimintaan. Sama ohjelma voi tuottaa tietoa verkkosivulle tai REST rajapinnan tai Excel tiedoston, https://8thlight.com/blog/unclebob/2012/08/13/the-clean-architecture.html
Yksinkertainen reititys ja kontrollerit 41
Perinteinen sivusto 42 Perinteisesti WWW-sivut ovat koostuneet toisiinsa linkatuista html sivuista Index, About me, Contact me Index.html, about.html, contact.html Modernimmissa ohjelmissa meillä on yleensä vain yksi päätiedosto front controller, joka koostaa palautettavan näkymän Aiemmin jaoimme sivun kolmeen osaan (header, main content ja footer) FC tulkitsee pyynnön (index.php?page=about), päivittää main content alueen sisällön ja palauttaa koostetun sivun
Yksinkertainen reititys 43 Tarkistetaan jokin parametri ja ohjataan sen mukaan oikealle sivulle Index.php?page=about TIETA12-2017 - A.S.
.htaccess 44 Yleensä ei kuitenkaan käytetä muotoa www.example.com/index.php?page=about Tavallisempi muoto olisi www.example.com/about WWW-palvelin voidaan määrätä uudelleenohjaamaan pyynnöt.htaccess tiedostolla Selaimen kannalta osoite näyttäisi olevan /about/, mutta taustalla se ohjataan vaikka /index.php/about tai /index.php?page=about Avaimena siis about tai avain-arvo parina page=about Käytännön toteutus vapaavalintainen, molemmissa annetaan parametrina tietoa front controllerille TIETA12-2017 - A.S.
Apache vs. NginX 45 Koulun koneilla Apache, joten pyyntöjen uudelleenohjaaminen.htaccess tiedostolla NginX käyttää Virtual server määrittelyissään try files määrettä Homestead käyttää NginX www-palvelinohjelmaa, joten oletuksena se tarjoaa PHP tiedostoille palvelinpään reititystä
Käytäntöjä 46 Useimmiten framework olettaa rakenteeksi Controller Method Parameters Eli esimerkiksi index.php?controller=pages&method=index&.htaccess tiedosto voi ohjata /pages/listitems/desc=true Toisaalta.htaccess voi mapata jokaisen reitin RewriteRule ^pages/listitems$ index.php?controller=pages&method=listitems Yleensä tämä vastuu halutaan kuitenkin ohjelmalle, joten.htaccess vain kätkee front controllerin Rakenne ja toteutustapa on kuitenkin kehittäjän päätettävissä
Halutun reitin tulkitseminen 47 Otetaan esimerkiksi /index.php/about Ensimmäinen parametri on siis halutun sivun nimi Sen perässä voisi olla muutakin tietoa (/index.php/tasks/7, /index.php/tasks/showcompleted=true), mutta pidetään reititin tässä kohtaa yksinkertaisena ja ohjataan vain ensimmäisen parametrin mukaan
Controllers 48 Luodaan kaksi kontrolleria omaan hakemistoonsa Yksi etusivun näyttämiselle ja toinen kaikkien taskien hakemiselle
Reitittimen 49 refaktorointi Lisätään yksinkerainen tiedosto reittien määrittämiselle Assosiatiivinen taulukko, jossa kutsuttu polku vastaa haluttua kontrolleria
Core/Router 50 Router luokka lataa reitit omaan taulukkoonsa Kun reitittimelle annetaan polku, se palauttaa polkuun liitetyn ladattavan tiedoston nimen Instanssi mahdollistaa ketjutuksen
Reitittäminen 51 Bootstrap.php lataa reitit tiedostosta, alustaa reitittimen, antaa reitittimelle pyydetyn polun ja lataa tiedoston, jonka reititin palauttaa
Kontrolleri 52 Nyt jokainen looginen osio voidaan jakaa oman kontrollerinsa taakse ja koodi on paremmin rakenteistettu Kukin kontrolleri on kuitenkin vain pätkä koodia ilman rakennetta Oikea kontrolleri koostuisi metodeista, jotka liittyvät tiettyyn resurssiin Tätä varten pitäisi selvittää haluttu metodi halutun kontrollerin sisällä
Resurssikontrolleri 53 Yksi resurssikontrolleri toteuttaisi ainakin nämä 7 metodia Kutsut voisivat tulla selaimelta GET ja POST pyynnöillä Ja sovelluksilta GET, POST, PUT/PATCH, DELETE
Hyödyllistä luettavaa 54 Mikä on Active Recordin ja Data Mapperin ero? http://culttt.com/2014/06/18/whats-difference-active-record-data-mapper/
Kysymyksiä? 55