Přejít k navigační liště

Zdroják » PHP » ORM frameworky pro PHP5: Doctrine ORM

ORM frameworky pro PHP5: Doctrine ORM

Články PHP, Různé

Ve druhém díle miniseriálu o ORM knihovnách pro PHP5 se zaměříme na Doctrine ORM framework, který patří mezi nejčastěji používané. Na ukázkách kódu se seznámíme se základy práce s tímto frameworkem a ukážeme si nejdůležitější funkce. Dále si ukážeme propojení s MVC frameworkem Symfony a ukázku výsledku CRUD generátoru.

Doctrine framework nabízí množství funkcí, které jsou velice podrobně popsané v uživatelském manuálu na oficiálních stránkách projektu. V tomto článku jsou vypsané pouze funkce dělající tento framework unikátní oproti ostatním.

Práce s modelem

Práce s řádkem tabulky je na první pohled takřka totožná jako v jiných ORM:

ER model ukázky

$contact = new Contact();
$contact->seName('Jan Novak');
$contact->save();

Doctrine ale umožňuje i alternativní způsob zápisu:

$contact = new Contact();
$contact->name = 'Jan Novak';
$contact->save();

Přestože se $name tváří jako veřejná proměnná třídy Contact (rozšiřuje třídu Doctrine_Record), je při zápisu hodnoty použita magická funkce __set a při čtení __get, a je tedy možné použít validátory, případně zcela přepsat metodu setName, resp. getName. Kromě magických funkcí implementuje třída Contact rozhraní ArrayAccess a je možné použít tento způsob zápisu:

$contact = new Contact();
$contact['name'] = 'Jan Novak';
$contact->save();

Možnost práce s objektem jako s polem je užitečná, protože PHP obsahuje širokou nabídku funkcí právě pro proměnné typu pole. Seznam objektů (třída Doctrine_Collec­tion) můžeme procházet pomocí funkce foreach. Nebo stejné zobrazovací šablony použít jak pro záznamy načtené do pole, tak i pro záznamy tabulky načtené do objektu.

Díky tomu, že Doctrine zná strukturu databáze (viz další díl článku), je možné nastavovat i objekty v relaci:

$contact = new Contact();
$contact->name = 'Jan Novak';
$contact->save();

$phone = new Phone();
$phone->Contact = $contact; //totez jako $phone->contact_id = $contact->id;
$phone->number  = '123 456 789';
$phone->save();

//Totez jako:

$contact = new Contact();
$contact->name  = 'Jan Novak';
$contact->Phones[]->number = '123 456 789';
$contact->save();

DQL (Doctrine Query Language)

Doctrine obsahuje dotazovací jazyk, který je před odesláním databázovému stroji zpracován parserem. DQL je velice podobné standardnímu SQL, ale obsahuje několik rozšíření a na druhou stranu omezuje v použití SQL funkcí, které nejsou standardně dostupné na všech podporovaných databázových systémech. Ukázka DQL dotazu:

//SELECT * FROM Contact WHERE name LIKE ?

//objektově je tento dotaz zapsán

$contacts = Doctrine_Query::create()
    ->select('*')
    ->from('Contact')
    ->where('name LIKE ?', '%novak%')
    ->execute();

V proměnné $contacts bude uložena kolekce objektů třídy Contact. Dotazovací jazyk je největší výhodou Doctrine oproti frameworku Propel, protože umožňuje optimalizovat dotazy způsobem, který je u jiných frameworků nemožný nebo dosažitelný velice obtížně.

$contacts = Doctrine_Query::create()
    ->select("c.name, GROUP_CONCAT(', ', p.number) AS numbers")
    ->from('Contact c')
    ->leftJoin('c.Phones AS p')
    ->groupBy('c.id')
    ->execute();

Pomocí DQL je možné zapsat libovolný dotaz, nicméně velmi složité dotazy (zejména za použití vnořených dotazů) mohou narazit na limity parseru a je potřeba příkazy správně uspořádat, aby byly zpracovány správně. S DQL souvisí i možnosti načítání výsledků dotazu do proměnných více typů:

$numContactss = Doctrine_Query::create()
    ->select('COUNT(id)')
    ->from('Contact')
    ->execute(array(), Doctrine::HYDRATE_SINGLE_SCALAR);

echo $numContacts;
//proměnná obsahuje hodnotu skalárního typu, v tomto případě integer

$contacts = Doctrine_Query::create()
    ->select('*')
    ->from('Contact')
    ->where('name LIKE ?')
    //parametry dotazu je možné zadat až při spuštění dotazu
    ->execute(array('%novak%'), Doctrine::HYDRATE_ARRAY);

print_r($contacts);
//proměnná obsahuje asociativní pole

Kromě uvedených typů načtených výsledků je dostupných několik dalších možností včetně tzv. lazy loading, kdy jsou objekty ze záznamu vytvořeny až v okamžiku přístupu. V kombinaci s optimalizací dotazů pomocí jazyka DQL a načtením výsledku dotazu do proměnných typu pole se výkon Doctrine blíží standardnímu PHP bez ORM frameworku. Zároveň je ale zajištěna ochrana proti útoku přes SQL injection. Díky objektovému zápisu DQL dotazů můžeme dotaz rozdělit do metod a vyhnout se opakování:

class Contact extends BaseContact
{

    /**
     * Vraci true, pokud kontakt ma vlozene telefony
     *
     * @return boolean
     */
    public function hasPhone()
    {
        return $this->getPhoneQuery()->count() > 0;
    }

    /**
     * Seznam telefonnich cisel tohoto kontaktu
     *
     * @return array
     */
    public function getPhoneNumbers()
    {
        return $this->getPhoneQuery()
            ->select('number')
            ->execute(array(), Doctrine::HYDRATE_ARRAY);
    }

    /**
     * Instance dotazu na telefonni cisla tohoto kontaktu
     *
     * @return Doctrine_Query
     */
    protected function getPhoneQuery()
    {
        return Doctrine_Query::create()
            ->from('Phone')
            ->where('contact_id = ?', $this->id);
    }
}

Šablony a posluchače (Behaviors)

Behaviors jsou chování, které je možné připojit k objektu záznamu tabulky. Chování se nastavuje v definici struktury databáze. Pomocí chování je možné doplnit definice sloupců tabulky. Například u tabulky Phone budeme chtít evidovat datum vytvoření nového telefonu a datum změny záznamu. Proto do tabulky přidáme sloupečky created_at a updated_at, ve kterých budou data uložena. Protože bychom chtěli mít možnost evidovat datum vytvoření a změny i u kontaktu, vytvoříme chování Timestampable.

Prvním krokem je vytvoření třídy Timestampable, která rozšiřuje třídu Doctrine_Template (dále označovaná jako šablona). Šablona může dodefinovat další sloupečky (v našem případě created_at a updated_at) nebo i tabulky a připojit posluchače událostí. Dalším krokem je vytvoření posluchače Timestampable­Listener, který rozšiřuje třídu Doctrine_Recor­d_Listener a poslouchá události vložení nového záznamu nebo aktualizaci stávajícího záznamu. Díky použití DQL je možné odchytávat i události vztahující se k hromadné aktualizaci záznamů a můžeme se vyhnout použití databázových triggerů.

Ukázka možností nastavení chování Timestampable

Doctrine obsahuje již v základní distribuci několik jádrových chování. Jedním z nich je například chování Timestampable, dále pak SoftDelete (záznamy nejsou smazány z databáze, ale pouze označeny příznakem smazáno), Versionable (verzování), Sluggable (SEO přátelské URI), I18N (překladatelné záznamy), Searchable (fulltextové vyhledávání), Geographical (geografické údaje) a NestedSet (stromové struktury v relačních databázích). Tím ovšem možnosti chování nekončí. Pomocí chování můžeme zajistit, že tabulka uchovávající obrázky při vložení obrázku vygeneruje náhled a v okamžiku smazání záznamu v tabulce smaže také související soubory.

Moduly

Složitější aplikace je vhodné dělit do modulů, které obsahují samostatně funkční celky. Aplikace se zpřehlední a moduly je možné použít v jiných aplikacích. Definici struktury databáze je možné rozčlenit do více souborů a automaticky generované soubory jsou vygenerované jak do daného projektu, tak do jednotlivých modulů. Při generování tříd (reprezentujících tabulky) jsou pro každou třídu vygenerované tři soubory (ve skutečnosti více v závislosti na způsobu použití Doctrine).

V případě kontaktu se jedná o BaseContact obsahující definice v kontextu daného projektu. Do BaseContact nikdy nepíšeme kód, protože při dalším generování je smazán a vytvořen znovu. BaseContact je uložen ve složce projektu. Dále je poprvé vygenerován PluginContact, který je uložen do složky modulu. Do třídy PluginContact píšeme kód společný pro všechny aplikace využívající tento modul. Poslední třídou je Contact uložená opět v projektové složce, kam píšeme kód použití pouze v rámci projektu. Třída Contact je také vytvořena pouze jednou a není při dalším generování přepsána.

Chcete se naučit o PHP víc?

Akademie Root.cz pořádá školení Kurz programování v PHP5. Jednodenní kurz programování v PHP 5 je určen všem webovým vývojářům, kteří se chtějí do hloubky seznámit a sžít s programovacím jazykem PHP ve verzi 5. První část kurzu je zaměřena na nový objektový model se všemi jeho vlastnostmi, ošetření chyb pomocí výjimek a efektivní využití těchto konceptů. Druhá část je zaměřena na nové knihovny PHP 5, především pro práci s databázemi, XML a objekty. Pozornost je věnována i zajištění kompatibility s PHP 4, přechodu z této verze a výhledu na PHP 6. Máte zájem o jiné školení? Napište nám!

Doctrine a Symfony

MVC framework Symfony je od počátku vývoje s Doctrine velice úzce spjat. Doctrine je možné používat s libovolným frameworkem, např. Zend frameworkem. Možnosti Symfony a propojení s Doctrine jsou rozsahem mimo tento článek, proto si vyjmenujeme pouze základní výhody. Hlavní přínos je propojení s formulářovou komponentou integrovanou v Symfony. Hodnoty vložené do formuláře jsou přes validátory generované z Doctrine objektu vyčištěné a serializované přímo do databáze. Symfony obsahuje propracovaný a snadno rozšiřitelný CRUD generátor, který opět naplno využívá možností Doctrine. Na ukázce je zobrazen výsledek CRUD generátoru, který byl pouze nastylován CSS soubory, funkcionalita je automaticky vygenerovaná Symfony a Doctrine.

Ivolution - ukázka CRUD generátoru

//CRUD generátor
generator:
  class: sfDoctrineGenerator
  param:
    model_class:           Vat
    theme:                 ivolution
    non_verbose_templates: true
    with_show:             false
    route_prefix:          admin_vat
    with_doctrine_route:   true

    config:
      actions: ~
      fields:  ~
      list:
        title:             Seznam sazeb DPH
        display:           [=name, percentage]
        sort:              [name, asc]
      filter:  ~
      form:    ~
      edit:
        title:             Upravit sazbu "%%name%%"
      new:
        title:             Vložit novou sazbu DPH

Shrnutí funkcí

V tomto díle byly shrnuty pouze hlavní funkce, které činí Doctrine odlišnou oproti ostatním frameworkům. Jak již bylo řečeno na začátku článku, pro hlubší pochopení Doctrine je vhodné nastudovat dokumentaci dostupnou na oficiálním webu. Dokumentace je na rozdíl od jiných open-source projektů velice kvalitně zpracovaná. Mimochodem stejně jako dokumentace frameworku Symfony, kde je možné čerpat další informace.

Framework Propel obsahuje v aktuální verzi 1.4 obdobné funkce jako Doctrine 1.2, bohužel ale prozatím nejsou zpracovány stejně kvalitně nebo elegantně. Propel například nemá podporu vlastního dotazovacího jazyka. Dotazy je nutné psát objektově přes tzv. kritéria objekty, které ale nejsou vhodné pro zápis složitějších výrazů (např. WHERE s promíchanými logickými operátory OR a AND). Alternativou je komponenta DbFinder. Tato komponenta odstraňuje mnoho nedostatků kritéria objektů.

Hlavní nevýhoda Propelu je v zastaralém jádru knihovny a bude nutné celou knihovnu přepsat a odlehčit, aby byl možný další vývoj. Proto je vhodné o Propelu uvažovat pouze v případě, že musíme udržovat aplikaci, která na tomto frameworku již běží. Od roku 2009 převzal vývoj jeden ze zakladatelů frameworku Symfony a autor komponenty DbFinder. Od té doby také začaly vznikat nové verze a určovat se směr dalšího vývoje.

Komentáře

Subscribe
Upozornit na
guest
17 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
jos

větou Díky tomu, že Doctrine zná strukturu databáze (viz další díl článku), je možné nastavovat i objekty v relaci se mi má domněnka potvrdila

frantisek.troster

Můžete mi tedy prosím objasnit, co je podle Vás relace v entitně relačních databázích?

„A relationship captures how two or more entities are related to one another.“

Zdroj: wikipedia.org
http://en.wikipedia.org/wiki/Entity-relationship_model

Vertigo

A relation is defined as a set of tuples that have the same attributes.
http://en.wikipedia.org/wiki/Relational_database

frantisek.troster

Mohl bych argumentovat:

„Vztahy, neboli relace, slouží ke svázání dat, která spolu souvisejí a jsou umístěny v různých databázových tabulkách.“
http://cs.wikipedia.org/wiki/Rela%C4%8Dn%C3%AD_datab%C3%A1ze#Vztahy_mezi_tabulkami

ale nikam bychom se nedostali. Děkuji za upozornění, nevěděl jsem, že termín relation je určen k označení tabulky.

Šaman

„Relace = tabulka“ (zjednodusene) je matematicky pojem a stoji na ni cela relacni algebra (a relacni databaze).

„Relace = vztah mezi tabulkami“ je vseobecne rozsireny nazor, (mozna mylny – wiki NENI zdroj, ktery by prosel pri argumentaci treba na VŠ) ktery asi vychazi z pojmu „relativne“ tedy „v porovnami s necim jinym“.

Pri googlovani mi to bohuzel nabizi vysvetleni „relace = vztah“ (spousta neprilis odbornych zdroju) a „relace = skoro tabulka“ se musi pohledat: http://www.ksi.mff.cuni.cz/~pokorny/vyuka/srbd/rmd/
(sesty odkaz zhora ukazuje rozdil mezi matematickou relaci a tabulkou)

jos

BAM!

http://en.wikipedia.org/wiki/Relation_%28database%29 vypadá na první pohled v pohodě

ten všeobecně zmatenej názor možná taky vzniká tim, že jak lidi zaslechnou o cizích klíčích, tak už je zaboha nenapadne že ta relace je mezi datama v jednom tuple (kdyby nebyla, tak v něm nejsou spolu)

stilett

Já myslím, že zmatek vzniká tím, že v angličtině máme dvě různá slova:

relationship = vztah mezi entitami v entity-relationship modelu, při přenesení modelu do relační databáze se obvykle přemění na vztah mezi tabulkami (cizí klíče),
relation = podmožina kartézkého součinu (množina n-tic), v relační databázi to je tabulka.

Bohužel obě se dají do češtiny přeložit jako relace.

none_

Správná terminologie by asi měla být takhle:

buď Entity a relace – což by mohlo vycházet z ER modelu (Entity-relationship model)

nebo Relace a vztahy mezi nimy – z relační algebry.

David Grudl

To je mi ale slovíčkaření. Vztah mohou mít jak řádky v tabulce, tak tabulky mezi sebou. Že pojem relační databáze pochází z toho prvního vztahu přece neznamená, že je zapovězeno termín „vztah“ použít v jakémkoliv jiném významu, je-li o nich řeč.

jos

i když opomenu fakt, že se autor ke svý nevědomosti upřímě přiznal (což je legrace, když má firmu která se zabývá relačníma db), tak mi stejně příde správný slovo „relace“ vyhradit pro označení … ehm … relace

až se spolu budeme na odborný úrovni bavit o jízdních kolech, tak „kolo“ bude to kulatý a celek „bicykl“

frantisek.troster

Dobrý den,

i přesto, co mi říká zdravý rozum, Vám odpovím. To zda použiji slovo relation nebo relationship (v češtině jsou obě slova mimo jiné překladatelná jako relace nebo vztah), nemá podle mé zkušenosti naprosto žádný vliv na to, zda mohu vést firmu na vývoj nástroje pro modelování relačních databází. Kupodivu uživatelé software oceňují to, že jim aplikace funguje jak má a usnadňuje jim práci, možná by jim přišlo i hloupé toto řešit.

Mimochodem když už zmiňujete tu odbornou úroveň. Očekával bych pod tímto článkem diskuzi na téma, který ORM framework je lepší a takovou diskuzi bych velice uvítal. Určitě bych ocenil i protiargumentaci týkající se nedostatků Doctrine nebo Propelu. To by ovšem vyžadovalo nějaké znalosti a zkušenosti v této
oblasti, určitě si nevystačíte s úspěšně složenou zkouškou z datového modelování.

jos

relation nebo relationship (v češtině jsou obě slova mimo jiné překladatelná jako relace nebo vztah)

to nepopíram, je pouze krajně nevhodný vyjadřovat se nejednoznačně. co bude výsledkem vykonání příkazu „smaž relaci“, pokud nebudou obě komunikující strany chápat slovo „relace“ stejným způsobem?

To zda použiji slovo relation nebo relationship, nemá podle mé zkušenosti naprosto žádný vliv na to, zda mohu vést firmu na vývoj nástroje pro modelování relačních databází.

ani mí šéfové pravděpodobně neví, co se skrývá za slovem „relační“ v sousloví „relační databáze“. ale taky o relačních databázích nepíšou články.

… že jim aplikace funguje jak má …

gratuluju jim i Vám

… diskuzi na téma, který ORM framework je lepší …

tady neposloužim, žádnej nepoužívam, tuhle minisérii čtu jen ze zvědavosti

… určitě si nevystačíte s úspěšně složenou zkouškou z datového modelování …

pokud dobře chápu co se snažíte naznačit, tak odpověď je „ne“, nejsem produktem vysokého školství, převážně vycházim z tohoto

bazo

mna by zaujimalo z akeho softu je ten druhy obrazok, ci je to niake ide, kde sa daju definovat modely pre doctrine alebo nieco uplne ine

frantisek.troster

Dobrý den, oba screenshoty jsou z naší aplikace ORM Designer (http://www.orm-designer.com/) s aktivovanou podporou frameworku Doctrine.

Techi

„magická funkce __set a __get“ – nejedná se o funkce, ale o metody

praethorian

Chlapi mě stejnako připadá, že si tyhle články čtou jen lidi, co už to znají :), tak přece všichni víme jak to autor myslel. Hádat se tu o slovo „fce“ nebo „metoda“ vždyť je to prkotina. ;) Je super, že to někdo sepíše a někdo kdo o tom nemá ani páru si o tom něco přečte. Takhle autory akorát odrazujete od psaní když do nich ryjete :)

P.

Filip Procházka

v článku je malý překlep
$contact->seName(‚Jan Novak‘);

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.