Předávání otagovaných služeb jiným službám – „agregátorům“
- Filip Procházka
- Moderator | 4668
Když už máme ty tagy, občas je potřeba otagované služby někam předat. Dejme tomu, že mám otagované komponenty, nebo filtry pro Assetic, to je jedno. Každopádně, teď mám dvě možnosti, jak naložit s hromádkou služeb, které mají nějaké tagy.
Jak to funguje teď?
1) Compile-time předání natvrdo
Vytvořím si vlastní CompilerExtension a v něm si všechny služby najdu
$components = $buider->findByTag('component'); // callbacky na továrničky (shared = false)
$filters = $buider->findByTag('filter'); // služby (shared = true)
a předám si je kam potřebuji. Čisté, jednoduché, funkční. Ale není to lazy. Co si budem povídat, předat někam 20 objektů, které můžou být klidně i pořádné molochy a pak z toho použiju jenom třeba 2, je zbytečné.
2) Run-time lazy accessor
Příklad implementace. Je to podle mě relativně čisté, udělal jsem malý ústupek v jedné třídě, aby zbytek aplikace neviděl na Container a díky tomu mám všechny potřebné služby lazy.
Jak to vylepšit?
První krok je odložit Container někam, kde na něj bude ještě méně
vidět. Třeba do samostatné
třídy, která
mi bude umět podle tagu vrátit služby.
services:
componentsFactory:
class: Kdyby\ComponentsFactory
arguments: [Kdyby\Config\TaggedServices(component)]
Kdyby\Config\TaggedServices
mi přijímá v druhém argumentu
Container
, ale to mě nemusí zajímat, protože se tam doplní
díky autowiringu (cool magie!). Ve službě componentsFactory
mám
teď přístupné všechny továrničky, které potřebuji. Nemusím sahat na
Container a jejich vytváření je maximálně líné.
Jak to ještě vylepšit?
Co takhle, kdyby stačilo zapsat
services:
componentsFactory:
class: ComponentsFactory
arguments: [@#component]
Otázky
- Myslíte si, že by podobná třída jako je moje
TaggedServices
, měla v Nette místo? Jestli ne, tak proč ne? Výhody jsou zřejmé. - Myslíte si, že by dávala smysl úprava neon syntaxe?
- mřížka je průser, protože je to prostě komentář a blbě se to rozlišuje, napadá vás lepší znak?
- Jan Tvrdík
- Nette guru | 2595
Problém s mřížkou řeší uvozovky ["@#component"]
.
Vzhledem k tomu, že zatím nikde nepoužívám tagy, tag to ale
nepotřebuji.
- Filip Procházka
- Moderator | 4668
Pak to ale ztrácí speciální význam pro Neon. Bez uvozovek by se to chovalo jako NeonEntity, což se pak lépe zpracovává než string a Nette už to používá (anonmyní služby).
Editoval HosipLan (4. 4. 2012 13:22)
- David Grudl
- Nette Core | 8218
@#component
by mělo fungovat, obarvovač kecá.
Asi by to chtělo use-case, protože moc nechápu, o co ti jde. Nerozumím ani „odložení Container někam, kde na něj bude ještě méně vidět.“
- Filip Procházka
- Moderator | 4668
odložení Container někam, kde na něj bude ještě méně vidět.
Chci tím říct, že ten Container je tam prostě potřeba, jinak to z principu nemůže být lazy. Ale nechci aby s Containerem pracoval můj kód, když to může být schované za třídou frameworku (jako je například NestedAccessor)
Tak tedy rozvedu příklady z prvního příspěvku.
Assetic
Zaregistruju si služby
services:
lessFilter:
class: Assetic\Filter\StylusFilter
tags: {filter: less}
coffeeFilter: Assetic\Filter\CoffeeScriptFilter
tags: {filter: coffee}
Teď ty služby potřebuju předat do FilterManager
u a pokud
možno co nejvíc jednoduše.
services:
filterManager: Kdyby\FilterManager(TaggedServices(filter))
TaggedServices
dostane jméno tagu a Container
,
vytáhne si jména služeb a když jsou potřeba, tak je předává. Do třídy
se mi dostane accessor pro všechny služby, které mají tag „filter“
class FilterManager
{
// předal jsem filtry, ale žádný se nevytvořil
function __construct($filters) { $this->filters = $filters; }
}
Pokud teď posílám requesty na ty jednotlivé soubory, tak se mi budou buildovat podle toho, který filtr bude potřeba. Na Less se mi použije jeden na Coffee druhý.
Komponenty
Tohle nemám ještě úplně domyšlené, ale chci mít jeden kontejner, který bude vědět jaké služby jsou továrničky na komponenty a tento se předá do presenteru. Chtěl bych tedy něco jako
services:
componentsFactory:
class Kdyby\ComponentsFactory(TaggedServices(component))
myPresenter:
class: Kdyby\MyPresenter(@componentsFactory)
TaggedServices
dostane jméno tagu a Container
,
vytáhne si jména služeb a když jsou potřeba, tak je předává.
A v presenteru
class MyPresenter
{
function __construct($factory) { $this->factory = $factory; }
function actionDefault($page)
{
$components = $this->model->findComponentsForPage($page);
foreach ($components as $name => $component) {
$this[$name] = $this->factory->create($component);
}
}
}
Chci mít zkrátka službu, která bude mít přístup k podmnožině služeb containeru, ale bez toho, abych musel předávat container do svých tříd.
A myslím si, že by se k tomu daly využít tagy. Implementace na ukázku
Editoval HosipLan (4. 4. 2012 14:46)
- awsickness
- Člen | 98
jako asi to je dobre jesete by se to dalo trosku upravit ten zapis na
services:
componentsFactory:
class Kdyby\ComponentsFactory(TaggedServices(component))
myPresenter:
class: Kdyby\MyPresenter(@#tags)
kde by se o naloudovani postarala ta sluzba sama sice nevim ted jestli by to slo nejak rozpoznat ze je to tag ale takhe by to bylo jeste lepsi.
a pak by se jen delalo
function actionDefault($page)
{
foreach ($components as $name => $component) {
$this[$name] = $this->create($component);
}
}
ale mozna taky blabolim.
- Filip Procházka
- Moderator | 4668
awsickness napsal(a):
ale mozna taky blabolim.
Trošku ano. Služba sama se o naroubování starat nemůže, to musí
dělat DIC nebo CompilerExtension
. Tedy tohle je hloupost
services:
componentsFactory:
class Kdyby\ComponentsFactory(TaggedServices(component))
myPresenter:
class: Kdyby\MyPresenter(@#tags)
Tohle by už dávalo smysl
services:
componentsFactory:
class Kdyby\ComponentsFactory(@#component)
myPresenter:
class: Kdyby\MyPresenter(@componentsFactory)
Ale to je zatím nepodstatný rozdíl a je jenom krůček k tomu, aby to fungovalo. Co se týče
$this->create($component);
tak to je úplně zcestné, protože přidáváš presenteru další odpovědnosti, které se ho vůbec netýkají.
Ale o tom tato diskuze není. Diskuze je o tom, jestli má smysl, implementovat podporu pro předávání X služeb přes lazy accessor.
- Honza Marek
- Člen | 1664
Přijde mi to jako těžko pochopitelnej okrajovej případ. Což je pro mě kombinace, která nepatří do frameworku :)
- Ondřej Brejla
- Člen | 746
David Grudl napsal(a):
@#component
by mělo fungovat, obarvovač kecá.
Jaké tedy máš pro commenty pravidlo, pokud #
předchází
cosi (třeba referenční zavináč), pak je vše ok? Pokud ne, tak je to
comment? Ze zdrojáků se mi to luštit nechce (ani chuť ani čas
;). Díky.
- Filip Procházka
- Moderator | 4668
<ot>@Ondřej Brejla spíš si myslím, že jde o kontext a tohle má větší přednost</ot>
- Patrik Votoček
- Člen | 2221
- @#tag co to provede s dodatečnejma informacema pro tag (ve tvém případě je to ‚less‘ a ‚coffee‘)?
- vytváří se kolekce accessorů což je tak trochu wtf (nehledě na to že Accessor jako takový v Nette (ještě) není)
Honza Marek napsal(a):
Přijde mi to jako těžko pochopitelnej okrajovej případ. Což je pro mě kombinace, která nepatří do frameworku :)
tak nějak souhlasím
- Filip Procházka
- Moderator | 4668
Patrik Votoček napsal(a):
@#tag co to provede s dodatečnejma informacema pro tag (ve tvém případě je to ‚less‘ a ‚coffee‘)?
To je naznačené v mé implementaci. Tam předám objektu
TaggedServices
jméno tagu a Container
. Získá si
seznam služeb s tagem a pak je zpřístupní. Mému use-case to stačí,
navíc tam mám tuto
metodu, podle které si můžu získat ještě i službu s konkrétními
daty v tagu, takže například
$lessFilter = $tagged->findOneByMeta('less');
vytváří se kolekce accessorů což je tak trochu wtf (nehledě na to že Accessor jako takový v Nette (ještě) není)
To není kolekce accesorů to je accesor na podmnožinu služeb
Containeru
.
Honza Marek napsal(a):
Přijde mi to jako těžko pochopitelnej okrajovej případ.
Jak říká jeden můj kamarád, lepší 10 nápadů zavrhnout, než jeden geniální neříct ;)
- David Grudl
- Nette Core | 8218
Ondřej Brejla napsal(a):
Jaké tedy máš pro commenty pravidlo, pokud
#
předchází cosi (třeba referenční zavináč), pak je vše ok?
Všechny pravidla jsou vlastně dány těmito
regexpy. Pokud token začíná #, tak je to komentář.
Nicméně znak # může být použit uprostřed literalu.
Takže [@#component]
je ok, zatím co [#@component]
je
otevřená hranatá závorka a komentář.
- David Grudl
- Nette Core | 8218
Položím důležitou otázku: lze vytořit funkční presenter (tj. předat filtry atd.) i bez existence kontejneru?
Pokud provázání tříd povede k tomu, že musíš mít nějaký kontejner, není návrh dobrý. Řešení by se mělo odvíjet od opačného konce, tady máš něco, co lze udělat bez kontejneru, a poté teprve doplníš kontejner.
- Ondřej Brejla
- Člen | 746
David Grudl napsal(a):
Jasný, to jsem přesně předpokládal a obarvovátko jsem upravil. Díky.
- David Grudl
- Nette Core | 8218
Aby nedošlo k nedorozumění, psal jsem o obarvovátku zde na fóru, které je skutečně nespolehlivé.
- Ondřej Brejla
- Člen | 746
To mi došlo ;-) Nicméně jsem zmíněnou syntax vyzkoušel v Neon colorovátku pro NetBeans a taktéž propadlo :-) Ale už zase stojí na nohou a vesele barví.