ORM frameworky pro PHP5: Doctrine ORM

PHP logo

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.

Seriál: ORM frameworky pro PHP (3 díly)

  1. ORM frameworky pro PHP5: Obecný úvod 27.1.2010
  2. ORM frameworky pro PHP5: Doctrine ORM 3.2.2010
  3. ORM frameworky pro PHP5: Databázový model 10.2.2010

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.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 17

Přehled komentářů

jos Re: ORM frameworky pro PHP5: Doctrine ORM
frantisek.troster Re: ORM frameworky pro PHP5: Doctrine ORM
Vertigo Re: ORM frameworky pro PHP5: Doctrine ORM
frantisek.troster Re: ORM frameworky pro PHP5: Doctrine ORM
Šaman Re: ORM frameworky pro PHP5: Doctrine ORM
jos Re: ORM frameworky pro PHP5: Doctrine ORM
stilett Re: ORM frameworky pro PHP5: Doctrine ORM
none_ Re: ORM frameworky pro PHP5: Doctrine ORM
David Grudl Re: ORM frameworky pro PHP5: Doctrine ORM
jos Re: ORM frameworky pro PHP5: Doctrine ORM
frantisek.troster Re: ORM frameworky pro PHP5: Doctrine ORM
jos Re: ORM frameworky pro PHP5: Doctrine ORM
bazo 2.obrazok
frantisek.troster Re: 2.obrazok
Techi OOP poznámka
praethorian Re: ORM frameworky pro PHP5: Doctrine ORM
HosipLan chybka
Zdroj: https://www.zdrojak.cz/?p=3161