Jaké jsou výhody Doctrine 2 proti čistému SQL? rubrika: Programování: PHP

12 Kit
položil/-a 4.10.2015

Začetl jsem se trochu do článků o Doctrine 2 od Jana Tichého. Zatím jsem u 5. dílu a stále nechápu, k čemu je Doctrine 2 vlastně dobré. Vždyť je to jen mírně ohnutý starý špatný ActiveRecord. Mám k tomu několik otázek:

  • Proč se struktura tabulek čte z dokumentačních komentářů, když mnohem lépe poslouží reflexe na databázi?
  • Proč se u dvojice svázaných tabulek pracuje s každou tabulkou samostatně? Není to poněkud zvrácené?
  • Proč se tam příšernými oklikami řeší v Doctrine 2 situace, které jsou v SQL na jednom řádku?
  • Proč je při používání Doctrine 2 propagováno používání getterů/setterů, když jsou v daném případě zcela zbytečné?

Původně jsem se chtěl naučit Doctrine 2 kvůli tomu, abych si rozšířil obzor, ale nadzvedlo mě to ze židle tak, že si nejsem jist, zda v tom budu pokračovat. Něco málo mě inspirovalo pro dotvoření mého doménového uspořádání servisních modulů (obdoba entit v článku), ale celkově z toho zatím mám pocit, že Doctrine 2 téměř nic nedělá a to co dělá, dělá zbytečně komplikovaně.

Ještě jedna otázka na závěr: Mohou se entity v Doctrine 2 překrývat? Například tím, že se jedna tabulka nachází ve více entitách?

Edit doplnění pro @fprochazka - příklad, který mi nesedí:

// Změníme titulek produktu s ID 123
$product = $em->find('Product', 123);
$product->setTitle('Foo bar');
// Odstraníme kategorii s ID 123
$category = $em->find('Category', 123);
$em->remove($category);
// Všechny změny výše potvrdíme a pošleme do databáze
$em->flush();

Jsou zde třídy Product a Category. Proč je to rozděleno? Kategorie je přece součástí produktu č. 123, je to i na stejném vstupním formuláři. Mělo by se to dělat naráz. V mém controlleru by to volání vypadalo následovně:

$product = new Product($post);  // $post - data z formuláře předaná přes $_POST
$model->update($product);       // $product obsahuje všechny informace, které $model (DM) potřebuje k provedení změn

Komentáře

  • michal.svojanovsky : IMHO se to tu již řešilo - je to zkrátka pro ty, co chtějí mít objektový návrh modelu a nechtějí to vymýšlet. 3.10.2015
  • Taco : @Kit: No, já to dělám ještě lépe, já prostě zavolám json_encode($product) (to jen aby bylo jasné, jak jsou ty dva tvé příklady ekvivalentí). 6.10.2015
  • Kit : @Taco: json_encode($product) je v pořádku. Říkáš tím, že objekt $product má implementováno rozhraní JsonSerializable. Jen to dělá něco úplně jiného, než zápis do databáze... 6.10.2015
  • Jakub Macek : Příklad je dost nestandardní, ale realizovatelný. Kategorie nebývají součástí produktu (kompozice), ale bývají pouze asociovány. Tam se používá velmi elegantně http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/refe... . Jinak ta popsaná situace je v tomtéž dokumentu o něco výš. 7.10.2015
  • Tomáš Votruba : Jaký tvůj současný problém bys chtěl vlastně touto novou znalostí vyřešit? 30.10.2015
  • maryo : Ten new Product vypadá, jakobys vytvořil novej produkt a ne ho updatoval. 30.10.2015
  • Kit : @maryo: On v tu chvíli ještě neví, zda ho budu vytvářet, updatovat nebo mazat. O tom rozhoduje až model. Product je vlastně servisní třídou pro doménu "product". 30.10.2015
  • maryo : Aha, to je IMO pak trochu zavádějící nazývat to Product. Pod Product si představím produkt a ne něco, co se o produkt stará. A jak vypadá ten model? Nebo ten doménovej objekt? Je tam vůbec? Nebo je to jen pole? 30.10.2015
  • Kit : @maryo: Model drží datové zdroje. Jeho metoda update() vybere vhodný datový zdroj a přidá ho jako parametr metody update() injektovaného objektu. Product není pole, je to třída obsahující konkrétní příkazy do datových zdrojů. Pole (resp. slovník) se vstupními daty je do ní pouze injektováno v konstruktoru. Možná třídu Product časem přejmenuji na \Project\Product\Service, aby ten název byl popisnější, ale bude to hlubší zásah do všech tříd a zatím se mi do toho moc nechce. Také proto, že mi to šlape jako hodinky :-) 30.10.2015
  • maryo : Pod doménovym objektem jsem myslel něco, co reprezentuje ten produkt jako takovej. Něco, co má identitu. Co vzniká zavoláním konstruktoru. Pokud to tam neni, je otázka, do jaký míry je ta hlavní doména vlastně OOP. Šlapat jako hodinky to ale může :). 31.10.2015
  • Kit : @maryo: Produkt jako takový je pouze v databázi. Je možné získat jeho kopii, je možné ho updatovat nebo smazat. V té doméně však nejsou jeho data, doména servisní vrstvy slouží pouze jako rozhraní k té databázi. Schválně nepíši k tabulce, ale k databázi, protože jedna doména může obsluhovat více tabulek a naopak jedna tabulka může být obsluhována ve více doménách. Transakce se děje buď přímo v databázi, anebo v doméně. Nikdy se neděje mimo, např. v controlleru nebo view. Ty pracují pouze s hotovými transakcemi jako s nedělitelnými jednotkami. 31.10.2015
  • maryo : To je možná zajímavej přístup, ale nejspíš platí co jsem psal. V Doctrine pracuješ s daty toho objektu jako s jeho vlastními. Nepotřebuješ tam žádnou servisní vrstvu. 31.10.2015
  • Kit : @maryo: Doctrine má přece za úkol nahradit servisní vrstvu. Na můj vkus to však dělá polovičatě a druhou polovinu práce nechává na controlleru nebo view. Úkolem mých controllerů je pouze sestavit požadavek a poslat ho modelu, což se obvykle vejde na zmíněné dva řádky. 31.10.2015
odkaz
10 fprochazka
odpověděl/-a 4.10.2015

V první řadě si musíš uvědomit, že u Doctrine 2 jde o práci s objekty nikoliv s tabulkami a relačním modelem. Pointou je dovolit ti navrhnout si entity tak jak ti vyhovuje, ideálně tak, aby ti z toho nevznikl anémický model http://www.martinfowler.com/bliki/AnemicDomainModel.html.

Tomuhle je často vytýkáno, že není nikdy možné 100% odstínit datázi od aplikace. Jenže to není pointa. Hlavní pointou doctriny je dělat mapování dat z databáze na objekty, nikoliv schovat databázi.

Vždyť je to jen mírně ohnutý starý špatný ActiveRecord.

ActiveRecord != Data Mapper

Proč se struktura tabulek čte z dokumentačních komentářů, když mnohem lépe poslouží reflexe na databázi?

  • číst strukturu z dokumentačních komentářů není povinné, existují drivery na XML a YAML
  • reflexe nad databází funguje do té doby, dokud máš 100% aplikovanou konvenci. Já například vůbec nepíšu názvy sloupců, jak se mají jmenovat v databázi, protože mám zapnutý https://api.kdyby.org/class-Doctrine.ORM.Mapping.UnderscoreNamingStrateg..., tak jsou názvy sloupců předvídatelné, pojmenovávám pouze tabulky v annotaci @Table(name="xyz")

Proč se u dvojice svázaných tabulek pracuje s každou tabulkou samostatně? Není to poněkud zvrácené?

Můžeš to rozvést? Na příkladu ideálně?

Proč se tam příšernými oklikami řeší v Doctrine 2 situace, které jsou v SQL na jednom řádku?

Můžeš to rozvést?

Proč je při používání Doctrine 2 propagováno používání getterů/setterů, když jsou v daném případě zcela zbytečné?

Protože to je nejsnadnější způsob jak s entitou pracovat. To ovšem neznamená, že to tak musíš nezbytně používat. Já se snažím getterům a setterům vyhýbat, pokud to jde.

Komentáře

  • Kit : Používám techniku, která je podobná jak ActiveRecordu, tak i DataMapperu. Podstatným rozdílem je, že jsem z ní vyčlenil perzistentní data do messengerů. Dá se tedy říct, že místo jedné (AR) či dvou vrstev (DM) používám tři vrstvy. Příklad s dvojicí tabulek se pokusím doplnit do úvodního dotazu. 4.10.2015

Pro zobrazení všech 6 odpovědí se prosím přihlaste:

Rychlé přihlášení přes sociální sítě:

Nebo se přihlaste jménem a heslem:

Zadejte prosím svou e-mailovou adresu.
Zadejte své heslo.