Choď na navigáciu

4. Tvoríme CMS s CakePHP - Model

V minulej časti seriálu sme si vysvetlili srdce frameworku Active Record. Od dnešnej časti sa pustíme do programovania nášho CMS a začneme návrhom databáze a vytvorením databázovej vrstvy našej aplikácie tzv. Modelu.

2. Konvencie CakePHP

Aby sme vedeli správne navrhnúť databázu, a CakePHP na jej základe správne chápal väzby medzi tabuľkami, je potrebné poznať konvencie pre návrh databáze. Čo myslíme tým, aby CakePHP správne chápal väzby medzi tabuľkami? Znamená to, že ak budeme mať databázu správne navrhnutú, framework bude automaticky rozpoznávať cudzie kľúče, primárne kľúče a väzby medzi tabuľkami, a nebudeme ich musieť pre každú tabuľka explicitne zadávať.

Ešte je dobré poznamenať, že je veľmi rozumné zadávať názvy tabuliek, modelov atď. v anglickom jazyku. CakePHP automaticky chápe anglické jednotné a množné čísla, takže sa vyhneme zbytočným nastavovaniam (samozrejme je možné zadávať názvy aj v národných jazykoch, ale je to práca naviac a my preto budeme používať anglické konvencie).

Názvy tabuliek
Názvy tabuliek sa zapisujú pomenovaním v množnom čísle jednotiek, ktoré uchovávajú. Napríklad máme tabuľku, ktorá uchováva články, preto musí byť táto tabuľka pomenovaná v množnom čísle, v našom prípade articles.

Primárny kľúč
Primárny kľúč sa v tabuľkách označuje pomenovaním id. Taktiež platí, že môžeme používať aj iné pomenovanie pre primárny kľúč, ale opäť to musíme nastaviť.

Cudzie kľúče
Cudzie kľúče sa sa v tabuľkách označujú podľa tabuľky, kde sa pôvodne nachádzajú. Predstavme si, že každý článok má svojho autora. Čiže budeme mať dve tabuľky a to articles a users. V tabuľke articles sa bude potom pochopiteľne nachádzať cudzí kľúč z tabuľky users a to pomenovaním user_id. Čiže nám je hneď zrejmé, že cudzí kľúč sa tvorí podľa konvencie jednotne_cislo_ta­bulky_cudzieho_klu­ca_id.

Názvy atribútov
Ešte spomeniem, že je celkom vhodné, aby sa atribút názov pomenovával ako name. Oceníme to najmä pri generovaní select boxov vo väzbách 1:N, kde si budeme chcieť napríklad pri článku zvoliť kategóriu výberom z niekoľkých možností.

Spojovacia tabuľka pri väzbe N:M
Tabuľky ktoré sú vo väzbe N:M abecedne zoradíme a následne novú tabuľku pomenuje podľa konvencie nazov_prvej_ta­bulky_nazov_dru­hej_tabulky. Predstavme si, že náš článok má N nálepiek a jedna nálepka môže byť v M článkoch. Máme teda tabuľky pomenované articles a tags a následne vytvoríme spojovaciu tabuľku, ktorú pomenujeme articles_tags. Cudzie kľúče do spojovacej tabuľky vytvoríme podľa konvencií, ktoré sme si definovali vyššie.

2. Navrhujeme databázu

Na obrázku môžeme vidieť našu databázu pre náš nový redakčný systém, ktorú sme navrhli na základe vyššie definovaných konvencií

3. Modely tabuliek

Databázu už máme správne navrhnutú a môžeme sa pustiť do tej najzaujímavejšej časti, a to je návrh databázovej vrstvy našej aplikácie tzv. Modelu, čo je jedna z troch častí návrhového vzoru Model View Controller. Ako sme si už spomínali v predchádzajúcom článku, modely sa umiestňujú do adresára app/models. V našom prípade vytvoríme 4 modely, i keď máme tabuliek spolu 5. Ako sme si už spomínali, model sa pre spojovaciu tabuľku nevytvára a kedže máme databázu správne navrhnutú, všetku potrebnú prácu v súvislosti so spojovacou tabuľkou vykoná Cake automaticky. Predtým, než začneme vytvárať samotné modely treba poznamenať, že model sa pomenováva ako jednotné číslo názvu tabuľky, čiže napr. pre našu tabuľku articles sa bude model nazývať article a bude umiestnený v súbore app/models/ar­ticle.php

Model Article umiestený v súbore /app/models/ar­ticle.php

<?php
class Article extends AppModel {

        var $name = 'Article';
        var $useTable = 'articles';
        var $validate = array(
                'title' => VALID_NOT_EMPTY,
                'text' => VALID_NOT_EMPTY,
                'user_id' => array('numeric'),
                'created' => array('date')
        );

        var $belongsTo = array('User');
        var $hasAndBelongsToMany = array('Tag');
        var $hasMany = array('Comment');
}
?>

Model User umiestnený v súbore /app/models/u­ser.php

<?php
class User extends AppModel {

        var $name = 'User';
        var $useTable = 'users';
        var $validate = array(
                'username' => VALID_NOT_EMPTY,
                'password' => VALID_NOT_EMPTY
        );

        var $hasMany = array('Article');

}
?>

Model Comment umiestnený v súbore /app/models/com­ment.php

<?php
class Comment extends AppModel {

        var $name = 'Comment';
        var $useTable = 'comments';
        var $validate = array(
                'title' => VALID_NOT_EMPTY,
                'text' => VALID_NOT_EMPTY,
                'username' => VALID_NOT_EMPTY,
                'article_id' => array('numeric')
        );

        var $belongsTo = array('Article');

}
?>

Model Tag umiestnený v súbore /app/models/tag­.php

<?php
class Tag extends AppModel {

        var $name = 'Tag';
        var $useTable = 'tags';
        var $validate = array(
                'name' => VALID_NOT_EMPTY,
                'seo_link' => VALID_NOT_EMPTY
        );

        var $hasAndBelongsToMany = array('Article');

}
?>

Ako môžeme vidieť vyššie, v kóde sme už uviedli aj validačné kritéria, ktoré sme si priblížili už v predchádzajúcej časti seriálu. Využívať ich však budeme až v časti nasledujúcej, v ktorej sa budeme venovať vytváraniu zvyšných dvoch častí architektúry MVC a to Controller a View.

Aby sme si mohli otestovať, či sme našu databázu skutočne navrhli správne, môžeme využiť tzv. scaffolding. Cake nám pomocou scaffolding dokáže na základe vytvorených modelov automaticky skonštruovať ukážkovú aplikáciu, pomocou ktorej môžeme otestovať, či nám väzby v našej tabuľke fungujú správne. Potrebujeme však ku každému modelu vytvoriť Controller, v ktorom určíme, že Cake má používať na vytvorenie aplikácie scaffolding (ako som už spomínal vyššie, controlleru sa budeme venovať v nasledujúcej časti, preto nám teraz stačí bez hlbšieho vysvetlenie skopírovať jednotlivé kódy konkrétnych súborov s controllermi).

Controller Articles umiestnený v súbore /app/controller­s/articles_con­troller.php

<?php
class ArticlesController extends AppController {

        var $name = 'Articles';
        var $scaffold;
}
?>

Controller Users umiestnený v súbore /app/controller­s/users_contro­ller.php

<?php
class UsersController extends AppController {

        var $name = 'Users';
        var $scaffold;
}
?>

Controller Comments umiestnený v súbore /app/controller­s/comments_con­troller.php

<?php
class CommentsController extends AppController {

        var $name = 'Comments';
        var $scaffold;
}
?>

Controller Tags umiestnený v súbore /app/controller­s/tags_contro­ller.php

<?php
class TagsController extends AppController {

        var $name = 'Tags';
        var $scaffold;
}
?>

Skúste si zadať do url prehliadača napríklad adresy

www.tvoja-adresa/articles, www.tvoja-adresa/comments alebo www.tvoja-adresa/users.

Aké jednoduché však? Scaffolding je mocný nástroj tohto frameworku a dokáže nám značne uľahčiť prácu pri návrhu zložitejších vzťahov v databáze, preto určite stojí zato ho v rannom štádiu vývoja používať.

Rád uvítam vaše námety a pripomienky v diskusii k článku.

Stiahnuť zdrojové kódy k článku

Povedz o článku aj ostatným - www.pridej.cz

Hodnotenie článku: 56%
Počet hodnotení: 53

zlýdobrý

Komentáre k článku

Nový komentár

  1. Titulok: Nazov atributov
    Autor: huco
    Vytvorený: 18. 03. 2008 11:47

    Nie je mi jasne, preco je vhodné, aby sa atribút názov pomenovával ako name. Mohol by si uviest nejaky konkretnejsi priklad s vysvetlenim?

  2. Titulok: RE: Nazov atributov
    Autor: Tibor
    Vytvorený: 18. 03. 2008 17:49

    Odpoved najdes v tomto clanku, ktory som pisal pred nedavnom. Ide v podstate len o to, ze zas by si musel nieco nastavovat navyse a pritom to nie je vobec potrebne, ked to dokaze Cake pochopit automaticky

    http://ims.rockretail.com/…generatelist

  3. Titulok: Vazba v třídě Article
    Autor: Andrea
    Vytvorený: 19. 03. 2008 10:14

    Super seriál. Mám jednu otázečku:

    V třídě Article nemělo by být také:

    var $hasMany = array(‚Comment‘);

    protože je mezi nimi vazba 1:N nebo stačí, když je vazba uvedená pouze v třídě Comment?

  4. Titulok: RE: Vazba v třídě Article
    Autor: Tibor
    Vytvorený: 19. 03. 2008 11:58

    Dakujem za pochvalu :-)

    Ano ta vazba tam ma byt, uz je to opravene. Komentare by sice bolo mozne pridat k clanku, ale bez urcenia tejto vazby v modely Article by nebolo mozne zobrazit tieto komentare k clanku.

    Opravil som taktiez validacne kriteria, ktore neboli spravne.

  5. Titulok: Diagram
    Autor: pointer
    Vytvorený: 19. 03. 2008 14:19

    Ahoj, v akom programe si „namaloval“ ten DB diagram (ER model) ?

  6. Titulok: problem s DELETE
    Autor: Andrea
    Vytvorený: 19. 03. 2008 14:53

    Ahoj, zkouším cakephp podle článku. Vytvořila jsem v MySQL (Verze MySQL: 4.0.26-nt-log) všechny tabulky, přesně podle článku. Ale mám problémy s editací, přesněji řečeno s mazáním položek. Píše to následující hlášku:

    Query: DELETE ArticlesTag FROM articles_tags AS ArticlesTag WHERE ArticlesTag.article_id IN (1)

    Warning (512): SQL Error: 1066: Not unique table/alias: ‚articlestag‘ [CORE\cake\lib­s\model\datasou­rces\dbo_source­.php, line 440]

    5 queries took 3 ms Nr Query Error Affected Num. rows Took (ms) 1 SELECT COUNT(*) AS count FROM articles AS Article WHERE Article.id = 1 1 1 1 2 UPDATE articles AS Article LEFT JOIN users AS User ON (Article.user_id = User.id) SET Article.id = 1, Article.title = ‚clanek 1‘, Article.text = ‚text článku‘, Article.user_id = 1 WHERE Article.id IN (1) 1 1 3 SELECT ArticlesTag.article_id FROM articles_tags AS ArticlesTag WHERE article_id = 1 1 1 0 4 DELETE ArticlesTag FROM articles_tags AS ArticlesTag WHERE ArticlesTag.article_id IN (1) 1066: Not unique table/alias: ‚articlestag‘ 0 5 INSERT INTO articles_tags (article_id,tag_id) VALUES (1,1) 1 1

    Vím, že kdyby příkaz DELETE byl takto: DELETE FROM articles_tags WHERE articles_tags.article_id IN (1)

    tj bez aliasu, tak to projede. Podle internetu nejsem sama, ale řešení jsem nenašla.

  7. Titulok: RE: Diagram
    Autor: Tibor
    Vytvorený: 19. 03. 2008 15:02

    Jedna sa o netbeans http://www.netbeans.org/

    Skusal som ho prvykrat, ale moc sa mi tam to uml nepozdava. Uvidime, ked pride verzia 6.1

  8. Titulok: RE: problem s DELETE
    Autor: Tibor
    Vytvorený: 19. 03. 2008 15:28

    Jedna sa pravdepodobne o tento problem https://trac.cakephp.org/ticket/3977

    Aku verziu cake pouzivas? Skus si bud nahodit najnovsiu verziu cake, co je 1.2.0.6311-beta alebo skus novsiu verziu mysql.

    Tieto problemy sa objavuju vo verzii mysql 4.

    Ja osobne pouzivam cake 1.2.0.6311-beta a mysql 5 a tieto problemy nemam.

  9. Titulok: problem s DELETE
    Autor: Andrea
    Vytvorený: 19. 03. 2008 15:41

    Děkuji. Použila jsem poslední 1.2.0.6311-beta verzi. Ani 1.2…alfa ani 1.1 to nedělala. Bohužel musím zůstat u MySQL4 kvůli hostingu.

  10. Titulok: chaba podpora objektov...
    Autor: ropman
    Vytvorený: 04. 04. 2008 10:42

    neskutocne mi v cakephp vadi ze vsetky metody ktore vracaju zaznamy z databazy typu find(), nie su objekty ale len polia. kym sa toto nezmeni, tak u mna pouzitie cakePHP neprichadza do uvahy… tym padom sa nedaju pouzit ani setters a getters atd… som zastancom „convention over configuration“ a tak som bol na tento framework velmi zvedavy, tymto zasadnym neduhom ma vsak sklamal…

  11. Titulok: RE: chaba podpora objektov...
    Autor: Tibor
    Vytvorený: 09. 04. 2008 15:12

    No to je pravda, ze active record objekty nevracia, ale myslim si, ze z pohladu view je to zas velmi dobra moznost mat k dispozicii viacrozmerne polia…

  12. Titulok: kodovanie
    Autor: st3n
    Vytvorený: 02. 05. 2008 19:19

    zdravim, mam otazocku..ked pisem do suborov .ctp tak mi to dava zle formatovanie, pritom je nastavene utf-8…editujem to v ps-pade kde davam tiez utf-8 v com je problem? dakujem a co sa tyka databazky zobrazuje data spravne ale ked sa pozriem na obsah tabulky cez phpMyAdmin tak tam mam zle kodovanie… vsetko mam na localhoste cez XAMPP

    dik za pomoc

  13. Titulok: sort a filter
    Autor: st3n
    Vytvorený: 02. 05. 2008 20:30

    problem s kodovanim som vyriesil, tie subory boli zle naformatovane… neviem preco ale uz to ide…

    mam ale 2 otazocky: 1 potrebujem spravit filter nasledovne:ked mam zobrazene udaje v databaze-tabulku, ze by tam bol textbox do ktoreho po tom ako by sa zadal text by prehladal celu databazu a vratil len zaznamy kde by nastala zhoda 2 druha vec sortovanie tabulky podla stlpcov ako je to v scaffolding… ako to spravit?

    dakujem velmi pekne za pomoc, potrebujem to na projekt do skoly…

  14. Titulok: filter
    Autor: st3n
    Vytvorený: 02. 05. 2008 22:07

    takze to sortovanie je lahke to uz mam: $paginator->sort(); ale co ten filter/search ? hladal som vselikde na nete a teda nic moc som nenasiel, pritom by to podla mna mal byt jeden zo zakladnych objektov…

  15. Titulok: vyhladavanie
    Autor: Tibor
    Vytvorený: 04. 05. 2008 23:23

    Bohuzial cake nedisponuje vstavanym vyhladavacim enginom, takze uvadzam niekolko moznosti, na ktore som doposial narazil

    Jedna z moznosti je pouzit lucene vyhladavanie

    http://bakery.cakephp.org/…-application

    Druha moznost je vytvorit si indexy na stlpce priamo v databaze v ktorych sa bude vyhladavat a normalne pouzivat metodu find() ktora ma ako prvy parameter conditions co by bol hladany vyraz.

    Urcite pojde pouzit aj fulltext, ktory maju implementovane samotne databazy, avsak to uz zrejme bez custom query nepojde.

    Kedze to mas ako nejaky jednoduchy projekt, odporucal by som ti druhu moznost.

  16. Titulok: filter
    Autor: st3n
    Vytvorený: 11. 05. 2008 23:59

    hladal som na internete ale nejako… neviem, nejaky podrobnejsi postup by som potreboval na ten filter

  17. Titulok: RE:filter
    Autor: Tibor
    Vytvorený: 12. 05. 2008 15:40

    No uplne jednoducho beznym find()

    $searchString = "%".$this -> data["Search"]["text"]."%";
    $this->Product->find('Product.name like "'.$searchString.'"');

    Kde $this → data[„Search“][„tex­t“] pochadza z inputu Search.text, cely kod je umiestneny napr. v controllery Products a v databaze je vytvorena tabulka products so stlpcom nazvanym name

    Samozrejme je este dobre na v tabulke products vytvorit index na stlpec name

  18. Titulok: pekne
    Autor: st3n
    Vytvorený: 12. 05. 2008 18:18

    no to je sice pekne, dakujem, ale nepomohlo mi to…

  19. Titulok: kontakt
    Autor: st3n
    Vytvorený: 12. 05. 2008 20:25

    nejaky kontakt by sa nedal vymenit? je nas viac co robime projekty v cakephp a velmi by nam to pomohlo :) moje icq: 269324339

  20. Titulok: relacie mezdi tabulkami
    Autor: ajpi
    Vytvorený: 22. 05. 2008 02:21

    Dobry, potreboval by som pomoc s vypisom obsahu tabuliek, mam totiz: modely: land.php

    <?php class Land extends AppModel {
    var $name = ‚Land‘;
    var $validate = array(‚id‘ ⇒ array(‚rule‘ ⇒ array(‚minLength‘, 1)),‚shortname‘ ⇒ array(‚rule‘ ⇒ array(‚minLength‘, 1)),‚fullname‘ ⇒ array(‚rule‘ ⇒ array(‚minLength‘, 1)));
    var $hasMany = ‚Player‘; } ?>

    a player.php

    <?php

    class Player extends AppModel {
    var $name = ‚Player‘;
    var $validate = array(‚name‘ ⇒ array(‚rule‘ ⇒ array(‚minLength‘, 1)),‚surname‘ ⇒ array(‚rule‘ ⇒ array(‚minLength‘, 1)),‚land_id‘ ⇒ array(‚rule‘ ⇒ array(‚minLength‘, 1)));
    var $belongsTo = ‚Land‘; }

    ?> Tabulka players obsahuje polia id, name, surname, land_id. Tabulka lands obsahuje polia id, name a fullname. Potreboval by som vypisat obsah tabulky players tak, aby mi vypisalo name, surname a land_id. nemam problem s name a surname, ale land_id potrebujem tak, aby mi nevypisalo id cislo z tabulky land, ale k tomu prisluchajuce meno, napr. v tabulke lands mam: id=1, name=SK, fullname=slovensko do tabulky players som pridal: id=1, name=peter, surname=novak, land_id=1. Vystup by som potreboval: name=peter, surname=novak, land_id=slovensko. Vlastne neviem co mam dat do view aby mi to islo. V controller som pouzil nasledovnu funkciu kvoli sortovaniu:

    function index() {
    $this->set(‚players‘, $this->paginate(‚Pla­yer‘));
    }

    Potreboval by som to rychlo :S Tiez by sa mi hodil nejaky kontakt na Vas. Dakujem velmi pekne.

  21. Titulok: RE:ajpi
    Autor: Tibor
    Vytvorený: 22. 05. 2008 09:22

    Dobry den. V subore view si jednoducho pozrite co ste si do view poslali napr. jednoducho takto

    <? var_dump($players); ?>

    A urcite uvidite, ze v premennej $players mate pole, ku ktoremu mozete pristupovat takto

    foreach ($players as $player):
    
    //data z tabulky players
    echo $player['Player']['name'];
    
    //data z tabulky lands
    echo $player['Land']['fullname'];
    
    endforeach;
  22. Titulok: RE: RE: ajpi
    Autor: Ajpi
    Vytvorený: 22. 05. 2008 12:03

    Dakujem velmi pekne, Vy ste fakt macher :) Uz by som potreboval iba jednu vec, neda sa nejak spravit to, ked pridavam novy player, aby sa mi zobrazilo scroll box s aktualnymi hodnotami v tabulke lands namiesto obycajneho inputboxu pre pole land_id? Ako to je napr. aj v MS Access. Cize by som do pole players → land_id mohol vyberat z danych hodnot, a nie pisat rucne.

    Prepacte ze tolko otravujem ale som uplny zaciatocnik s cake a nezvykavam robit vela ani v php. Dakujem este raz :)

  23. Titulok: scroll box
    Autor: Ajpi
    Vytvorený: 24. 05. 2008 14:32

    Cize mam spojene tie tabulky a funguju aj dobre, cez scaffold mi to robi perfektne, mozem vyberat iba z tych zaznamov co tam zatial mam v tej druhej tabulke, lenze ja by som potreboval kod ako spravit manualne ten scrollbox, a nie cez scaffold :(

    Hladal som na nete, ale nic uzitcne som nenasiel… :(

  24. Titulok: RE:scroll box
    Autor: Tibor
    Vytvorený: 25. 05. 2008 11:12

    Takze tatko. Do suboru app/controller­s/players_con­troller.php vloz tento kod

    function add() {
    
    //toto pridaj ku kodu, ktory sa uz v metode nachadza
    $lands = $this->Player->Land->generateList();
    $this->set(compact('lands'));

    A vo view scroll box vytvor klasicky

    echo $form -> input('land_id');

    Co tak si prestudovat zdrojove kody napr. mojho Fantoma, co je redakcny system na ktorom bezi tento blog alebo zamerat sa na cake console, ktora ti znacne ulahci pracu a vygeneruje ti hotove zdrojove kody na zaklade vazieb v databaze atd.

  25. Titulok: vlastne sql a reakcia cakephp nan
    Autor: jozko
    Vytvorený: 26. 05. 2008 19:19

    ked si robim vlastny sql prikaz a dam tam napriklad SUM(tabulka.hod­nota) AS spolu ako potom mam k tomuto pristupovat vo view ked to chcem vypisat? surne, dakujem za pomoc

  26. Titulok: RE: vlastne sql a reakcia cakephp nan
    Autor: Tibor
    Vytvorený: 27. 05. 2008 10:52

    Vo view si daj metodou var_dump() vypisat obsah premmenej, ktoru si si poslat do view, to je predsa jednoduche

    //kod v metode controlleru
    $exam = $this->Daybook->query('select sum(income-outcome) as sum from daybook_items');
    $this->set(compact('exam'));
    
    //kod vo view
    var_dump($exam);

    A hned vidis, co sa ti do view dostalo. Z vlastneho query by si mal dostat troj rozmerne pole, ku ktoremu pristupujes $exam[0][0][‚sum‘]

    Avsak najjednoduchsie pouzitie sum je v metode findAll()

    $exam = $this -> DaybookItem -> findAll(null,"sum(income)"));

    Taktiez dostanes rovnake troj rozmerne pole, ku ktoremu pristupis vo view takto $exam[0][0][‚s­um(income)‘]

  27. Titulok: Rýchlosť písania seriálu
    Autor: w3q
    Vytvorený: 05. 06. 2008 20:36

    Ahojte, nechcem kritizovať, ale bol by som rád ak by tento seriál vychádzal rýchlejšie. Tibo, neber to ako buzeráciu, proste ten seriál je super, a mám problém čakať na dalšiu časť. :D Je fakt super.

  28. Titulok: RE:Rýchlosť písania seriálu
    Autor: Tibor
    Vytvorený: 06. 06. 2008 00:53

    No v provom rade slo o dohodu s abclinuxu.cz pockat, kym nevyjdu clanky aj u nich.

    Taktiez som teraz dost zaneprazdneny, ale buduci tyzden planujem vydat niekolko clankov a jednym z nich by malo byt aj pokracovanie mojho serialu.

    Takze musis este chvilku vydrzat :-)

  29. Titulok: jak dostat model do vlastni komponenty
    Autor: hAdam
    Vytvorený: 10. 07. 2008 15:07

    Zdravim nevedel by tu nekdo jak dostat model do vlastni komponenty. jedno reseni sem nasel na http://debuggable.com/…4647cbdd56cb

    toto reseni mi bohuzel nepomohlo .. cake nebyl schopen model najit… navic jedna funkce pouzita v tomto reseni konkretne loadModel($mo­delClass) je podle cakephp 1.2 nepripustna