Doctrine hydrate array nebo object?

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Jiří Nápravník
Člen | 710
+
0
-

Jakým způsobem získáváte data v Doctrine, resp. jakou hydraci, pokud mi jde o to data jen vypsat na stránce a nebudu s nimi dále pracovat, např. vykreslení článku.

Co jsem koukal, tak většina lidí doporučuje (a dokonce i v dokumentaci Doctrine to je), využít v tomhle případě Array hydration, kvůli lepší výkonnosti. Jaká je praxe u vás? Nepřijde se pak přeci jen o ten pěkný objektový přístup?

Michal Vyšinský
Člen | 608
+
0
-
  1. myslím, že ti to smažou
  2. většinou na výpis použiji hydrate array a abych nepřišel o objektový přístup, tak jej dám do ArrayHash – nijak jsem to ale neměřil tak nevím, zda to nakonec nevyjde nastejno
mkoubik
Člen | 728
+
0
-

Já si pro dané view vytvářím hlopý data transfer object který obaluje entitu a vytahuje si z ní co potřebuje. Případné problémy s výkonem řeším cacheováním buď toho DTO nebo přímo kusu HTML z něj vygenerovaného v závislosti na id a názvu entity.
Je to sice trochu psaní navíc, ale pak máš jasně definovaná data co jsou do šablony a je ti jedno jestli je vezmeš z entity, z pole, z cache nebo si je vycucáš z prstu při prototypování.
Navíc, i když je to lákavé, doctrine entita jako objekt z persistenční vrstvy rozhodně nemá co pohledávat v šabloně (jo, vím že to tak dělají všichni a jsem ochotný se o tom kdykoliv pohádat u piva :-).

Jiří Nápravník
Člen | 710
+
0
-
  1. No v popisu kategorie je pímo napsáné Doctrine 2 a viděl jsem tu dost příspěvků, čistě na Doctrinu, tak uvidíme
  2. To s tím arrayhash mě nenapadlo, pěkné, díky
Jiří Nápravník
Člen | 710
+
0
-

@mkoubik: můžeš ukázat konkrétněji, co myslíš data transfer objectem? Jinak cachování HTML vygenerovaného je naprd ne? Protože o úroveň výše se musím dotazovat stejně databáze a vezme si to pod správu entitymanager. Nebo mi něco uniká?

mkoubik
Člen | 728
+
0
-

Něco jako:

/**
 * @method getName
 * @method getAddress
 */
class CustomerDTO extends \Nette\Object
{
	private $name;
	private $address;

	public __construct(Customer $customer)
	{
		$this->name = $customer->getName();
		$this->address = new AddressDTO($customer->getCurrentAddress());
	}
}

Když pak kodér potřebuje třeba počet objednávek daného zákazníka tak si tam dopíše public $ordersCount = 666; a založí isuue aby tam někdo ten údaj rozumným způsobem dostal, místo toho aby psal hrůzy typu {= count($customer->orders)}.

Edit: základní ukázka je tady (bez toho cacheování a nad NDB, ale pro představu to snad stačí).

Editoval mkoubik (21. 1. 2014 14:17)

enumag
Člen | 2118
+
0
-

@mkoubik: Tohle se mi líbí. :-)

mkoubik
Člen | 728
+
0
-

To cacheování funguje tak, že pokud se nespustí daná část šablony tak se do db ani nikam jinam pro ta data nesahá – používám na to LazyAccessor a LazyIterator. Do cache se to ukládá s tagem typu aggregate/customer/123 a při updatu dané entity se vyvolá událost která to invaliduje.

Jiří Nápravník
Člen | 710
+
0
-

@mkoubik: díky za reakci. Je to pěkné, ale není tohle v podstatě duplikát entity a mám tak zbytečně zduplikovaná data v systému? Možná se to hodí u nějakých komplikovaných entit se spoustou vazeb, ale v případě nerozsáhlých entit, to bude ve výsledku skoro 1:1 entita ne?

To, že to count($this->collection), je celkem divočina v šablone, souhlas, nicméně nic mi nebrání dát getter na count přímo do entity samotné. Nicméně v obou případech musíme hydratovat jako object, což asi nebude tedy úplně výkonově ideální.


Edit: ještě reakce na cachování

můžeš mě nejak více nasměrovat? Pokud beru klasický přístup:

presenter získá z modelu něco a pošle to do view. Tak ve view, pokud chci cachovat, tak už mám stejně pozdě, jelikož už v presenteru se mi zavolal model a tedy i databáze etc. Samozřejmě řeší to cachování už výstupu z modelu, ale jak to cachovat přímo v šabloně?

Editoval Jiří Nápravník (21. 1. 2014 14:44)

mkoubik
Člen | 728
+
0
-

Právě že presenter do view nepošle přímo ten objekt, ale jeho proxy, která ta data načte až když na ní sáhneš.

$this->template->customer = new LazyAccessor(function() use ($repository, $id) {
	return $repository->getById($id);
});
mkoubik
Člen | 728
+
0
-

Jiří Nápravník napsal(a):

Možná se to hodí u nějakých komplikovaných entit se spoustou vazeb.

To je jedna z výhod. Představ si třeba zboží v eshopu. Taková entita může být poměrně dost složitá, ale ve výpisu kategorie tě zajímá v podstatě jen název, cena a nějaký obrázek, v detailu zboží tě toho zajímá trochu víc, při objednávce tě zajímají zase jiné atributy a vazby, v administraci chceš vidět počet objednávek za poslední měsíc, pak můžeš mít třeba skladový modul kde potřebuješ úplně jiná data atd. Tahat pokaždé celý agregát s milionem joinů by bylo neefektivní.

Fór je právě v té cache, díky ní si vytáhneš jednoduchý malý objekt přesně s těmi daty co potřebuješ.

V podstatě je to takové CQRS akorát místo read modelu v nějaké nosql db máš tu cache, která je v nette hodně mocná (a konec konců může být třeba v redisu).

Další výhoda je to jasně definované rozhraní mezi presenterem a šablonou – kodér kóduje a programátor programuje. Navíc může klidně kodér kódovat nad namockovanými daty, zatímco programátor teprve přemýšlí jakou db vrstvu použije.

Navíc představa že šablona může ovlivnit pokládané SQL dotazy o 2 – 3 úrovně abstrakce pod sebou něčím jako {$author->related('book.author_id') as $book} nebo {foreach $author->getBooks() as $book} by mi asi nedávala spát.

Jiří Nápravník
Člen | 710
+
0
-

ad. LazyAccessor aha, to je pěkné. je něco takového implementováno (přímo v nette, či jako rošíření) nebo to si mám udělat sám?

A když máš na mysli tedy vždy jiný pohled na entity, tak máš několik DTO? Nebo máš jedno DTO, v cachi uloženo ucelou entitu, včetně všech těch joinů a vytahuješ si na požádání z té cache co potřebuješ? Nebo ještě úplně jinak?

mkoubik
Člen | 728
+
0
-

To DTO je pro konkrétní šablonu, komponentu, skupinu podobných šablon apod. Prostě jsou to data co šablona potřebuje z „modelu“, může to být slepenec víc entit, výsledek nějakého výpočtu apod. Ta vazba DTO ↔ entita je dost volná. Klidně tam ani žádná doctrine nemusí být – můžeš to načítat ze souboru nebo přes síť.

Editoval mkoubik (21. 1. 2014 17:07)

Jiří Nápravník
Člen | 710
+
0
-

Aha, tak to už trochu mění situaci a odpadá to, jak jsem říkal, že to je dupliakce entity. Ta to tím pádem není. Pokud to tedy chápou dobře, tak jak jsi uvedl příklad Produkt v přehledu kaegorie, produkt v detailu a produkt v objednávace, tak to jsou tři DTO. Chápu srávně?

A ještě pokud vezmu klasický přístup presenter si vytáhne data z modelu a pošle do view. Tak ty DTO plníš kde. Předpokládám, že to je práce pro presenter…

Filip Procházka
Moderator | 4668
+
0
-

DTO jsem si nikdy nedokázal dost dobře ospravedlnit a ještě složitější je někomu vysvětlit výhody.

Btw, když už, tak bych se asi zbavil i nepotřebných getterů

enumag
Člen | 2118
+
0
-

@Filip Procházka: Tohle vypadá jako zajímavý pattern, ovšem většinou stejně potřebuješ většinu položek na nějakém výpisu zobrazit takže těch getterů se tak často nezbavíš.

Jiří Nápravník
Člen | 710
+
0
-

Filip Procházka: U Tebe by mě celkem zajímalo, jak řešíš předání dat do view vrstvy z Doctrine. Celou entitu jako objekt, nebo array kvůli výkonnosti. Mluvím o případu kdy bude z ní v tom daném requestu jen číst.

Editoval Jiří Nápravník (22. 1. 2014 0:49)

Filip Procházka
Moderator | 4668
+
0
-

Normálně předávám klidně entity do šablon, naši kodéři nejsou cvičené opice abychom to po nich museli opravovat :) Imho tenhle argument je sám o sobě většinou neplatný. Problém to může být jedině když hodně outsourcuješ a často narážíš na blbce.

Dává mi to ovšem smysl jako „kontrakt“ jaký data potřebuješ, protože můžeš mít různý DTO pro front, api, crony, … a nemusíš mít všechny operace v entitě

Pole většinou moc nepoužívám, buďto entity, nebo hydratuju list idček pro jinou query která vytahuje entity.

Jiří Nápravník
Člen | 710
+
0
-

Díky za reakci, mě nejde ani o ty kodéry tolik, protože já stejně dostanu čistou šablonu a dodělat tam to napojení apod stejně musím já:-) Jde mi spíše o tu výkonostní stránku, jelikož entita prý sežere hodně zdrojů kvůli tomu, že si jí bere pod správu EntityManager apod.

Ono to pole sice tohle řeší, ale zase mi vypadnou gettery entit, které řeší nějakou logiku…

Filip Procházka
Moderator | 4668
+
0
-

No hele, rozdil je jenom v čase (reflexe vs přejmenování pár klíčů do výsledného pole). Když mapuješ na entity tak je to i paměťově efektivnější, protože php od 5.4 lépe pracuje s objekty co mají definované properties (tedy například entity) než s poli, které jsou úplně dynamické.

A pokud budeš přemapovávat entity na DTO tak neušetříš vůbec nic, spíše naopak.

Jiří Nápravník
Člen | 710
+
0
-

Ok, tak já zkusím zatím čistě entity (resp. partial v některých případech) a optimalizaci budu řešit, až nastanou nějaké problémy.

DTO jsem prozatím vyloučil… Spíše jsem myslel, array z doctrine a hodit přes ArrayHash jak navrhoval někdo výše, ale to je asi výkonově podobné jako DTO…

Filip Procházka
Moderator | 4668
+
0
-

ArrayHash je jenom syntakticky cukr, bude to stejně pomalé a možná o drobeček pomalejší než raw pole.

Jiří Nápravník
Člen | 710
+
0
-

JJ, chápu, ale nechtěl jsem přijít o objektový přístup v šabloně, pokud bych hydratoval pole

akadlec
Člen | 1326
+
0
-

@mkoubik: pokud dobře chápu to tvé DTO, tak ty v okamžiku kdy z repozitáře taháš data pro šablonu je proženeš mapperem který data nasype do toho DTO a objekt DTO předáš šabloně? DTO používáš jen pro kodéry do šablony aby nešahali na celý objekt entity?

mkoubik
Člen | 728
+
0
-

Jiří Nápravník napsal(a):

ad. LazyAccessor aha, to je pěkné. je něco takového implementováno (přímo v nette, či jako rošíření) nebo to si mám udělat sám?

Hodil jsem to na github.