Architektura Doctrine 2

Sunday, May 16th, 2010 @ 09:37

Pozor, článek je notně zastaralý a prozatím jsem nenašel čas ho zrevidovat. Berte prosím zde prezentované informace s rezervou. Pokud byste se rádi dozvěděli více informací o Doctrine 2, přečtěte si seriál Doctrine 2 od Jana Tichého.

Rád bych ukázal základní stavební kameny PHP ORM nástroje Doctrine 2 a srovnal je s 5-ti vrstevným modelem, který představil Honza Tichý. Nebudu se zde věnovat databázové vrstvě (DBAL), kterou Doctrine používá a budu se snažit zaměřit více na vrstvy, které jsou blíže modelu. Stejně tak se nebudu snažit představit práci s Doctrine 2, která by vydala na několik samostatných článků. Použiji pouze to nejnutnější, pokud to bude potřeba.

Základní stavební kameny

V ORM Doctrine 2 nalezneme tyto základní členy:

  • Entity
  • Entity manager
    • Identit map
    • Unit of Work
  • Repository
  • Persister

Entity – entita doménového objektu

Entita v Doctrine 2 představuje libovolný doménový objekt. V případě klasického CMS to tedy může být například entita Article. Doctrine 2 nám absolutně nic nediktuje, což je její největší výhoda. Abychom naší třídu Article připravili na spolupráci s Doctrine, oanotujeme ji za pomocí konstrukce”@Entity”. Vypadá to takto:

/**
 * @Entity
 */
class Article
{
   /** ... */
   private $id;
   /** ... */
   private $title;
   /** ... */
   private $text;

   // Setters and getters
   ...
}

Entita má několik atributů, které odpovídají určitým sloupcům v určité tabulce. Z pohledu architektury je to ale nezajímavá věc, takže si to dovolím přeskočit. Zajímavější je možná to, že entita spolupracující s Doctrine 2 se může v aplikaci nacházet v různých stavech.

  • nová (new)
  • řízená (managed)
  • neřízená (detached)
  • odstraněná (removed)

Nová entita

Nová entita je taková entita, kterou jsme vytvořili a neřekli entity manageru, aby ji uložil. Pokud novou entitu nepředáme entity manageru pro uložení, změny v entitě provedené se nám neuchovají.

Řízená entita

Řízená entita je entita, která byla předána entity manager pro uložení do repozitáře, nebo již uložená entita, která byla z repozitáře načtena. Změny v této entitě se nám uloží.

Neřízená entita

Neřízená entita je taková entita, která je uložena v repozitáři, ale my jsme ji od něho odpojili. Změny, které v entitě provedeme, se nám neuloží a v příštím načtení dostaneme entitu ve stejném stavu, jak jsme ji dostali při aktuálním načtení.

Odstraněná entita

Odstraněná entita je entita, která byla řízená, ale my jsme se jí rozhodli odstranit z repositáře. Entita se vymaže.

Entity manager

Entity manager je ústřední část ORM. Je pouze jeden a obhospodařuje práci se všemi entitami přes jejich repozitáře. Samotný Entity manager se v Doctrine 2 skládá ze dvou částí.

Unit of Work

Tato část entity manageru má na starosti řízení SQL dotazů. Zajišťuje, že se příkazy nevykonávají hned, ale co nejpozději, což zajišťuje větší efektivitu v práci s databází. Jedná se o typickou implementaci vzoru Unit of Work.

Identity map

Implementace vzoru Identity map. Tato část zajišťuje to, že máme v entity manageru vždy pouze jednu instanci entity, což má hned několik výhod. Například to, že ne zbytečně neptáme vícekrát na jedno a to samé, nebo že si omylem nepřepisujeme data v případě, že bychom omylem vytvořili více instancí jedné entity. V reálu to tedy vypadá následovně:

$article = new Article();
$article->title = 'Foo';
$article->text = 'Big white bra.'
$em->persist($article); // Entity manager instance
...
...
...
$theSameArticle1 = $em->find('Article', $article->id);
$theSameArticle2 = $em->find('Article', $article->id);

Entity $theSameArticle1 a $theSameArticle2 jsou úplně to samé jako entita $article. Zde je dokonce pravděpodobné (v závislosti na tom, co ve ve vynechaném kódu), že vůbec neproběhla žádná komunikace s databází. Na začátku jsme sice $article uložili, ale jak jsme psal výše, Unit of Work nechává komunikaci s databází na co nejpozději, takže pokud mu to nikdo ještě neřekl pomocí metody flush, drží si data pouze v sobě a do databáze je odešle někde dál.

Repository – zdroj entit

Možná jste si všimli, že jsem několikrát psal o tom, že se entity načítají z repozitáře, ale já jsem entitu hledal v entity manageru. Pravdou je, že volání

$em->find('Article', $article->id);

je pouhou zkratkou k repozitáři, který si entity manager načítá. Opravdové volání by vypadalo nějak takto

$em->getRepository('Article')->find($article->id);

A co je to ten repozitář? Repozitář je zdroj entit. Můžeme si ho buď napsat sami, nebo můžeme využít výchozí repozitář, který nám Doctrine 2 nabízí. Ten se jmenuje EntityRepository. Pro většinu entit nám tento výchozí repozitář bude pravděpodobně stačit, ale občas je dobré napsat si vlastní repozitář pro nějakou entitu. V tomto nám Doctrine 2 nechává otevřená vrátka.

Persister – mapovač entit

Persister je o úvroveň nižší vrstva než repozitář, která slouží k namapování (přes anotace, XML, nabo YAML) dat na atributy entity. Persistory jsou jádrem ORM a ve většině případů se s nimi nesetkáme. Je ale dobré vědět, jaká část ORM přebírá mapovací konfiguraci a pracuje s ní.

Zhodnocení architektury Doctrine 2 s ohledem na HoTův 5-ti vrstesný model

Možná znáte článek Honzy Tichého, ve kterém píše o 5-ti vrstevném modelu. S dovolením si vypůjčím jeho obrázek. Model je rozvrstvený takto:

Model podle Doctrine 2 je rozvrstvený takto:


Ze schémat můžeme vidět, že se modely nijak výrazně neliší, právě naopak. Pět vrstev modelu nám zůstalo zachováno  a navíc jsme dostali vylepšenou komunikaci s databází (přes Unit of Work a Identity map), která částečně vynahradí fakt, že ORM SQL dotazy generuje, což se často negativně podepisuje na výkonu.

Externí odkazy

Tags: , ,
Posted in Uncategorized | Comments Off

Comments are closed.