Článek v rubrikách:

»PHP
»MySQL
»Dibi

ORM, Row Data Gateway, Table Data Gateway, Active Record, Data Mapper

Co to vlastně ORM je a jaké jsou návrhové vzory objektově relačního mapování. Jak efektivně pracovat s databází?


ORM je objektově relační mapování, je to jakýsi most mezi relačními databázemi (RDBMS) a objekty (OOP). Snažíme se o mapování objektů na data v databázi. Úkolem ORM by mělo být automatické skládání SQL dotazu a zabezpeční základních operací CRUD (create, read, update, delete). Chceme pracovat s objekty, aniž bychom se museli starat o to jakým způsobem budou v pozadí ukládány a získávány.

Tím snad obecný výklad končí, v některých detailech už se různé zdroje rozchází. Podle některých by mělo ORM zabezpečit i dědičnost (tu databáze neznají), mapování datových typů (např. bool mysql vůbec nemá - nahrazuje ho za tinyint(1)), validaci některých omezení jako je např. povinná hodnota (v mysql jedině jako NOT NULL, ovšem když se sql dotazu vůbec neúčastní pak je záznam bez problémů vložen), mapování asociací a sdružených objektů a další.

Nejvýznamější v této oblasti je asi autor a softwarový architekt Martin Fowler, zejména jeho kniha Patterns of Enterprise Application Architecture (ke stažení).

V této knize popisuje nejvýznamější návrhové vzory, které se týkají ORM. Dělí je na návrhové vzory architektury a návrhové vzory chování.

Návrhové vzory architektury (Architecture patterns)

Návrhový vzor architektury popisuje vazby mezi relačním databázovým úložištěm a objektovým modelem.

Domain Model (Doménový model) je souhrn tříd naplňující logiku vazeb, specifikuje atributy tříd, primární klíče, cizí klíče a další potřebné vlastnosti, které přesně odpovídají tabulkám v databázi. Jeden objekt odpovídá jedné tabulce v databázi.

Jednotlivé objekty, bychom pak měli vhodně rozšířit o business logiku, kterou databáze nemohou zajistit. Např. vydat zboží ze skladu lze až po jeho zaskladnění, nebo objednat zboží lze pokud je volná skladová dostupnost.

Table Data Gateway

Je přístup k databázové tabulce, komunikace s databází je obalena do jedné třídy, která data získává i čte, mapuje a vrácí kolekci dat. Najdeme v takové třídě metody jako insert, update, delete, ale i vyhledávací funkce find, findByUrl apod.

Insert

  1. $customer=new Customer();
  2. $customer->name="František";
  3. CustomerTable::insert($customer);

Update

  1. $customer=CustomerTable::find(1);
  2. $customer->name="František";
  3. CustomerTable::update($customer);

Row Data Gateway

Jedna se o model samotného řádku v tabulce databáze. Mapuje přesně vlastnosti objektu, které odpovídají struktuře databázové tabulky, tedy jednotlivým sloupcům. Sám umí provést běžné operace jako insert, update, delete. Pro samotné získání tohoto objektu se využívá externí třídy Finderu.

Insert

  1. $customer=new Customer();
  2. $customer->name="František";
  3. $customer->insert();

Update

  1. $customer=CustomerFinder::find(1);
  2. $customer->name="František";
  3. $customer->update();

Active Record

Kombinuje Row Data Gateway a funkce pro získání objektu z databáze (již existujícího modelu).

Insert

  1. $customer=new Customer();
  2. $customer->name="František";
  3. $customer->insert();

Update

  1. $customer=Customer::find(1);
  2. $customer->name="František";
  3. $customer->update();

Data Mapper

Tento návrh je vhodné použít pokud nelze zajistit zcela ekvivalentní nastavení objektu vůči databázi, například pro účely dědění, nebo jiných OOP vztahů, které relační databáze neumí zachytit. Data Mapper je prostředník mezi relačním schématem databáze a objektovým schématem, využívá se hlavně pro účely, kdy tyto pohledy nejsou totožné.

Problémy nastávají s určováním a povědomím mapperu o stavu objektu, o stavu v databázi a v aplikační vrstvě, jak předcházet duplicitám apod.

Vhodným příkladem je přihlašování uživatelů. Např. systém, který má vlastnosti redakčního webu a zároveň eshopu. Začnete stavět redakční systém a potřebujete kvůli diskusím logovat uživatele (userid, user, pass), později potřebujete zahrnout PR soutěže apod. a potřebujete znát o soutěžícím jeho adresu a jméno (userid, user, pass, name, adress), později přidáte možnost prodávat nějaké suvenýry a uděláte z toho eshop, potřebujete logovat zákazníka (userid, user, pass, name, adress, ICO, DIC, phone, email). A teď co s tím? V OOP budete mít základní objekt User a podědí ho Reader/Competitor a také Customer, ovšem relační databáze Vám tohle nedovolí a nezbyde nic jiného než udělat tři tabulky ve kterých udržujete všechny údaje duplikátně a nastává problém ze které tabulky budete ověřovat přihlašovací údaje. Přesné mapování tabulek v tomto případě může být problém a k vyřešení by měl pomoci Data Mapper.

Data Mapper je objekt , který požádáme o získání zákazníka a je už na něm jak se postará o namapování tohoto objektu a ze kterých tabulek, s objektem zákazníka už pracujeme jako v OOP a příliš nás nezajímá, jak je uložen v databázi. Ten samý objekt se stará o ukládání, mazání a další operace v databázi.

Insert

  1. $customer=array('name'=>"František");
  2. Mapper::Customers()->insert($customer);

Update

  1. $customer=Mapper::Customers()->findById(1);
  2. $customer->name="František";
  3. Mapper::Customers()->update($customer);

Návrhové vzory chování (Behavioral Patterns)

Tyto návrhové vzory se zabývají způsobem jak efektivně data načítat a ukládat. Jak je jednoznačně identifikovat v paměti, jak zajistit konkurenční přístup (jak ukládat data, které nám někdo uložil pod nosem) a další. Nejvýznamnější návrhové vzory jsou např. Unit of Work, Identity Map, Join fetching, Lazy Load atd.

Článek vznikl jako součást seriálu, kde se snažím vytvořit vlastní relační mapování postavené nad Dibi, zde je pro ujasnění pár pojmů ohledně ORM, které můžete prostudovat, abychom věděli o čem se bavíme.

Zajímavé zdroje (další vzory a názory odborníků na přístup)

Pro tvorbu vlastního ORM se asi budu soustředit na návrhový vzor Active Record, případně jeho nějakou kombinaci s Data Mapperem nebo jiným vzorem.

Repository pattern

Určitě stojí za zmínku, krátké porovnání Active Record a Repository pattern najdete v článku od Aleše Roubíčka.

 

podobné články

15.12.2009DbDibiOrm - DibiConnection - staticky, instančně, přistupovat přímo k dibi factory?(17%)
09.12.2009DbDibiOrm(17%)
07.12.2009ORM, dibi, MySQL defaultní hodnoty (pokračování článku)(17%)
03.12.2009MySQL povinné hodnoty, NULL hodnoty, defaultní hodnoty, prázdné hodnoty a jak by se mělo chovat ORM(17%)
22.07.2009ORM, Zend_Db, Doctrine, DibiTableX a co bych navrhl pro dibi(17%)