Jak zařídit, aby Repository našeptávalo metody z Mapperu?

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Šaman
Člen | 2635
+
0
-

Ahoj, zkouším zrovna YetORM, ale předtím jsem používal Fabikovo DAO a různé jiné „ORMy“.
A všechny (včetně ukázek v QuickStartu) volají z Repository přímo databázi.
Rád bych přešel na známý pětivrstvý model – oddělit Repository a Mapper je jednoduché. Pro začátek stávající repozitář přejmenovat na mapper (jsou tam přímé dotazy do db) a vytvořit třídu Repository, která magicky deleguje všechny neznámé metody na mapper. (Pak to samozřejmě upravit, některé metody patří do Repository, ale o tom tenhle post není).

Je nějaká možnost, aby Repository našeptával metody z Mapperu? Například si dynamicky vygeneroval anotace?

Nebo je nutné tyto metody uvést, ideálně jako rozhraní (aby bylo možné měnit mapper a mít jistotu, že je kompatibilní). Druhá možnost je určitě čistější, ale taky pracnější a protože málokdy se mění mapper, tak na většině projektů bude dál vrstva Mapper a Repository splývat v jedno.

enumag
Člen | 2118
+
0
-

Osobně se mi moc nelíbí Repository jen jako proxy na Mapper. Můj závěr je mít ke každému repository interface a v aplikaci pro potřeby DI používat právě ten interface. Pak mohu mít více implementací repository a mapper nepotřebuju, respektive repository a mapper splynou, tak jak píšeš. Je to více psaní, časem si na ty interfacy chci napsat generátor.

castamir
Člen | 629
+
0
-

Jasně, že si jsou Mapper a Repository hodně podobné, ale řešení, které popisuje @enumag se mi také zrovna moc nelíbí. Např. pokud potřebuju pro nějakou tabulku mít speciální přístup (např. closure table), tak si s pouhým interface a továrničkou nad ní asi nevystačím. Během dneška zkusím sepsat nějaký demo příklad…

Šaman
Člen | 2635
+
0
-

enumag napsal(a):

Osobně se mi moc nelíbí Repository jen jako proxy na Mapper.

Rozhodně nemyslím repository JEN jako proxy, to by ztrácel smysl.

Ale spousta metod je stejných v mapperu i repository – findAll(), findByFoo(), getById() a nechci jim v repository psát obálku.
Začínám se přiklánět ke kompromisu v Repository vyjmenovat v anotacích podporované metody mířené na mapper (zároveň je to seznam metod, které mapper musí obsahovat, aby byl kompatibilní). A magický __call bude na mapper mířit jen ty metody, které jsou v anotacích, aby to nebylo moc woo-doo.

Ten interface by totiž nevyřešil problém našeptávání – byl by to interface mapperu a repository by ho nemohl implementovat, protože jeho metody fyzicky neobsahuje (jen magicky). Bylo by to jen super řešení kompatibility mapperu (BookMapperNDatabase i BookMapperDibi by musely implementovat IBookMapper), ale je to psaní navíc a seznam pomocí anotací stačí programátorovi aby věděl, co repository od mapperu očekává.

castamir
Člen | 629
+
0
-

Za mě –1

Bavíme se tu o abstraktní třídě Repository, od které všechny ostatní třídy Repository dědí. Ten kód se napíše pouze jednou, takže bych si s tím napsáním kódu hlavu nelámal. Napíšeš si to pouze jednou a už to nemusíš řešit. Kdybychom se bavili o něčem, co budeš psát často, tak ano, udělej si to třeba přes anotace (typicky atributy a sloupce u Entity resp databázové tabulky), ale ne zde.

Podle mě je to zbytečná magie, se kterou třeba ani @DG v ApiGenu nepočítá.

Editoval castamir (5. 3. 2013 15:16)

Šaman
Člen | 2635
+
0
-

Nevím, jestli si rozumíme – BookRepository bude jen jeden pro daný objekt (Book). Mapperů může být více.

Obecný Repository by řešil tu práci s anotacemi, teoreticky by mohl obsahovat findAll() a getById() ale nic víc.

Mě jde spíš o spoustu getterů specifických pro daný objekt – u BookRepository např. findByAuthor(), findByYear(), …

Editoval Šaman (5. 3. 2013 15:39)

castamir
Člen | 629
+
0
-

Jenže BookRepository bude velice podobný dalším Repository (např. AuthorRepository). Základní funkcionalita se tedy dá přenést do rodiče (abstract class Repository) a ve FooRepository tak zůstanou jen ty operace, které se skutečně vážou pouze k dané datové množině.

Edit: Aha, už tě chápu. Měl jsem za to, že se bavíme o veškerých operacích nad Repository. Tohle už je na tobě. Klidně si to dej přes anotace. Všechny základní operace ale implementuj v tom základním Repository a přes __call je jen volej.

Editoval castamir (5. 3. 2013 15:44)

Šaman
Člen | 2635
+
0
-

:)

On ten repository toho u mě moc jiného neumí, než zprostředkovávat data z mapperu a přidat možnost je nějak filtovat, řadit a limitovat. A to samozřejmě obstarává abstraktní Repository, to je jasné.

//edit: a už si zase nerozumíme. Základni operace přes žádný __call nepůjdou, _call by jen zjistilo, že např. findByAuthor() existuje v anotaci a zavolá $this->mapper->findByAuthor(). Nic víc bych magii nesvěřil a vzhledem k tomu, že je v anotacích přesně vyjmenované jaké metody bude možné volat, tak ta magie je imho přijatelná. A anotace zařídí i našeptávání, o což mi na začátku šlo.
Zůstává jediný problém a to, že bez interface IBookMapper mi někdo může předat mapper, který metodu findByAuthor() nezná. Ale to teď neřeším, abych řekl, tak v práci jsem ještě mappery neměnil, maximálně driver.

Editoval Šaman (5. 3. 2013 15:54)

enumag
Člen | 2118
+
0
-

@Šaman: Posledních několik příspěvků jsem nečetl, ale pokud se ty metody na které potřebuješ proxy z repository do mapperu pro všechny repository stejné tak bych použil traitu.

castamir
Člen | 629
+
0
-

Blbě jsem se vyjádřil. Call jen na ty tvé metody findByFoo.

Spíš bych viděl problém v údržbě. Občas se stane, že se struktura tabulky mění, takže bys to musel promítnout i do anotací.

Co se find* metod týče. Dříve jsem používal jednu hlavní findBy(conditions, limit, offset) metodu a tu volal ve všech ostatních find* metodách. Nyní jsem to ale přepsal na findBy(where, conditions), kde conditions se nyní používají jen pro limit, offset, order by apod. klauzuje ale nikoliv pro where. A právě na takový findBy by se daly triviálně navázat to tvé findBy* metody.