„BasePresenter is antipattern.“

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

Ahoj,
na Twitteru psal David, že BasePresenter je antipattern. Což potvrdil i na nedávné přednášce konference PHPlive v Ostravě.

Jen bych se chtěl zeptat:

  1. Proč tedy v sandboxu BasePresenter pořád je?
  2. A jak v Nette řešit obvyklé věci (kontrola zabezpečení; předávání dat, která se vypisují napříč celou aplikací, do šablony; apod.) správně?

Díky

enumag
Člen | 2118
+
0
-

Já bych se s dovolením přidal s dotazem kde zachytávat výjimky z metody checkRequirements za účelem přesměrování (např. pokud uživatel není přihlášen, přesměrovat na stránku kde se má přihlásit).

Jan Tvrdík
Nette guru | 2590
+
0
-

BasePresenter je antipattern, ale nejde se ho rozumně zbavit, protože UI\Presenter je antipattern. Tedy první je IMHO potřeba zrefactorovat UI\Presenter a pak bude možné odstranit BasePresenter.

Jan Suchánek
Člen | 404
+
0
-

a co BaseControl nebo BaseRepository?

mkoubik
Člen | 728
+
+1
-

Jan Tvrdík napsal(a):

+1

Např v symfony je controller jakýkoliv objekt, který nemusí nic dědit (resp. jakékoliv \callable). Pokud se tedy chcete vyhnout dědičnosti a service locatoru, tak si musíte do každého controlleru předávat tunu služeb. Nakonec se to nejpohodlněji řeší pomocí různých god-objektů.

David Matějka
Moderator | 6435
+
0
-

@jenicek: BaseRepository je zlo, kteremu se jde vyhnout (respektive jeho dedeni), vice treba tady

BaseControl je zlo, kteremu se vyhnout nejde (no dobre, tak jde, ale je to komplikovanejsi :) )

David Grudl
Nette Core | 7734
+
+1
-

V Nette nemusí presenter nic dědit přinejmenším od verze 0.7. A klidně jím může být i callback, od nějaké 2.0 dev.

Problém BasePresenteru je ten, že obvykle neobsahuje společnou logiku pro všechny své potomky, ale jen pro většinu. Takže se stane, že BasePresenter::beforeRender připravuje data pro renderování layoutu, ačkoliv se nakonec kreslí třeba XML výstup či chybová stránka 500 v ErrorPresenteru, nebo při kontrole přihlášení se obchází SignPresenter atd.

Všechny tyto věci se dají řešit jinak. Úplně nejjednodušší je nahradit BasePresenter několika traity (SignedTrait, LayoutTrait, …). Protože ty fungují od PHP 5.4, nemůžu je v příkladech používat. Pracnější a akademicky čistější je kompozice, tj. místo traitů vytvořit samostatné třídy a ty předat přes DI.

Jan Suchánek
Člen | 404
+
0
-

@matej21: BaseRepository jasné, uváděl jsem ho proto, aby mi bylo jasné zda k presenteru a komponentě bude v budoucnu přistupováno stejně? Je to ten samý problém?

Editoval jenicek (17. 12. 2013 15:31)

Tharos
Člen | 1029
+
0
-

matej21 napsal(a):
BaseRepository je zlo, kteremu se jde vyhnout (respektive jeho dedeni), vice treba tady

Aby to ale nevedlo k nahrazení jednoho zla jiným zlem. :)

Jak předáváš v aplikaci bez konkrétních repositářů závislosti? Předáváš vždy „celé piano“?

Editoval Tharos (17. 12. 2013 15:59)

David Ďurika
Člen | 328
+
0
-

Tharos napsal(a):
Jak předáváš v aplikaci bez konkrétních repositářů závislosti? Předáváš vždy „celé piano“?

nie, ved v configu nastavit parametre…

- \App\Foo(@doctrine.dao(\App\BarEntity))

Editoval achtan (17. 12. 2013 16:26)

Tharos
Člen | 1029
+
0
-

achtan napsal(a):

nie, ved v configu nastavit parametre…

- \App\Foo(@doctrine.dao(\App\BarEntity))

No a jaký máš u toho parametru type hint? Poznáš z toho, na čem je ta třída skutečně závislá? A není škoda přijít o auto-wiring takových služeb?

Filip Procházka
Moderator | 4668
+
0
-

Dědit DAO/Repository jenom kvůli autowiringu je antipattern :P

Tharos
Člen | 1029
+
0
-

To zdaleka není jenom kvůli auto-wiringu. O tom, že je to anti-pattern, se mile rád nechám přesvědčit, ale v téhle věci se fakt nespokojím s nějakým kategorickým tvrzením. Já totiž tvrdím, že anti-pattern je generické Repository/DAO. :) Takové, jaké je doporučované v Kdyby.

Rozumíme-li anti-patternem toto:

- Some repeated pattern of action, process or structure that initially appears to be beneficial, but ultimately produces more bad consequences than beneficial results, and
- An alternative solution exists that is clearly documented, proven in actual practice and repeatable.

Můžeme ta dvě řešení porovnat.

Generic Repository/DAO (v duchu Kdyby)

Výhody

  • O něco méně psaní
  • Jakási zvrácená radost, že není zapotřebí nic dědit

To dědění ale hned dementuji. Konkrétní repositáře nemusí dědit od BaseRepository, mohou jej dostávat kompozicí. Takže v obou zde srovnávaných přístupech se lze obejít bez dědění.

Nevýhody

  • Třídy, které jsou na nějakých repositářích závislé, mají příliš obecné type hinty (závisí na nějakém repository namísto konkrétním repository) a velmi vágně tak deklarují své skutečné závislosti
  • Z výše uvedeného důvodu nefunguje auto-wiring a třeba v neonu je zapotřebí psát @doctrine.dao(\App\BarEntity), což mi přijde jako pěkná pruda
  • Tipuji, že absolutně nefunguje našeptávání v IDE (jakou entitu mi vrátí $em->getRepository('Role')->find(1)?)
  • Při refaktoringu nebo statické analýze se musí zkoumat, kde se vlastně jaké repository reálně používá

Konkrétní Repository/DAO

Výhody

  • Jasně deklarované závislosti
  • Z výše uvedeného důvodu fungující auto-wiring
  • IDE perfektně napovídá:
  • Při refaktoringu nebo statické analýze je jasně dané, kde se jaké repository používá

Nevýhody

  • Nutnost vytvářet třídy, které jsou mnohdy zdánlivě prázdné (rozeberu ještě dále)
  • Tedy o něco málo více psaní

Tohle bych ale taky hned rád dementoval. Dovolím si vcelku přesnou analogii s výjimkami: konkrétní výjimky, které jsou také v důsledku prádzné třídy, vám také vadí? Pokud ne, proč ne? Tipuji, že proto, protože mají jasný význam – potomci svým názvem definují, o jakou konkrétní výjimku se jedná. S repositáři je to stejné. Byť většina poděděných může být prázdnými třídami, svými názvy říkají, o jaké konkrétní repositáře se jedná.

Navíc představují logický prostor, kam umístit potřebné anotace pro kvalitní napovídání od IDE (viz mé screenshoty výše) a také připravují půdu pro případ, že u nějakého repositáře jednou bude zapotřebí například ukládat entitu do databáze nějak specificky, nebo ji specificky mazat…


Sečteno podtrženo mi řešení propagované v Kdyby nepřijde jako nijak výhodné a jelikož existuje lepší řešení, je to tedy anti-pattern.

Dovolím si ještě trefný článek přesně na tohle téma. V tom článku autor víceméně tvrdí, že obecné Repository/DAO může existovat, ale je anti-pattern s ním přímo pracovat v aplikaci ($em->getRepository('Role') či @doctrine.dao(\App\BarEntity)). V aplikaci je prostě nejlepší mít konkrétní repositáře. Ty mohou vzniknout buďto děděním z nějakého BaseRepository („pragmatické řešení“), anebo kompozicí („akademické řešení“).

Mile rád se nechám věcnými argumenty (nikoliv výkřiky a kategorickými tvrzeními) přesvědčit, že se mýlím. ;)

Editoval Tharos (18. 12. 2013 10:59)

Filip Procházka
Moderator | 4668
+
0
-

V první řadě je potřeba si uvědomit, že tvoje řešení funguje v LeanMapperu, já se celou dobu bavím o Doctrine.

Výhody

O něco méně psaní

O psaní to vůbec není, kdybych měl problém s psaním kódu tak nepoužívám doctrine ale Nette\Database a nepíšu modely ;)

Jakási zvrácená radost, že není zapotřebí nic dědit

Nechytej mě za prohlášení které měly být pokus o vtip ;)

Nevýhody

Třídy, které jsou na nějakých repositářích závislé, mají příliš obecné type hinty (závisí na nějakém repository namísto konkrétním repository) a velmi vágně tak deklarují své skutečné závislosti
Z výše uvedeného důvodu nefunguje auto-wiring a třeba v neonu je zapotřebí psát @doctrine.dao(\App\BarEntity), což mi přijde jako pěkná pruda

V malých aplikacích je to šedá zóna, ale jakmile ti aplikace naroste, předávat jenom repozitáře se ukáže jako blbost, protože často potřebuješ volat $entityManager->flush(), opět kvůli speficikům doctriny.

Například já už v Damejidlo @doctrine.dao(\App\BarEntity) vůbec nepoužívám, píšu

class ArticlesFacade extends Nette\Object
{
	public function __construct(EntityManager $em)
	{
		// $this->articles = $em->getDao(App\Articles::getClassName());
		$this->articles = $em->getDao(App\Articles::class);

Tipuji, že absolutně nefunguje našeptávání v IDE (jakou entitu mi vrátí $em->getRepository('Role')->find(1)?)

Tipuješ správně, ale je to irelevantní, protože stejně tak bys musel dědit i EntityManager, aby jsi si tam doplnil potřebné anotace… hups, to skoro nejde, jenom hackem, kterej se stejně chová staticky a je tedy nedostatečný.

Při refaktoringu nebo statické analýze se musí zkoumat, kde se vlastně jaké repository reálně používá

Tohle je taky irelevantní, protože $em->getRepository() ti stejně IDE nepozná správný typ.

Navíc představují logický prostor, … a také připravují půdu pro případ, že u nějakého repositáře jednou bude zapotřebí například ukládat entitu do databáze nějak specificky, nebo ji specificky mazat…

Což v doctrine stejně neuděláš. Protože my dodržujeme SRP a na persistenci máme vlastní vrstvu ;)


Sečteno a podtrženo mi z toho vyplývá, že problém řešíš na špatné vrstvě a správné řešení je napsat rozšíření do PHPStormu (nebo Netbeans), které bude umět simulovat generika nad repository třídami.

Jediné tvoje argumenty které jdou aplikovat na Doctrine jsou ty s napovídáním a ty jsou taky mimo, protože to má řešit IDE.

Tharos
Člen | 1029
+
0
-

@Filip Procházka:

V první řadě je potřeba si uvědomit, že tvoje řešení funguje v LeanMapperu, já se celou dobu bavím o Doctrine.

Já bych strašně rád zůstal na obecné rovině, protože v jádru jde o ryze obecný problém. To, že jsem kvůli stručnosti ve své ukázce použil entitu z Lean Mapperu, je nepodstatné. Klidně tu svou ukázku mohu přepsat do následující podoby:

class Author
{

	/** @var string */
	private $name;

	/**
	 * @param string $name
	 */
	public function setName($name)
	{
		$this->name = $name;
	}

	/**
	 * @return string
	 */
	public function getName()
	{
		return $this->name;
	}

}

/**
 * @method Author[] findAll
 */
class AuthorRepository extends BaseRepository
{
}

$authorRepository = new AuthorRepository;

foreach ($authorRepository->findAll() as $author) {
	echo $author-> // zde mi IDE napoví metody getName a setName včetně správného typu
}

Tohle řešení funguje v Lean Mapperu i v Doctrine, viz dále.

Já: Jakási zvrácená radost, že není zapotřebí nic dědit

Ty: Nechytej mě za prohlášení které měly být pokus o vtip ;)

Tak Tě chytnu za jiné. :) Ty od dědičnosti odrazuješ poměrně často. To je z mého pohledu chvályhodné, ale namísto toho, abys ji nahradil například kompozicí, tak ji zde nahrazuješ nějakým generic objektem. To považuji za anti-pattern.

Já: Třídy, které jsou na nějakých repositářích závislé, mají příliš obecné type hinty (závisí na nějakém repository namísto konkrétním repository) a velmi vágně tak deklarují své skutečné závislosti
Z výše uvedeného důvodu nefunguje auto-wiring a třeba v neonu je zapotřebí psát @doctrine.dao(\App\BarEntity), což mi přijde jako pěkná pruda

Ty: V malých aplikacích je to šedá zóna, ale jakmile ti aplikace naroste, předávat jenom repozitáře se ukáže jako blbost, protože často potřebuješ volat $entityManager->flush(), opět kvůli speficikům doctriny.

Abstrahujme nyní od $entityManager->flush() a zkusme zůstat na obecné rovině. Já tvrdím, že čím je aplikace větší, tím důležitější je předávat třídám ty nejkonkrétnější závislosti, jaké jenom lze. Jedině tak zůstane „graf závislostí“ transparentní a snadno udržovatelný. Jakmile mě něco nutí předávat „celé piano“, je to na pováženou.

Například já už v Damejidlo @doctrine.dao(\App\BarEntity) vůbec nepoužívám, píšu

class ArticlesFacade extends Nette\Object
{
    public function __construct(EntityManager $em)
    {
        // $this->articles = $em->getDao(App\Articles::getClassName());
        $this->articles = $em->getDao(App\Articles::class);

Ve Tvém článku máš v závěru kapitolu „Co jsem tím získal?“ a v ní tvrdíš, že už skoro vůbec nepotřebuješ EntityManager. A pak si takhle někde to „piano“ předáš, abys z něj vytáhl DAO… Jak si to mám vyložit? :)

Já: Tipuji, že absolutně nefunguje našeptávání v IDE (jakou entitu mi vrátí $em->getRepository(‚Role‘)->find(1)?)

Ty: Tipuješ správně, ale je to irelevantní, protože stejně tak bys musel dědit i EntityManager, aby jsi si tam doplnil potřebné anotace… hups, to skoro nejde, jenom hackem, kterej se stejně chová staticky a je tedy nedostatečný.

Já: Při refaktoringu nebo statické analýze se musí zkoumat, kde se vlastně jaké repository reálně používá

Ty: Tohle je taky irelevantní, protože $em->getRepository() ti stejně IDE nepozná správný typ.

Mně to přijde jako naprosto relevantní. Netuším, proč bych musel dědit EntityManager. Vždyť i v Doctrine lze mít konkrétní repositáře. Co víc, snad všude se k tomu nabádá, nebo mi něco uniká? Když si otevřu třeba Doctrine quick start … tadá, hned tam na mě vyskočí nějaký konkrétní repositář (class BugRepository extends EntityRepository).

Fakt jsem pátral po tom, co Tě k Tvé koncepci přivedlo, a zaměřil jsem se na články, na které se odkazuješ. Ale například i zde Benjamin Eberlei používá konkrétní repositáře.

Já: Navíc představují logický prostor, … a také připravují půdu pro případ, že u nějakého repositáře jednou bude zapotřebí například ukládat entitu do databáze nějak specificky, nebo ji specificky mazat…

Ty: Což v doctrine stejně neuděláš. Protože my dodržujeme SRP a na persistenci máme vlastní vrstvu ;)

Že dodržujete SRP vás šlechtí, ale jak to souvisí s naší diskuzí? Zase nedoržujete Law of Demeter ;). Klidně si v tom repositáři představ nějakou jinou specialitu. Ber to prosím na obecné rovině.


Sečteno a podtrženo mi z toho vyplývá, že problém řešíš na špatné vrstvě a správné řešení je napsat rozšíření do PHPStormu (nebo Netbeans), které bude umět simulovat generika nad repository třídami.

Tvůj závěr mě fascinuje :D. Co řeším na špatné vrstvě? Proč bych si měl v aplikaci dobrovolně vyrábět problém, pro jehož řešení bych pak potřeboval nějaké speciální rozšíření do IDE, když s konkrétními repositáři ten problém vůbec nemám?

Věřím, že Doctrine má určitá specifika, kvůli kterým je nejpohodlnější předat si celý EntityManager. Jak to řeší jiné projekty nad Doctrine, které využívají konkrétní repositáře? Nebylo by lepší mít pro volání flush nějakou proxy službu a předávat si jenom tu? (Nechytej mě ale prosím za tenhle návrh, to jen střílím od boku.)


Takže, sečteno podtrženo, Tebou doporučovaný postup v Kdyby z výše uvedených důvodů stále považuji za anti-pattern.

Jediné tvoje argumenty které jdou aplikovat na Doctrine jsou ty s napovídáním a ty jsou taky mimo, protože to má řešit IDE.

Chceš říct, že v Doctrine nelze mít konkrétní repositáře? ;) IMHO je tohle Tvé závěrečné tvrzení naprosto nepodložené a ryzí výkřik do tmy na závěr. :)

Editoval Tharos (18. 12. 2013 17:51)

Filip Procházka
Moderator | 4668
+
0
-

Já bych strašně rád zůstal na obecné rovině, protože v jádru jde o ryze obecný problém. To, že jsem kvůli stručnosti ve své ukázce použil entitu z Lean Mapperu, je nepodstatné. Klidně tu svou ukázku mohu přepsat do následující podoby:

Jenže to nefunguje, stejně jako nemůžeš srovnávat dibi a doctrine.

Já tvrdím, že čím je aplikace větší, tím důležitější je předávat třídám ty nejkonkrétnější závislosti, jaké jenom lze.

Píšeš to jako bych tomu někde odporoval :)

Jakmile mě něco nutí předávat „celé piano“, je to na pováženou.
Ve Tvém článku máš v závěru kapitolu „Co jsem tím získal?“ a v ní tvrdíš, že už skoro vůbec nepotřebuješ EntityManager. A pak si takhle někde to „piano“ předáš, abys z něj vytáhl DAO… Jak si to mám vyložit? :)

Takže popořadě… EntityManager není „celé piáno“, je to objekt jako jakýkoliv jiný a má své zodpovědnosti. Pár z nich je možné udělat v repository, ale některé zkrátka ne.

Dřív jsem ho taky viděl jako hovado, který je lepší nepoužívat, jenže to se ukázalo jako zbytečná paranoia. Máš si to vyložit tak, že jsem udělal experiment a zjistil jsem, že některé části byly dobré a některé špatné. Těch špatných se zbavuju (nebojím se předávat si entitymanager) a dobré si nechávám (dodržuju SRP a nedědím dále dao/repository).

Mně to přijde jako naprosto relevantní. Netuším, proč bych musel dědit EntityManager.

Aby ti napovídalo $em->getRepository(Article::class)->find(1)

Když si otevřu třeba Doctrine quick start … tadá, hned tam na mě vyskočí nějaký konkrétní repositář (class BugRepository extends EntityRepository).

Jistě, ale pak to skončí tak, že takový repozitář má 200 metod na seletování dat. Pár jsem jich takových viděl, takže si stojím za tím že dědit repozitáře není dobrá cesta.

Repository je jenom alias abys nemusel psát $em->find(Article::class, 1) ale uložíš si do proměnné/property $em->getRepository(Article::class) a voláš $articles->find(1), který umí ještě nějaké další šikovné věci, není to nic víc, nejdůležitější objekt v Doctrine je EntityManager.

Tvůj závěr mě fascinuje :D. Co řeším na špatné vrstvě? Proč bych si měl v aplikaci dobrovolně vyrábět problém, pro jehož řešení bych pak potřeboval nějaké speciální rozšíření do IDE, když s konkrétními repositáři ten problém vůbec nemám?

Jenže ty jako středobod své argumentace používáš nějaké napovídání a napovídání je doména IDE, proto IDE by mělo mít podporu pro konkrétní ORM nástroj a potom je jedno jestli problém máš nebo nemáš, protože nebudeš muset psát anotace nad repository classy ;)

V Jave tohle řeší generika, vytvoříš si repository jako generickou třídu a předáním classy entity IDE hned ví co ti má napovídat. Tvoje řešení jednoduše supluje „hloupost“ jazyka. Stejně jako to dělaly dřív anotace nad generovaným ServiceLocatorem v Nette. $this->context->application určitě pamatuješ ;)

Jak to řeší jiné projekty nad Doctrine, které využívají konkrétní repositáře?

Musí si předat konkrétní repozitáře aby si zavolali metody co potřebují a předat si i EntityManager aby si zavolali flush().

Chceš říct, že v Doctrine nelze mít konkrétní repositáře?

To přece nikde netvrdím, přečti si to znovu a pozorněji ;)


Pořád stahuješ diskuzi na obecnou rovinu, tam bych s tebou možná souhlasil, jenže máme tady nějaké podmínky které jsou aktuální a jen tak se měnit nebudou a proto je potřeba je vzít v potaz. Takže je mi líto, že jsem tě nepřesvědčil, ale ani ty jsi nepřesvědčil mně.

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

Filip Procházka napsal(a):
Jistě, ale pak to skončí tak, že takový repozitář má 200 metod na seletování dat. Pár jsem jich takových viděl, takže si stojím za tím že dědit repozitáře není dobrá cesta.

Dovolím si vstoupit vám do diskuse.

Nekončí to ale pak často tak, že ty fasády/services využívající DAO se stanou pak právě takovými nesmyslně velkými objekty? Samozřejmě můžeme argumentovat tím, že to záleží jak si to každý napíše a rozdělí ty fasády, ale tak můžeme argumentovat i u repositářů, že záleží jak si to každý napíše

Milo
Nette Core | 1252
+
0
-

Mohu poprosit, abyste se drželi subjektu? ORM je taky zajímavý, ale ať se tom čtenář nemusí přehrabovat… díky.

Tharos
Člen | 1029
+
0
-

@Filip Procházka:

Díky za odpověď. Pokusím se už jen stručně zareagovat alespoň na pár bodů:

EntityManager není „celé piáno“, je to objekt jako jakýkoliv jiný a má své zodpovědnosti. Pár z nich je možné udělat v repository, ale některé zkrátka ne.

Přes Entity Manager se dá přistoupit ke všem repositářům, takže v tomhle směru to takové „piano“ je. Je to mimo jiné prostě takový service locator, ze kterého podle potřeby „taháš“ repositáře. Předat si někam Entity Manager a pak si z něj vytáhnout nějaké jednotlivé repositáře je v bledě modrém něco jako předat si někam systémový DI kontejner a pak si z něj vytáhnout, co potřebuješ. Se všemi pozitivními i negativními důsledky.

Jistě, ale pak to skončí tak, že takový repozitář má 200 metod na seletování dat. Pár jsem jich takových viděl, takže si stojím za tím že dědit repozitáře není dobrá cesta.

To je právě totální blbost. :) Přemýšlím, jestli tohle píšeš naschvál, anebo jestli si to opravdu myslíš? To spolu přece vůbec nesouvisí. To, abys v repositáři neměl tunu findBy<XYZ> metod, lze vyřešit vhodnými query objekty (nebo alespoň criteria objekty ), což Ty v Kdyby děláš. Jenomže ty může zpracovávat konkrétní repositář úplně stejně, jako nějaký generický. Ostatně třeba takhle vypadají právě mé repositáře z poslední doby: jsou to konkrétní repositáře (UserRepository, BookRepository), které mají typicky jen minimum metod. Běžně si vystačí s findBy(BookQuery $query), findOneBy(BookQuery $query), findCountBy(BookQuery $query) (a je vedlejší, jestli tyto metody získají děděním, anebo jestli je skutečně mají a třeba jen delegují práci na nějaký obecný repositář předaný kompozicí). Rozhodně to nejsou repositáře o 200 metodách, ale ani generické cosi.

Jenže ty jako středobod své argumentace používáš nějaké napovídání a napovídání je doména IDE, proto IDE by mělo mít podporu pro konkrétní ORM nástroj a potom je jedno jestli problém máš nebo nemáš, protože nebudeš muset psát anotace nad repository classy ;)

To je, řekl bych, jenom Tvůj dojem /nebo přání :)/. Já jako středobod své argumentace používám tvrzení, že Tvé třídy, které závisí na repositářích, mají v konstruktoru pramálo říkající závislosti (Entity Manager, nějaké DAO…). To má mnoho negativních důsledků – zatajování skutečných závislostí, obtížný refactoring, místy nefungující auto-wiring… a v neposlední řadě i to, že Ti IDE neumí kvalitně napovídat (protože prostě ani ono neví, s čím se vlastně pracuje :–P).

V Jave tohle řeší generika, vytvoříš si repository jako generickou třídu a předáním classy entity IDE hned ví co ti má napovídat.

Což to já vím, ale rád bych zůstal pro účely této debaty u PHP ;).


Nevadí mi, že jsem Tě nepřesvědčil. Nemrzí mě ani to, že Ty jsi mě nepřesvědčil. :)

Díky ale za slušnou diskuzi, protože zde zazněla spousta věcných argumentů a já pevně věřím, že už si na věc každý udělá svůj vlastní názor.

enumag
Člen | 2118
+
0
-

Díky ale za slušnou diskuzi, protože zde zazněla spousta věcných argumentů a já pevně věřím, že už si na věc každý udělá svůj vlastní názor.

Na Kdyby/Doctrine se mi to dále neděděné EntityDao nelíbilo přesně ze stejných důvodů jako @Tharosovi. Vaše diskuse byla sice velmi zajímavá, ale ve výsledku jsem v podstatě tam kde jsem byl – nejsem s tím spokojený, ale nevím co s tím. Nekonkrétní EntityDao se mi nelíbí, předávat EM taky ne a dědit s tím že ty třídy budou většinou prázdné rovněž ne. :-) Vlastně jste mne jen utvrdili v tom co jsem si už myslel – dokud PHP nebude mít generika tak to asi ideálně vyřešit nepůjde.