Lekce 1 - Úvod do Doctrine 2 v Nette frameworku
Vítám vás v první lekci kurzu o technologii ORM (objektově relační mapování) v Nette frameworku. Tato technologie je konkurenční klasickému získávání tabulek z databáze pomocí čistého SQL nebo nějaké jeho nástavby jako např. Dibi a umožňuje pracovat s relační databází objektově. Série vám technologii, která se v komerčních aplikacích často využívá, představí na systému pro správu obsahu, kde použijeme asi nejznámější ORM knihovnu pro PHP - Doctrine 2. Osobně nemám rád dlouhé slohy, ale pár slov na úvod je potřeba říci.
Články budou mít za úkol seznámit vás s použitými technologiemi a ukázat, jakým dalším způsobem lze napsat webovou aplikaci. Nebudou proto vysvětleny veškeré detaily týkající se použitých technologií, ale ukáží vám postup při vývoji.
Je proto potřeba mít alespoň základní znalost:
- frameworku Nette
- šablonovacího systému Latte
- OOP v PHP (výjimky, jmenné prostory, rozhraní atd.)
- znalost Doctrine 2 či jiného ORM je výhodou (ale pochopíte ji během seriálu, doporučuji i průběžně sledovat dokumentaci)
V článcích budou uvedeny mimo jiné i vlastní názory, případné klady
nebo zápory použitého algoritmu a tak podobně. Doufám, že pro vás budou
přínosné a otevřou vám nové možnosti.
Systém bude umět:
- registraci a přihlášení uživatele
- správu kategorií (vytváření, úprava, mazání, schvalování)
- správu článků (vytváření, úprava, mazání)
- správu uživatelů (vytváření, úprava)
- role uživatel/admin
- překlad do cizích jazyků
- nastavení účtu (v podstatě jen popis uživatele)
Pro lepší představu si ještě ukažme alespoň 2 screenshoty z hotového systému:


Představení technologií
Při tvorbě systému budou použity tyto technologie:
Framework Nette
Nette je český framework využívající architekturu MVP (Model-View-Presenter). Obsahuje ale pouze vrstvy VP, pro modelovou má jen databázový wrapper. Je proto čistě na vývojáři, jak si modelovou vrstvu sestaví. Díky tomu máme velmi dobrý základ pro naši aplikaci - nemusíme se téměř starat o bezpečnost aplikace, cachování a vůbec většinu nudných, avšak důležitých věcí za nás framework udělá sám, a my se tak můžeme plně soustředit na samotné úkoly.
S pomocí frameworku tedy můžeme ušetřit mnoho času a to v reálné aplikaci znamená vydělat více peněz. Jiné frameworky jsem (v době, kdy jsem psal článek) nezkoušel, ale jsem přesvědčen, že je Nette velmi kvalitním frameworkem s dobrou komunitou. Jen je škoda, že byla dokumentace velmi dlouho pouze v češtině, a tak se příliš nedostal do světa.
- Oficiální web Nette: http://nette.org
- Dokumentace: http://doc.nette.org
Doctrine 2
Doctrine je tzv. ORM, neboli objektově relační mapování (object-relational mapping), jehož cílem je mapovat objekty na relační databázi. Doctrine nicméně umí oba směry (z entit udělat strukturu databáze a naopak ze struktury databáze vytvořit kostru entit).
S tímto nástrojem můžeme v podstatě zapomenout, že máme nějakou databázi. Veškerá naše práce s daty se týká objektů, SQL příkazy vůbec nejsou potřeba - ORM se o to postará samo.
DQL
Doctrine má i vlastní dotazovací jazyk nazvaný Doctrine Query Language. Syntaxí se velmi podobá SQL, ale rozdíl je v tom, že místo s tabulkami a sloupci pracujeme s objekty a jejich atributy.
Příklad použití DQL:
// takto by vypadal SQL dotaz do databáze SELECT `name`, `email`, `ip` FROM `user` WHERE `id` > 10 ORDER BY `name` DESC // a takto příkaz vypadá v DQL SELECT u FROM Jmenny\Prostor\User u WHERE u.id > 10 ORDER BY u.name DESC
Velmi podobné, že? DQL obsahuje i pokročilejší konstrukce, jako je řazení, seskupování atd. Je potřeba vytvořit si alias pro vybrané entity (v naše případě písmeno 'u'). Defaultně se entita načítá celá, když ale chceme jen některá data, využijeme k tomu klíčové slovo partial.
Entita
Entita je objekt nesoucí data. Nemá přístup k databázi ani k jiným datovým úložištím. Jejím úkolem je uchovávat a předávat data. Jelikož by ale neměla být pouhou přepravkou, může (a měla by) obsahovat i validaci vstupu, jako je například ošetření délky předávané hodnoty atributu. Také může obsahovat libovolné metody pracující s daty, které jí náleží. Veškeré informace o entitě samotné a jejich atributech zapisujeme pomocí anotací.
Entity manager
Jak z názvu vyplývá, jedná se v podstatě o správce entit. Jeho úkolem je ukládat nové entity (tzv. persistence), odpojovat je z fronty a nebo je úplně smazat. Všechny entity, jejichž data se mají nějakým způsobem projevit v databázi (ať je to SELECT, INSERT, UPDATE nebo DELETE), musí být pod správou Entity manageru.
I když by se mohlo zdát, že se stará o práci s databází, v jeho hlubinách se skrývá objekt využívající vzoru Unit of work (stejně tak se i jmenuje), který tuto úlohu zastává. Obsahuje frontu všech změn a po příkazu všechny změny provede i v databázi.
Jelikož se jedná o automatizaci, máme zde i jednu nehezkou nevýhodu - všechny změny v databázi jsou provedeny podle určitého pořadí (např. prvně je INSERT, poté UPDATE apod.), což může někdy vyvolat nechtěné chování (např. že se vymaže právě vytvořený záznam, protože odpovídá podmínce v DELETE). Všechny nové entity je potřeba dostat pod správu Entity manageru příkazem persist(). Pokud chceme všechny změny provést i v databázi, musíme použít příkaz flush().
Jednoduchá ukázka práce entity s Entity managerem:
// každou entitu je potřeba označit anotací Entity /** * @ORM\Entity */ class User { // všechny atributy je potřeba označit anotací @Column, do závorky poté udáváme údaje, jako je datový typ, délku, možnost nullable atd. /** * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue */ private $id; /** * @ORM\Column(type="string") */ private $name; // obyčejný setter, ve kterém můžeme využít i jednoduchou validaci public function setName($name) { if (strlen($name) > 15) { throw new Exception("Name is too long"); } $this->name = $name; } public function getName() { return $this->name; } // jelikož entita nemá být pouhou obálkou na data, může vykonávat i jednoduché operace public function getFullId() { return "{$this->name}#{$this->id}"; } } // předpokládejme, že máme Entity manager nakonfigurovaný $user = new User(); $user->setName("Martin"); $entityManager->persist($user); // uložíme objekt do fronty, zatím se s databází nic nestane $entityManager->flush(); // provede všechny změny - v našem případě vytvoří nový záznam v tabulce user (odvozen z názvu entity), nový záznam bude mít ID 1 $userFromDb = $entityManager->find("User", 1); // User je název entity, 1 je ID echo $userFromDb->getFullId(); // Martin#1
Všimněte si, že entita neumí komunikovat s databází. Doctrine 2 se odprostila od vzoru ActiveRecord a využívá nyní vzor DataMapper.
Doctrine je rozsáhlý nástroj, zde jsem nastínil jen velmi stručně hlavní pojmy. Mnohem více se dozvíte v samotné dokumentaci.
- Oficiální web: http://doctrine-project.org
- Dokumentace: http://doctrine-orm.readthedocs.org
Rozšíření Kdyby
Kdyby je rozšíření Nette frameworku, které obsahuje spoustu užitečných nástrojů. V našem seriálu použijeme Kdyby\Translation na překlad textů a Kdyby\Doctrine na usnadnění práce s Doctrine.
- Oficiální web: http://www.kdyby.org
Architektura aplikace
Vrstvy View a Presenter má Nette zabudované v sobě, do těch raději zasahovat nebudeme. Důležité ale je navrhnout si modelovou vrstvu. Naše bude vypadat takto:
- Entity - objekty přepravující data
- Fasády - tzv. služby (services), které provádějí určité úkony (ukládání záznamů, editace, získávání atd.), v aplikaci je vždy jen jedna instance dané fasády a mají koncovku Facade
- Pomocné třídy pro skládání dotazů - uvidíte během seriálu, lze s nimi efektivně skládat DQL dotazy, mají koncovku Query
- Form factories - třídy pro vytváření a zpracovávání formulářů, mají koncovku FormFactory
Presentery budou pracovat s fasádami a ty budou pracovat s Entity managerem. Proto nikdy v presenteru neuvidíte objekt třídy EntityManager, ale pouze příslušné fasády. Všechny závislosti (ať v presenterech, fasádách nebo form factories) bude automaticky obstarávat DI container Nette.
Všechny presentery budou ve jmenném prostoru App\Presenters, modelové třídy App\Model a formuláře App\Forms.
Abychom neměli v presenterech úplný nepořádek, budeme psát atributy a metody tímto způsobem:
- prvně atributy
- action a render metody pro danou stránku pod sebe (actionStranka, renderStranka, actionStranka2, renderStranka2 atd.)
- zpracování signálů (
handle<Signal>
) - vytváření komponent (
createComponent<Component>
)
Adresářová struktura
Struktura zůstává stejná dle Nette, jen do složky app/ přidáme složku lang/, do které budeme přidávat jednotlivé soubory sloužící pro překlad.
Kořenový adresář

Složka app/

Složka presenters/

To bude z první lekce vše. Chápu, že obsahovala mnoho teorie, ale bez pár úvodních slov to bohužel nejde.
Příště, v lekci CMS v Nette a Doctrine 2 - Kostra aplikace, vytvoříme základní strukturu naší aplikace.