Jak zařídit, aby Repository našeptávalo metody z Mapperu?
- Šaman
- Člen | 2666
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
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
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 | 2666
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
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 | 2666
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
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 | 2666
:)
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)
- castamir
- Člen | 629
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.