Formulář jako komponenta – onSuccess nefuguje
- cactux
- Člen | 12
Ahoj,
Experimetuji s komponentami a tak jsem si vytvořil komponentu, která má na
starosti formulář.
Chci, abych neměl v presenteru žádnou zbytečnou metodu… Takže jsem si
přepsal metodu createComponent
v BasePresenteru.
protected function createComponent($name) {
$component = parent::createComponent($name);
if ($component === NULL) {
$className = 'App\\Components\\'.$name;
if(class_exists($className))
$component = new $className($this, $name);
else
return NULL;
}
return $component;
}
A chtěl bych v komponentě kompletně obsluhovat model. (Jak do komponenty
dostanu bezpracně model? – Nejhezčí by mi přišlo
@inject
…)
To chci využít v metodě zaregistrované v onSuccess
($form->onSuccess[] = $this->processForm;
). Když odešlu
formulář tak se mi jenom znova vykreslí ten samý formulář – nic se
nestane. Kde je chyba?
Ještě: Formulář vytvářím v render(…)
- Šaman
- Člen | 2666
- Ukázku jednoduché formulářové komponenty máš tady
- Takhle vypadá řešení pomocí automaticky generovaných továrniček. Pokud je nebudeš používat, tak interface (ILogin…) ignoruj a to, co je v traitě (TCreate…) patří do presenteru.
- Do komponent všechno musíš předat přes konstruktor, jestli chceš aby se to předávalo samo pomocí autowiringu. Případně setterem, ale pak musíš v továrničce tento setter nastavit.
- Zax
- Člen | 370
Dokážu si představit, že někdo chce používat @inject v komponentách. Je to úplně stejný problém jak u presenterů (Base třída a závislosti, konstruktor hell) a naštěstí tu je možnost @inject zapnout (aspoň tedy pro komponenty generované továrničkami). Sám to už pár dní používám a zatím si spokojeně chrochtám :-) Akorát to není čisté řešení, porušuje zapouzdřenost, ale pokud to někdo chce, ta možnost tu je ;-)
Editoval Zax (1. 6. 2014 23:04)
- Šaman
- Člen | 2666
Zax napsal(a):
Dokážu si představit, že někdo chce používat @inject v komponentách. Je to úplně stejný problém jak u presenterů (Base třída a závislosti, konstruktor hell) a naštěstí tu je možnost @inject zapnout (aspoň tedy pro komponenty generované továrničkami). Sám to už pár dní používám a zatím si spokojeně chrochtám :-)
Ano, zapnout to jde. Ale dokud nemá potřebu to zapínat, tak bych ho neučil způsob, který není podporovaný frameworkem. Myslím, že nehledá způsob, jak zapnout injectování ala presenter, ale teprve dělá první pokusy jak Nette vlastně funguje.
A propo dopiš prosímtě k tomu řešení varování, že anotace @inject je prasárna, která porušuje základní pravidlo OOP a to zapouzdření. Osobně doporučuji v abstraktních třídách používat inject metody, které jsou v pohodě. Presenter je z několika důvodů trochu zvláštní, takže použití anotace @inject se tam snese, ale jen když člověk ví, že tak trochu prasí. Rozhodně by si programátor neměl myslet, že je to čisté.
- cactux
- Člen | 12
Mě se @inject
tak líbí, že jsem si to zajistil při
přepisování metody v basePresenteru:
protected function createComponent($name) {
$component = parent::createComponent($name);
if ($component === NULL) {
$className = 'App\\Components\\'.$name;
if(class_exists($className)) {
$component = new $className($this, $name);
$injectProps = Nette\DI\Helpers::getInjectProperties(\Nette\Reflection\ClassType::from($component));
foreach ($injectProps as $name => $type) {
$component->$name = $this->context->getByType($type);
}
} else
return NULL;
}
return $component;
}
@Šaman : Ano, není to čisté – jsem si toho vědom. A to mě opravňuje k tomu to používat.
(„První pokusy“ dělám už dva roky…)
- Oli
- Člen | 1215
Pokud má ten model využití, tak ho tam vložit musíš. Znovu použitelné to je, jen deklaruješ: „Aby jsi mě mohl použít, tak mě dej tenhle model“. Např. když máš komponentu, která vypisuje všechny články, ale ty jí nepředáš model/články, tak nemá co vypisovat a je zbytečná. A může být znovupoužitelná jak chce. :-)
Nebo jsi to myslel jinak?
- cactux
- Člen | 12
Představme si klasickou situaci: Máme blog s články. Máme komponentu, která má na starosti vykreslení článku. A teď jde o to jak ten článek tam dostaneme:
- Můžeme ho předat v templatu, tam ho dostat z presenteru a tam z modelu. Dostaneme tedy text (autora, datum vydání atd.) jako paramatr metody render() komponenty. Ten potom pošleme rovnou do templatu komponenty a je to.
- Můžeme komponentě předat id článku a model a komponenta sama si vytáhne text (atd.) z modelu.
Mě se osobně zamlouvá více druhá varianta protože asi nebudeme vykreslovat článek, který není v db a kód tak rozdělíme do více míst (tj. nebude obrovský presenter). ⇒ Bude to přehlednější.
Možná se ptáte na znovupoužitelnost. Když se kouknete na phpFasion.com, tak uvidíte, že články se vykreslují jak za sebou na homepage tak na stránce článku
- Zax
- Člen | 370
U druhé varianty bych se právě bál těch výpisů. Když komponenta dostane IDčko a vytáhne si jeden řádek, co potom udělá cyklus, kde vypisuješ seznam deseti článků? Nejspíš to bude 10 dotazů do databáze…
První varianta tento problém nemá, ale je fakt, že se mi taky víc zamlouvá ta druhá varianta.
Osobně to řeším tím, že seznam a detaily (zobrazení jednoho článku) dělím do dvou komponent. Můžeš mít pro ně společnou sub-template, kterou si naincluduješ do hlavní šablony, aby se ti to zobrazovalo stejně.
- David Matějka
- Moderator | 6445
moznost 1, tedy predani do renderu v sablone, neni idealni – v komponente pak nebudou korektne fungovat snippety.
Ja pouzivam moznost 3 – do komponent temer vzdy predavam konkretni entitu, kterou si vytvorim v presenteru (respektive na urovni, kde vytvarim onu komponentu)
- cactux
- Člen | 12
matej21 napsal(a):
Ja pouzivam moznost 3 – do komponent temer vzdy predavam konkretni entitu, kterou si vytvorim v presenteru (respektive na urovni, kde vytvarim onu komponentu)
V presenteru získáš entitu a potom jí přes šablonu dáš do komponenty? Nebo ji injectuješ přímo z presenteru (a jak?)?
- Oli
- Člen | 1215
komponenta se vytvori ve chvíli, kdy ji poprvé zavoláš. Takže můžeš udělat něco jako
/** @var ArticlesComponent @inject */
private $articlesComponent;
public function createComponentArticles()
{
return $this->articlesComponent;
}
public function actionDetail($id)
{
$article = $this->articleRepository->getById($id);
$this['articles']->setArticle($article);
}
- David Matějka
- Moderator | 6445
priklad:
Komponenta, ktera v konstruktoru prijima entitu:
class SomeArticleControl extends Control
{
public function __construct(Article $article, Dependency1 $dep1, ...)
{
...
}
}
Factory:
interface SomeArticleControlFactory
{
/**
* @param Article $article
* @return SomeArticleControl
*/
public function create(Article $article);
}
Presenter:
class FooArticlePresenter extends BasePresenter
{
/** @var Article */
protected $article;
/** @var SomeArticleControlFactory @inject */
public $someArticleControlFactory;
public function actionDefault($id)
{
if(!$this->article = $this->findArticle($id)) {
$this->error();
}
}
protected function createComponentSomeArticleControl()
{
return $this->someArticleControlFactory->create($this->article);
}
}
Jen musim registrovat factory v neonu (nebo v compiler extension), coz muze byt s parametrama v nekterych verzich nette trosku wtf. Tohle reseni by melo fungovat vzdy:
services:
-
class: SomeArticleControl
implement: SomeArticleControlFactory
parameters: [Article $article]
arguments: [%article%]
autowired: true
od 2.2 neni nutno uvedet autowired: true
, stejne jako jiz neni
nutno uvadet znovu class, kdyz uz je v return anotaci. Pokud je to registrovany
v compiler extension, neni nutno ani uvadet parametry, pouze argumenty.
Typehint parametru v neonu byt musi (pokud je ve factory) a musi to byt FQN.
Proto radeji registruju factory v compiler extension :)
Editoval matej21 (3. 6. 2014 16:55)
- cactux
- Člen | 12
Oli napsal(a):
komponenta se vytvori ve chvíli, kdy ji poprvé zavoláš. Takže můžeš udělat něco jako
/** @var ArticlesComponent @inject */ private $articlesComponent; public function createComponentArticles() { return $this->articlesComponent; } public function actionDetail($id) { $article = $this->articleRepository->getById($id); $this['articles']->setArticle($article); }
Tohle he možná nejhezčí, ale já bych předával jenom id a data tahal až v komponentě… (argumenty dříve).
A nelíbí (=myslím že by se mohlo udělat lépe) se mi ta jednořádková
metoda createComponentArticles
a ta property (Neměla by být
public?)…
Jsou tam potřeba? (I v tom tvém řešení) Nefungovalo by to stejně bez
nich? … (po chvílí) … Vlastně já si přepisuju petodu
createComponent
tak, že se to dělá samo :D (viz dříve)
- Mesiah
- Člen | 240
Spíše jsem měl na mysli situaci komponenta jako formulářový prvek –
pokud bych obsluhu callbacku (a přiznám se, že ve většině svých projektů
to tak skutečně mám) nechal přímo v komponentě – tedy sama komponenta
ví, jak se v aplikaci pracuje s entitami a na můj vkus je tam moc velká
provázanost s konkrétním modelem aplikace, takže pokud bych udělal
ctrl-c/ctrl-v s adresářem komponenty, pak musím upravovat vnitřní logiku,
a naopak, pokud poskytnu komponentu čistě jako komponentu, tedy bez nějakých
vnitřních business procesů a přinutím implementátora napsat vlastní
obsluhu, pak považuji komponentu za nezávislou a dobře znovupoužitelnou.
Ohledně komponenty na vypisování článků, ty u sebe řeším
předáváním entit, ať už jde o kolekci, či jeden prvek – v práci
(ASP.NET MVC) nám jeden kolega (teď už bývalý) dělal modul na rendering
sestav a použil princip předávání ID a komponenta sama si entity dotáhne,
ale ukázalo se to, jako hodně neefektivní řešení (ačkoli se data,
dotahovaná z db cachují) a mě osobně to přijde i jako jít proti
ideologii aplikace, bo když už využíváme MEF a princip dependency
injection, tak jej používejme všude a pokud nějaká data mi ten kdo daný
modul používá neposkytne, tak prostě vyhoď exception (btw, kolega
nevyhazuje exception, ale vrací null – hledat proč se sestava
nevyrenderovala, ale vrátila null je fakt boží).
- Oli
- Člen | 1215
cactux napsal(a):
Tohle he možná nejhezčí, ale já bych předával jenom id a data tahal až v komponentě… (argumenty dříve).
A nelíbí (=myslím že by se mohlo udělat lépe) se mi ta jednořádková metoda
createComponentArticles
a ta property (Neměla by být public?)…
Jsou tam potřeba? (I v tom tvém řešení) Nefungovalo by to stejně bez nich? … (po chvílí) … Vlastně já si přepisuju petoducreateComponent
tak, že se to dělá samo :D (viz dříve)
Máš pravdu v podstatě ve všem. Jen jsem se snažil nastínit řešení. Řešení, které plus/minus používám taky popsal lip (a podrobnějc) @matej
- cactux
- Člen | 12
Oli napsal(a):
Máš pravdu v podstatě ve všem. Jen jsem se snažil nastínit řešení. Řešení, které plus/minus používám taky popsal lip (a podrobnějc) @matej
To mi přijde moc složitý… Pokud bych chtěl mít hodně komponent (jakože chci) tak se utopím v továrničkách (a takové snečištění ovzduší… :D ).
- Filip Procházka
- Moderator | 4668
To cos udělal s tou createComponent
se tady na fórum vrací
pravidelně jako chřipka. Je to anti-pattern. Měl bys každé komponentě
kterou budeš v presenteru potřebovat, napsat extra továrnu.
A navíc ty injecty děláš špatně, správně takto
$object = $this->context->createInstance($class);
$this->context->callInjects($object);
A vůbec, správně to tady má jenom matej21, byť možná zbytečně složitě, entitu bych předával raději setterem (má to víc výhod než jenom jednodušší konfiguraci).
Zax napsal(a):
@inject anotace v komponentách má i své výhody a navíc cactux dal přímo jasně najevo, že má zájem o anotace ;-)
Nemá to žádné výhody a přestaň prosím lidem radit prasárny.
Konstruktor hell v komponentách neexistuje a pokud ho tam máš tak to děláš špatně.
Mesiah napsal(a):
Mimochodem, off topic, tím, že do komponenty vkládáš model přicházíš o znovupoužitelnost, nepletu-li se…? Není to škoda?
Pleteš.
Oli napsal(a):
komponenta se vytvori ve chvíli, kdy ji poprvé zavoláš. Takže můžeš udělat něco jako
/** @var ArticlesComponent @inject */ private $articlesComponent; public function createComponentArticles() { return $this->articlesComponent; } public function actionDetail($id) { $article = $this->articleRepository->getById($id); $this['articles']->setArticle($article); }
Tohle je taky brutální prasárna, komponenty nejsou služby!
cactux napsal(a):
Tohle he možná nejhezčí, ale já bych předával jenom id a data tahal až v komponentě… (argumenty dříve).
A co když dostaneš neexistující IDčko? Jak pošleš 403 ze šablony?
A naučte se prosím citovat, opravil jsem vám ty příspěvky, není nutné kopírovat stejný text 10×.
- Zax
- Člen | 370
Filip Procházka napsal(a):
Zax napsal(a):
@inject anotace v komponentách má i své výhody a navíc cactux dal přímo jasně najevo, že má zájem o anotace ;-)
Nemá to žádné výhody a přestaň prosím lidem radit prasárny.
Chtěl inject, tak proč mu neporadit? Mně se inject líbí, mám to napsané hnedka a funguje to. Jo, když do toho někdo sáhne zvenku a přepíše to, je to v pr., ale úplně to samé platí o presenterech a ty jsou úplně stejně tak součástí Nette jako komponenty (dokonce mají oba stejnou Base class – PresenterComponent). Proč tedy u jednoho je to dokonce doporučovaný způsob injectování závislostí a u druhého prasárna? WTF?
Konstruktor hell v komponentách neexistuje a pokud ho tam máš tak to děláš špatně.
Base control, který chce továrnu na komponentu pro flash messages. Naprosto validní požadavek (2/3 mých komponent potřebujou zobrazovat flashky). Bez traitů. DRY a KISS. Go! ;-)
Editoval Zax (3. 6. 2014 19:22)
- Filip Procházka
- Moderator | 4668
Zax napsal(a):
Chtěl inject, tak proč mu neporadit? Mně se inject líbí, mám to napsané hnedka a funguje to.
Tak si to používej ale neraď to ostatním.
Proč tedy u jednoho je to dokonce doporučovaný způsob injectování závislostí a u druhého prasárna? WTF?
- https://www.youtube.com/watch?…
- https://forum.nette.org/…inych-sluzeb
- https://forum.nette.org/…a-steroidech#…
Base control, který chce továrnu na komponentu pro flash messages. Naprosto validní požadavek. Bez traitů. DRY a KISS. Go! ;-)
abstract class BaseControl extends Nette\Application\UI\Control
{
// https://github.com/Kdyby/Autowired/blob/master/docs/en/index.md
use Kdyby\Autowired\AutowireComponentFactories;
protected function createComponentFlashes(IFlashesControlFactory $factory)
{
return $factory->create();
}
}
class PollControl extends BaseControl
{
private $polls;
public function __construct(PollManager $polls)
{
parent::__construct();
$this->polls = $polls;
}
}
„Bez traitů“ není validní požadavek.
Btw, aby base control měl flashky mi přijde dost divný, všechny tvoje
komponenty mají vždy flashky? Moje rozhodně ne. Navíc zobrazovat flashky na
deseti místech v aplikaci je úchylnost která mate uživatele, daleko lepší
je volat $this->presenter->flashMessage()
a mít je všechny
na jednom místě vykreslené někde v layoutu.
- Oli
- Člen | 1215
Tohle je taky brutální prasárna, komponenty nejsou služby!
Mohl by jsi mě vysvětlit proč je to prasárna? než jsem začal používat autowire komponent, tak jsem to používal v podstatě tak jak uvádím. Osobně nevidím zase až takovej rozdíl mezi tím, kdy si to injectnu do presenteru jako službu a kdy si tam injectnu tovarnu, ktera me tu sluzbu vytvori…
Teda, proc by neměli být komponenty injectovaný jako služby?
- Filip Procházka
- Moderator | 4668
Teda, proc by neměli být komponenty injectovaný jako služby?
V momentě kdy budeš chtít jednu komponentu dvakrát v tom samém presenteru nebo v jiné komponentě v jeden moment, tak ti to nebude fungovat, protože komponenta může být připojena pouze k jednomu rodiči a jenom jednou. Když ji zkusíš připojit dvakrát tak to vyhodí výjimku.
Proto je potřeba vytvářet nové instance komponent pro každé použití. Ideálně pomocí generovaných továrniček.
- Zax
- Člen | 370
@Filip Procházka: Principy DI jsem samozřejmě studoval. Mně stačí ±5 řádků abych si zapnul @inject, o kterém se ví, jak funguje (neboť v presenterech ho používají skoro všichni) a místo toho si mám do projektu přidat závislost na tvé knihovně a „magii“ schovat do traitu, který je právě v té knihovně. Někdo po mně kód převezme a ještě aby se začal učit tvou knihovnu. Navíc v dokumentaci uznáváš, že to není úplně čisté – což není ani inject. Nějak nechápu ten rozdíl… to si fakt radši na pět řádků prostě zapnu inject a je to všechno naprosto jasné a pochopitelné, byť ne zapouzdřené.
Furt nechápu – když ano u presenterů, proč ne u komponent? Nějaký BasePresenter a BaseControl má v projektu prakticky každý. Pro BasePresentery máme workaround, proč bych tedy neměl úplně ten samý workaround použít i u komponent a místo toho bych to měl řešit ještě úplně nějak jinak? To jako jen tak pro jistotu aby aplikace byla co nejmíň srozumitelná kdyby někdo po mně kód převzal? Anotaci @inject porozumí každý, kdo někdy používal Nette, i opoždění lidé. Navíc to chci pro presentery a komponenty a toť vše, nikde jinde to nepoužívám. Je to fakt až tak velký „zločin“? Fakt jedno velké WTF.
EDIT: Ano, potřebuji flasky u komponent, moje komponenty (hlavně právě ty backendové, kde pracuješ s různými seznamy a formuláři) jsou velmi často AJAXové a chci zobrazit hlášku přímo před očima uživatele, ne ji schovávat někam do hlavičky když má uživatel odscrollováno do půlky. A manipulovat mu přímo se scrollbarem by byla maximální úchylnost (IMHO). EDIT2: Jo a v případě že má uživatel vyplý JS, tak ho redirectnu na anchor, to jen tak pro jistotu ;-)
Editoval Zax (3. 6. 2014 19:56)
- Filip Procházka
- Moderator | 4668
Pro BasePresentery máme workaround, proč bych tedy neměl úplně ten samý workaround použít i u komponent a místo toho bych to měl řešit ještě úplně nějak jinak?
Jenže ty se na to díváš špatně a ptáš se špatně. Správná otázka je „a proč je to dovoleno v presenterech?“ né „proč to nemůžu používat i jinde?“. Na to je pak jasná odpověď, protože property injection v PHP je ten nejproblematičtější typ injekce a nejčistější je konstruktor.
Ještě bych i pochopil, kdybys měl jednu inject metodu (nebo klidně i ty properties) v BaseControlu. To je přijatelný workaround pro usnadnění problému.
Stále to ale není argument pro použití @inject
v konkrétních komponentách. Tam bys měl striktně
dodržovat konstruktor injection.
Nebaví mě opakovat pořád to samé dokola, poslal jsem ti 3 odkazy, tam je vše vysvětlené, pokud to nechápeš nebo odmítáš chápat tak tvoje smůla, já tě za každou cenu přemlouvat nebudu. Sám si to dělej jak chceš, ale nenabádej jiné programátory k prasárnám.
EDIT: Ano, potřebuji flasky u komponent, moje komponenty jsou velmi často AJAXové a chci zobrazit hlášku přímo před očima uživatele, ne ji schovávat někam do hlavičky když má uživatel odscrollováno do půlky.
Pokud máš složitější strukturu stránky pak to asi smysl dává. Stejně ale nedává smysl mít to úplně všude.
A víš, že v jeden ajaxový request můžeš invalidovat zároveň víc než jednu komponentu? Není problém invalidovat komponentu a zároveň i flashky v presenteru ;)
- Oli
- Člen | 1215
Filip Procházka napsal(a):
V momentě kdy budeš chtít jednu komponentu dvakrát v tom samém presenteru nebo v jiné komponentě v jeden moment, tak ti to nebude fungovat, protože komponenta může být připojena pouze k jednomu rodiči a jenom jednou. Když ji zkusíš připojit dvakrát tak to vyhodí výjimku.
Proto je potřeba vytvářet nové instance komponent pro každé použití. Ideálně pomocí generovaných továrniček.
Filip Procházka: jasně, tohle je regulérní důvod pro použití generované továrničky. To chápu a beru. Chtěl jsem dokonce i napsat do toho předchozího příspěvku, že tohle omezení beru na zřetel… Často ta moje komponenta jen obalovala formulář abych měl čistej presenter. Takovej formulář jsem věděl, že bude na stránce vždy max 1×.
Za takového předpokladu to pořád považuješ za brutální prasárnu? Je tam ještě nějakej zádrhel, kterej nevidím?
Editoval Oli (3. 6. 2014 20:08)
- Filip Procházka
- Moderator | 4668
Oli napsal(a):
Často ta moje komponenta jen obalovala formulář abych měl čistej presenter. Takovej formulář jsem věděl, že bude na stránce vždy max 1×. Za takového předpokladu to pořád považuješ za brutální prasárnu? Je tam ještě nějakej zádrhel, kterej nevidím?
Komponenta prostě není služba. Zatím se mi nikdy nevyplatilo napsat méně kódu za cenu ústupku z čistoty. Vždy jsem se na to buď já nebo kolega nachytal protože se to chovalo nestandardně.
Nejlepší co můžeš udělat je, říct si nějaký standard jaký budete v projektu dodržovat a ten mít prostě v celém projektu. Dělat v tomhle případu věci tak a v tomhle jinak, když je to možnost dělat jedním způsobem (bez nějakých návrhových ústupků), je vždycky horší.
- Oli
- Člen | 1215
Filip Procházka: Dobře, díky. Beru to teda tak, že to není úplně čistý, ale když se to tak vezme kolem a kolem, tak to vlastně není ni proti ničemu. Jestliže víš o omezení který z toho plynou.
Nicméně právě díky autowire komponent už pomalu všechny komponenty přepisuju, tak aby byly generovaný továrničkou. Strašně návyková věc. Měl by jsi napsat upozornění ;-)
- Zax
- Člen | 370
Filip Procházka napsal(a):
Jenže ty se na to díváš špatně a ptáš se špatně. Správná otázka je „a proč je to dovoleno v presenterech?“ né „proč to nemůžu používat i jinde?“. Na to je pak jasná odpověď, protože property injection v PHP je ten nejproblematičtější typ injekce a nejčistější je konstruktor.
Ještě bych i pochopil, kdybys měl jednu inject metodu (nebo klidně i ty properties) v BaseControlu. To je přijatelný workaround pro usnadnění problému.
Stále to ale není argument pro použití
@inject
v konkrétních komponentách. Tam bys měl striktně dodržovat konstruktor injection.
No hurá, už se někam dostáváme! Ono to v konkrétních komponentách skutečně není třeba, ale je právě problém udržet konstruktor v BaseControl čistý a prostě lepší řešení než inject metody či anotace neznám (pokud si nechci přidávat další závislosti do projektu kvůli něčemu tak triviálnímu). A já preferuji anotace čistě kvůli konzistenci – v BasePresenteru @inject, v BaseControlu metoda inject* by mi prostě přišlo takový divný. Každopádně se bez nějakých kompromisů neobejdu. Workaroundy jsou naprosto zjevně nutné, proto nechápu proč na mě tak útočíš.
BTW traity nemají konstruktor, že? Co když chci na flashky místo BaseControl použít Trait? Jak do traitu dostanu závislost? Dost blbě, co? Potřebuji nějakou magii, která mi to tam dostane. Opět se přímo nabízí (@)inject(*).
Pokud máš složitější strukturu stránky pak to asi smysl dává. Stejně ale nedává smysl mít to úplně všude.
A víš že v jeden ajaxový request můžeš invalidovat zároveň víc než jednu komponentu? Není problém invalidovat komponentu a zároveň i flashky v presenteru ;)
Jednoduché stránky se časem mohou přetvořit ve složité. Radši to udělám už od základu jako pro složitou strukturu. Komponenty mám rád a hodlám je používat vždy a všude. Ajax v Nette funguje velmi pěkně, proto ho hodlám používat všude, kde se to hodí.
A ano, vím že se dá invalidovat víc komponent. Mám i některé komponenty, které používají $presenter->flashMessage(), ale třeba pro Ajax inline editaci někde uprostřed stránky to je nevhodné, spíš je lepší zobrazit flashku přesně tam, kde editace proběhla a nevynucovat scrollování na začátek stránky jenom kvůli tomu, abych uživateli oznámil, že právě provedl úpravu položky. Nebo když Ajaxově smaže nějakou položku, tak mu zobrazím flashku přímo na místě, kde ta položka byla (třeba rovnou i s tlačítkem „vzít zpět“) A jak říkám, podobných komponent mám celkem dost. Líbí se mi, že to tak může bez problémů fungovat, připadá mi to user-friendly, Ajax je sexy…
Editoval Zax (3. 6. 2014 20:29)
- Filip Procházka
- Moderator | 4668
Zax napsal(a):
Ono to v konkrétních komponentách skutečně není třeba, ale je právě problém udržet konstruktor v BaseControl čistý .
Jenže ono to celou dobu vypadá že se bavíš obecně o komponentách :) Pokud to máš jenom a pouze v té BaseControl a v potomcích používáš konstruktor injection tak je to stejný případ jako u BasePresenteru a tedy by to nemělo ničemu vadit.
a prostě lepší řešení než inject metody či anotace neznám (pokud si nechci přidávat další závislosti do projektu kvůli něčemu tak triviálnímu)
Autowired ten problém řeší jenom částěčně, usnadňuje ti psaní továrniček na komponenty v presenterech a komponentách. V momentě kdy potřebuješ službu tak už ti nepomůže. Protože jsi chtěl příklad s komponentou, logicky jsem použil rozšíření které celou situaci usnadňuje.
Mimochodem, když po tobě někdo přebere projekt a bude v něm hromada obskurdit, tak se mu bude učit hůře, než když v něm budou použity knihovny které zná :) Bránit se závislostem na cizích knihovnách je tedy dost kontraproduktivní, nehledě na to že psát si všechno sám je pro klienta a projekt dražší než když použiješ hotové řešení. Tady pozor! Nesnažím se ti vnutit své rozšíření, ale pouze obhajuji použití knihoven v projektu.
A já preferuji anotace čistě kvůli konzistenci – v BasePresenteru @inject, v BaseControlu metoda inject* by mi prostě přišlo takový divný.
to už je detail
Workaroundy jsou naprosto zjevně nutné, proto nechápu proč na mě tak útočíš.
Neber to prosím jako útok, ale jako normální diskuzi. Snažím se jenom předcházet tomu, aby se začátečníci kteří si tohle vlákno přečtou, učili cokoliv jiného než aktuální best-practices. Je strašně těžké takové lidi pak přeučit a ještě horší po nich přebírat projekty :)
- Zax
- Člen | 370
@Filip Procházka: No popravdě to byla moje chyba, nespecifikoval jsem to a s hanbou přiznávám, že píšu @inject i v konkrétních komponentách, ale činím tak jen ve svých projektech – šetří mi to čas a když není zapouzdřený BaseControl, tak je automaticky porušená zapouzdřenost i v potomcích, takže je to ve výsledku skoro jedno a můžu si klidně ušetřit psaní konstruktoru.
V těch, u kterých hrozí, že kód někdo převezme, používám konstruktor i v base komponentách (právě protože to chci psát co nejčistěji) a je děsnej opruz s tím pracovat – v polovině případů zapomenu zavolat parent::__construct a místo toho prostě znovu nadefinuju stejné properties a vložím je úplně stejně, jako kdyby v BaseControl vůbec nebyly. Což samozřejmě taky sucks a je v tom bordel. Proto si myslím, že prasárna je tak nějak na místě, pokud to vyřeší jinou prasárnu, a jsem rád, že mi dáváš tak nějak za pravdu v případě BaseControl ;-)
Nicméně zrovna tohle (@inject v komponentách) by mě osobně v převzatém projektu trápilo asi ze všeho nejméně, protože bych hnedka pochopil, která bije. A obecně bych ani do public properties nic nezkoušel zapisovat. Každá nastavitelná vlastnost by vždy měla mít setter (nejlépe vracející $this pro fluent cukřík), nezávisle na viditelnosti. Pokud vlastnost nemá setter, automaticky ji považuji za read-only.
No jo, asi si do podpisu přidám upozornění ať mě nikdo nebere vážně :-D Preferuji totiž účelnost, produktivitu a jednoduchost před akademickou čistotou.
Omlouvám se, jestli jsem se nějak dotkl tvé filozofie.
- Filip Procházka
- Moderator | 4668
No jo, asi si do podpisu přidám upozornění ať mě nikdo nebere vážně :-D Preferuji totiž účelnost, produktivitu a jednoduchost před akademickou čistotou.
To si asi nerozumíme :) Já taky budu mít raději hotovou aplikaci, než abych měsíc maturoval nad čistotou zápisu několika tříd, všiml sis že jsem napsal a používám kdyby/autowired? :) Když vím jak věc napsat čistě, tak ji napíšu čistě, když to nevím tak ji raději naprototypuju jako bastlík a pak to někdy třeba přepíšu než abych to neměl vůbec.
Omlouvám se, jestli jsem se nějak dotkl tvé filozofie.
Nejde o mou filozofii, ale o filozofii frameworku. Nette má určité best-practices, které se snaží své uživatele učit. Jelikož se považuju za evangelizátora Nette, tak je prostě učím i já (mimo jiné taky proto, že jim taky věřím).
- Šaman
- Člen | 2666
Filip Procházka napsal(a):
Ještě bych i pochopil, kdybys měl jednu inject metodu (nebo klidně i ty properties) v BaseControlu. To je přijatelný workaround pro usnadnění problému.
…
Nejde o mou filozofii, ale o filozofii frameworku. Nette má určité best-practices, které se snaží své uživatele učit. Jelikož se považuju za evangelizátora Nette, tak je prostě učím i já (mimo jiné taky proto, že jim taky věřím).
Tohle téma se tu poslední dobou opakuje (a tentokrát jsem to nebyl já,
kdo ho nadnesl:), takže ještě jednou a snad naposledy apeluji na možnost
nějak Nette-way zapnou inject metody i pro komponenty. V tuhle chvíli inject
metoda v BaseControl vyžaduje, aby se zapsal přepínač
autowired: yes
ke každému potomkovi. A jak je vidět, mnoho
lidí to obchází. Kdo nechce, ten to používat nebude, ostatní to aspoň
nebudou muset hackovat.
Naopak anotaci @inject bych klidně zrušil úplně – kdo o ni má
zájem, může použit Kdyby. Z důvodu zpětné kompatibility se asi
nezruší, ale rozhodně bych ji ani v presenterech neuváděl jako
best-practise (v příkladech a v sandboxu), ale spíš jako syntactic-sugar.
Nováčci si jinak zvyknou na nevhodný způsob (který pak vyžadují všude) a
kritikům to jen dodává munici.
Jo píšu to sem proto, že na poslední sobotu se nedostanu a jak je
i z tohoto vlákna vidět, neškodilo by to ještě jednou probrat v nějaké
širší komunitě.
Mě osobně by se to nejvíce líbilo takto:
– | Konstruktor | Inject metoda | Anotace @inject |
Abstraktní presenter | Ano | Doporučeno | Nedoporučeno (porušuje zapouzdření) |
Konkrétní presenter | Doporučeno | Ano | Nedoporučeno |
Abstraktní komponenta | Ano | Doporučeno(?) | Ne |
Konkrétní komponenta | Doporučeno | Ano | Ne |
Třída modelu | Doporučeno | Ne (ano jen v případě autowire: yes) | Ne |
- Zax
- Člen | 370
Šaman napsal(a):
Tohle téma se tu poslední dobou opakuje (a tentokrát jsem to nebyl já, kdo ho nadnesl:)
Však je to jedině dobře, mě osobně třeba tohle téma hodně zajímá, protože hodně využívám abstraktní komponenty a potřebuji do nich dostávat závislosti. Celkově mi přijde, že komponenty jsou hodně opomíjené a takové jakoby „chudé“ oproti presenterům ve spoustě věcí. A skoro mám pocit, že tenhle problém tu řeším akorát já a pár dalších jedinců – to skoro nikdo komponenty nepoužívá? :-(
takže ještě jednou a snad naposledy apeluji na možnost nějak Nette-way zapnou inject metody i pro komponenty. V tuhle chvíli inject metoda v BaseControl vyžaduje, aby se zapsal přepínač
autowired: yes
ke každému potomkovi. A jak je vidět, mnoho lidí to obchází. Kdo nechce, ten to používat nebude, ostatní to aspoň nebudou muset hackovat.
+1 pro Nette-way jak toto řešit. Beru cokoliv. Tedy cokoliv, kromě
nutnosti používat přepínače v configu (zkrácený zápis
„- Namespace\IFactory
“ je prostě boží).
Naopak anotaci @inject bych klidně zrušil úplně – kdo o ni má zájem, může použit Kdyby. Z důvodu zpětné kompatibility se asi nezruší, ale rozhodně bych ji ani v presenterech neuváděl jako best-practise (v příkladech a v sandboxu), ale spíš jako syntactic-sugar. Nováčci si jinak zvyknou na nevhodný způsob (který pak vyžadují všude) a kritikům to jen dodává munici.
Jo píšu to sem proto, že na poslední sobotu se nedostanu a jak je i z tohoto vlákna vidět, neškodilo by to ještě jednou probrat v nějaké širší komunitě.Mě osobně by se to nejvíce líbilo takto:
– Konstruktor Inject metoda Anotace @inject Abstraktní presenter Ano Doporučeno Nedoporučeno (porušuje zapouzdření) Konkrétní presenter Doporučeno Ano Nedoporučeno Abstraktní komponenta Ano Doporučeno(?) Ne Konkrétní komponenta Doporučeno Ano Ne Třída modelu Doporučeno Ne (ano jen v případě autowire: yes) Ne
Takto bych to klidně taky bral. Má to opodstatnění a určitou logiku. Naopak mě dost žralo, že @inject je u presenterů doporučený způsob a u komponent prasárna – je to prostě rozpor jak prase. Jediný rozdíl mezi nimi je, že presenter může obsahovat komponenty, ale naopak to nejde, což IMHO by obzvlášť mělo znamenat, že @inject u presenterů je fuj a u komponent trochu míň fuj – když přepíšu službu v presenteru, jde do háje celý presenter se všemi komponentami, které obsahuje. Když přepíšu službu v komponentě, spadne mi jenom ta komponenta (a vše co se vykresluje po ní).
Nicméně bych @inject nerušil úplně, naopak bych to povolil i u komponent, je to jak říkáš syntax sugar a hodí se to, když je potřeba něco rychle udělat a nezáleží tolik na čistotě. Nebo třeba něco vyvíjíš a nemáš ještě úplně jasné všechny závislosti, tak použiješ nejdřív anotace, protože jejich nesporná výhoda oproti ostatním řešením je ta, že se velice dobře udržují (přidání nebo odebrání závislosti změní dva řádky na stejném místě, nikoliv 4 řádky na 3 různých místech), a když máš komponentu hotovou, tak to během chvilky přepíšeš do inject metody (nebo do konstruktoru). Třeba by jako kompromis mohlo jít povolit je jenom v development režimu?
A předpokládám, že best practice pro inject metody je, aby vyhazovaly výjimku, pokud už služby existují, je to tak správně? Nebo je to overkill?
Editoval Zax (4. 6. 2014 2:53)
- Filip Procházka
- Moderator | 4668
Zax napsal(a):
A skoro mám pocit, že tenhle problém tu řeším akorát já a pár dalších jedinců – to skoro nikdo komponenty nepoužívá?
Používám je úplně na všechno a zatím jsem s tímhle problém nikdy neměl :)
Nebo třeba něco vyvíjíš a nemáš ještě úplně jasné všechny závislosti, tak použiješ nejdřív anotace
Ty nepoužíváš IDE? Přidat a odebrat závislost je otázka několika stisknutí kláves a s nativním autowire v nette/di je změna závislostí úplná lahoda, protože ani nemusíš sahat na config.
protože jejich nesporná výhoda oproti ostatním řešením je ta, že se velice dobře udržují (přidání nebo odebrání závislosti změní dva řádky na stejném místě, nikoliv 4 řádky na 3 různých místech)
Tohle tvrzení je ekvivalentní tomu, jako bys řekl, že statický přístup je lepší, protože je s tím méně psaní.
Šaman napsal(a):
Tohle téma se tu poslední dobou opakuje (a tentokrát jsem to nebyl já, kdo ho nadnesl:), takže ještě jednou a snad naposledy apeluji na možnost nějak Nette-way zapnou inject metody i pro komponenty.
V momentě kdy tohle bude zaplé by default, tak to lidé prostě budou používat na všechno. Když se bez toho nezvládneš obejít, tak napsat compiler extension, který zapne injecty pro všechny komponenty, je práce na 5 minut a kód na 10 řádků.
- Zax
- Člen | 370
Filip Procházka napsal(a):
Zax napsal(a):
A skoro mám pocit, že tenhle problém tu řeším akorát já a pár dalších jedinců – to skoro nikdo komponenty nepoužívá?
Používám je úplně na všechno a zatím jsem s tímhle problém nikdy neměl :)
Nemám páru jak to děláš. Taky používám komponenty na všechno a krom toho, že mi v základu chybí pár fičur (views, metoda forward), mám i potřebu používat Base komponenty i na různé další věci. Zatím to u mě vypadá zhruba takto:
- BaseControl: „views“ (pokud se zavolá signál, hledá
se šablona se stejným názvem, pokud neexistuje nebo se signál nevolá,
hledá se default.latte), metoda forward, metoda redirectOrForward (abych
nemusel dokola psát if($this->isAjax())…), metoda enableAjax (v šabloně
na to musím myslet, ale nakonec je fajn mít někde po ruce možnost ajax
zapnout/vypnout), obsahuje subkomponentu na flash messages, syntax sugar pro
kontrolu oprávnění (anotace
@resource Zdroj
pro celou komponentu + metoda $this->isAllowed([$resource,]$privilege) + magické $this->canSomething, což se přeloží jako $this->isAllowed(‚Something‘) + secured signálům stačí anotace @privilege)- BaseCollectionControl: abstraktní metoda na získání
kolekce a přetížitelná metoda na filtrování dat (v základu vrací celou
kolekci a nic nefiltruje)
- BasePaginableCollectionControl: subkomponenta paginator +
přetížená metoda pro filtrování dat ⇒ paginator díky tomu funguje
v podstatě automaticky
- BaseEditableCollectionControl: automaticky přijímá
signály add, edit a delete, kontroluje oprávnění, obsahuje některé
základní abstraktní metody pro práci s entitami, v základu bych chtěl,
aby to umělo i vygenerovat přidávací/editační formulář z definice
entity, ale k tomu jsem se ještě nedostal – zatím tedy řeším
formuláře přes abstraktní metody a musím si je u konkrétních
komponent dopsat
- BaseEditableCollectionFilesControl: obsahuje subkomponentu na správu souborů, abstraktní metody pro ukládání vazeb mezi entitou a soubory
- BaseEditableCollectionControl: automaticky přijímá
signály add, edit a delete, kontroluje oprávnění, obsahuje některé
základní abstraktní metody pro práci s entitami, v základu bych chtěl,
aby to umělo i vygenerovat přidávací/editační formulář z definice
entity, ale k tomu jsem se ještě nedostal – zatím tedy řeším
formuláře přes abstraktní metody a musím si je u konkrétních
komponent dopsat
- BasePaginableCollectionControl: subkomponenta paginator +
přetížená metoda pro filtrování dat ⇒ paginator díky tomu funguje
v podstatě automaticky
- BaseCollectionControl: abstraktní metoda na získání
kolekce a přetížitelná metoda na filtrování dat (v základu vrací celou
kolekci a nic nefiltruje)
a mohl bych pokračovat…
Závislosti jsou jasné:
- BaseControl závisí na IFlashMessagesFactory
- BasePaginableCollectionControl závisí na IPaginatorFactory
- BaseEditableCollectionFilesControl závisí na IFileManagerFactory
Vztahy taky dávají smysl:
- naprostá většina mých komponent chce kontrolovat oprávnění a zobrazovat vlastní flashMessages
- ne každá kolekce vyžaduje stránkovadlo
- naopak komponenta pro kolekce s možností úprav docela určitě bude vyžadovat i stránkovadlo
- k čemu je upload souborů u kolekcí (třeba článků) je snad dost self-explanatory
Všechny tyto komponenty mají svůj validní use-case:
- chci zobrazit jeden článek: použiju BaseControl, vytáhnu článek, kouknu jestli je public, pokud není, zkontroluju oprávnění (třeba pomocí $this->canEdit).
- chci na hlavní straně zobrazit pět nejnovějších témat na fóru: použiju BaseCollectionControl a napíšu metodu na získávání dat z modelu. Limit nastavím v přetížené metodě pro filtrování (někdy se hodí třeba zjistit počet všech položek před aplikováním filtru, proto to mám takto oddělené). Pokud bych chtěl víc položek se stránkováním, použiju BasePaginableCollectionControl
- chci zobrazit administrační rozhraní pro správu uživatelů: použiju BaseEditableCollectionControl a dopíšu nezbytné abstraktní metody.
- chci zobrazit seznam článků, každý článek může mít k sobě připojené libovolné množství souborů, a administrátorovi chci dát možnost spravovat vše přímo na stránce: použiju BaseEditableCollectionFilesControl, dopíšu pár abstraktních metod a nastavím hlavní adresář a povolené typy souborů.
Celé je to hodně založené na DRY a celkem dobře mi to funguje (tedy pokud nejsem nucen všude používat konstruktor, pak jsem naopak docela v pr.), řeším tím spoustu častých problémů. Pokud to dělám komplet špatně, rád se přiučím.
>
Nebo třeba něco vyvíjíš a nemáš ještě úplně jasné všechny závislosti, tak použiješ nejdřív anotace
Ty nepoužíváš IDE? Přidat a odebrat závislost je otázka několika stisknutí kláves a s nativním autowire v nette/di je změna závislostí úplná lahoda, protože ani nemusíš sahat na config.
Že většina IDE umí vygenerovat settery je samozřejmé. Ukaž mi ale IDE, které vezme inject metodu, rozparsuje ji na jednotlivé kousíčky (závislosti) a dá ti možnost stisknutím několika kláves odebrat nějakou závislost. V NetBeans jsem tuto funkci nenašel :-P Vlastnost s anotací smáznu hádej za jak dlouho.
protože jejich nesporná výhoda oproti ostatním řešením je ta, že se velice dobře udržují (přidání nebo odebrání závislosti změní dva řádky na stejném místě, nikoliv 4 řádky na 3 různých místech)
Tohle tvrzení je ekvivalentní tomu, jako bys řekl, že statický přístup je lepší, protože je s tím méně psaní.
Prdlajs. Smazat dva řádky (property a anotace) na jednom místě zabere cca dvě vteřiny i s hledáním, kde přesně mám mazat. Smazat tyto dva stejné řádky a k tomu ještě další dva řádky někde v inject metodě (jeden argument a jedno přiřazení) prostě trvá dýl, říkej si co chceš. Srovnání se statickým přístupem je úplně totálně mimo – statickou třídu do normální třídy budu přepisovat dost blbě, bude to trvat, hodně se přitom sblížím s Tracy a neusnadní mi to vůbec nic, oproti tomu transformace @inject ⇒ metoda inject zabere skutečně jen okamžik a na chodu aplikace se nezmění absolutně nic. A tou anotací si aspoň usnadním bouřlivý vývoj, kdy zběsile přidávám a odebírám závislosti a fakt nechci zrovna řešit, jestli je to čisté, to se dá snadno vyřešit ve chvíli, kdy mám jasně dané všechny závislosti.
Nicméně musím diskutujícím poděkovat (hlavně Filipu Procházkovi), jsou to celkem rady nad zlato!
Editoval Zax (4. 6. 2014 9:16)
- Filip Procházka
- Moderator | 4668
Myslím že to neděláš úplně špatně, máš to celkem pěkně vymyšlené, ale nevím jestli je dobrý takhle hrotit tu dědičnost. Věřím, že by to šlo navrhnout i pomocí kompozice takovým způsobem, aby tvůj BaseControl měl jenom nějaké základní vylepšeníčka a ty sis v potomcích vždy poskládal do sebe co potřebuješ.
Takhle teď
vypadá můj BaseControl, je to imho slušný kompromis mezi pragmatismem a
čistotou, protože když budu chtít tak si nakonfiguruju injecty, když nebudu
chtít, tak si translator najde sama. Ovšem uznávám, že kdyby byly povolené
@inject
na komponentách, tak bych nejspíš ten translator měl
řešený přes ně.
Na druhou stranu si nemyslím, že je v BaseControlu potřeba přidávat
cokoliv dalšího, i to co tam mám teď by šlo pravděpodobně dost ořezat a
věci typu onAttached
by klidně mohly být přímo v Nette. Od
2.2 půjdou vyhodit i ty helpery (tohle je psané pro 2.1) a zbude mi tak
akorát traita na AutowireComponentFactories
a translator.
Mám rozdělanou dost našlapanou administraci pro Doctrine, která je celá založená na komponentách, tak uvidíme jak se s tím zvládnu poprat a pak si to můžeme třeba poměřit znova, až to bude aktuální :)
Prdlajs. Smazat dva řádky (property a anotace) na jednom místě zabere cca dvě vteřiny i s hledáním, kde přesně mám mazat. Smazat tyto dva stejné řádky a k tomu ještě další dva řádky někde v inject metodě (jeden argument a jedno přiřazení) prostě trvá dýl, říkej si co chceš.
Já se ale bavím o konstruktoru (ikdyž to jde aplikovat i na inject metody). Jistěže to trvá déle, ale můj argument platí :)
A teď mi řekni jestli to ty i všichni tví kolegové takto poctivě děláte. To je stejné jako když něco naprogramuješ a řekneš to šéfovi, ten po tobě chce ať to okamžitě deployneš a ty si řekneš však co, deploynu to a testy napíšu potom. Nevěřím, že vám to v praxi dlouhodobě funguje. Pokud to neuděláš hned dobře, tak prostě není motivace se pak k tomu vracet, navíc když je spousta dalších věcí co je potřeba udělat.
Mimochodem, vsadil bych se, že hádáním se mnou jsi ztratil mnohem více
času, než kdy ušetříš tím, že bys místo @inject
metod psal
rovnou konstruktor injection :)
Každopádně mi přijde, že se začínáme dost odchylovat od tématu.
- Filip Procházka
- Moderator | 4668
Casper napsal(a):
Můžeš prosím upřesnit ty výhody setteru?
Nette když se například kanonizuje, tak musí nejprve posbírat parametry z presenteru (a komponent), tedy jsem schopnej poskládat url, která například předává parametr tvé komponentě a tedy ji tím zinicializuji a když máš blbě ošetřenej životní cyklus v presenteru, tak místo 404 (protože například chci nějaký článek který neexistuje) ti vygeneruju 500.
Je tedy podle mě lepší používat setter a v vždy se v komponentě
spoléhat na to, že ta entita tam nemusí být. Sice můžeš dát i
= NULL
do konstruktoru, aby neumřel když tam nepředáš objekt,
ale tady dává setter lepší smysl, protože settery jsou na závislosti bez
kterých objekt může existovat.
Nehledě na to, že konfigurace se smrskne z tohohle
services:
-
class: SomeArticleControl
implement: ISomeArticleControlFactory
parameters: [Article $article]
arguments: [%article%]
na tohle
services:
- ISomeArticleControlFactory
(Už aby se dořešilo tohle https://github.com/nette/di/pull/4 :)
Použití se pak změní z
protected function createComponentArticle(ISomeArticleControlFactory $factory)
{
return $factory->create($this->article);
}
na
protected function createComponentArticle(ISomeArticleControlFactory $factory)
{
return $factory->create()->setArticle($this->article);
}
Což IMHO není taková hrůza :)
- Šaman
- Člen | 2666
Souhlasím se Zaxem v jedné klíčové věci – Nette framework je aktuálně nekonzistentní, protože přístup, který je u jednoho typu tříd doporučován(!) je jinde zavržen, že není čistý. Nalejme si čistého vína – není to čisté ani v presenterech, jediný důvod existence anotace @inject je lenost. Kdyby programátor nebyl líný, tak všude bude používat inject metody, které mají úplně přesně stejný use case jako property injection, ale neporušuji zapouzdření.
Tak proč, @#$$%!, se tento přístup jinde zakazuje? Argument, že anotace @inject nejsou potřeba je sice pravda, ale zároveň je to argumentační faul. Ony totiž nejsou potřeba ani u presenterů, přesto tam jsou.
A proč, @#$$%! *&^$%#, u ostatních tříd nejdou používat ani čistější inject metody? Kde je v nějaké učebnici OOP/MVC napsané, že jediné místo, kde hrozí construction hell jsou presentery? Jediný argument proti inject metodám je ten, že neznalý programátor a ignoranti to budoupoužívat i tam, kde to není potřeba. Nezlobte se na mě, jsem přesvědčený, že framework k produkčnímu nasazení má dávat nástroje k efektivnímu vývoji a nebýt školním systémem, který bude hlídat, jestli nedělám činnost, která by mohla(!) být prasením.
Po Zaxově argumentech (hlavně rychlejší vývoj) bych tu tabulku upravil – myslím, že všechny tři způsoby by měly být povolené vždy. Doporučený způsob všech povinných závislostí konkrétních tříd konstruktor, jen v abstraktních třídách by to byl konstruktor i inject metody. Anotace @inject by měla být všude nedoporučená a neměla by se vyskytovat v oficiálních ukázkách, quickstartu a sandboxu. Umožňuje ale velmi efektivní návrh kostry aplikace, jen by po ni měl přijít refaktoring a uhlazení. Koneckonců u tříd, které mi řeší nějaký algoritmus, taky na prvotní návrh používám public property a až po odladění to přepisuji na settery a gettery.
- Filip Procházka
- Moderator | 4668
Vždyť to tak je, všude se doporučuje používat konstruktor injection,
ale v presenterech si to můžeš ulehčit pomocí @inject
nebo
inject*()
. I v quickstartu je dokonce v presenterech konstruktor
injection :)
Nette ti v ničem nebrání, jenom tě v některých věcech zkrátka nebude by default podporovat.
Nemá smysl tady tady dál spamovat vyřešené téma, založ RFC a můžeme se pobavit nad ním.