TemplateFactory: tenká hranice mezi patternem a antipatternem

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
David Grudl
Nette Core | 8218
+
0
-

Zase se ozývá věčný evergreen: TemplateFactory a globální instalace maker, tentokrát v souvislosti s rixxi latte filters (kde je LatteFiltersExtension), Flame/Modules (kde je Latte Macros provider) nebo tohoto dotazu.

Překvapuje mě to neustále nepochopení situace kolem template factory, kdy jsem vnímán jako někdo, kdo si na truc staví hlavu a odmítá všem navzdory tohle v Nette implementovat.

Je to totiž antipattern. Znovuzavádění service locatoru do Nette. Pokusím se ještě jednou a snad už naposledy vysvětlit důvody.

Nejprve – proč je service locator antipattern? Protože tají závislosti:

$sl = new ServiceLocator;
$sl->addService(..., ...);

$foo = new Foo($sl);

Vůbec nemáme páru, co a pod jakým klíčem je třeba do $sl dodat, aby $foo fungovalo. Proto Service locator Nette definitivně opustilo a kompletně přešlo na Dependency Injection:

$mailer = new Mailer;
$logger = new Logger;

$foo = new Foo($mailer, $logger);

Pokud jste s tímto ztotožněni, čtěte dál…

Teď se dobře podívejte na tento příklad:

$template = new Template;
$template->addMacro(..., ...);
$template->addFilter(..., ...); // nebo registerHelper()

$foo = new FooControl($template);
$foo->render();

Musíme přesně vědět, co a pod jakým klíčem je třeba do $template dodat, aby $foo->render() fungovalo. Template tedy není nic jiného, než service locator pro makra a helpery. Ale to přece nechceme.

Co když SL nahradíme za Dependency injection?

$foo = new FooControl($filterUpper, $filterPadLeft, ...);
$foo->render();

Ufff… V tu chvíli vyvstává otázka, proč tohle vůbec komponentě předávat? Vždyť to nejsou žádné závislosti. To jsou funkce, které komponenta interně potřebuje pro vykreslení šablony, a obvykle není žádný důvod, proč bychom je měli mít možnost nahrazovat vlastní implementací.

Takže výsledná iterace:

$foo = new FooControl;
$foo->render();

Komponenta si sama zaregistruje, co pro vykreslení své konkrétní šablony potřebuje. Bude to v ní zadrátované úplně stejně, jako cesta k šabloně. (Zaregistrovat to může jakkoliv, na přímo, zavoláním pomocné funkce, třídy, to je fuk.)

V Nette 2.2 ale existuje služba TemplateFactory, dokonce i LatteFactory

Ano, existují, a slouží ke globální konfiguraci vlastností Latte. Jde o volbu výstupu XHTML/HTML a parametry kešování, tj. cestu k adresáři a přepínač režimu debug/production. Nic víc.

Je naprosto zásadní rozdíl mezi nastavením cesty pro cache a instalací vlastních maker. To první je totiž komponentě šumák, kdežto to druhé je pro ni esenciální a není-li nastaveno přesně tak, jak vyžaduje, dojde k chybě. Instalaci maker si proto, opakuji, musí komponenta, používá-li fixní šablonu, dělat sama.

Proč teda lze v Nette instalovat makra pomocí konfiguračního souboru?

Byla blbost tuhle funkčnost implementovat, raději použijte na instalaci maker templatePrepareFilters() a filtrů beforeRender() v base presenter/control nebo traitu.

Jak tedy docílit toho, abych mohl pomocí konfiguráku instalovat makra?

Pokud pomocí konfiguráku instalujete makra, vytváříte tím závislost presenterů či komponent na konfiguraci. Což je přesný opak inversion of control.

Filip Procházka
Moderator | 4668
+
0
-

Okey, chápu důvody a to co píšeš je samozřejmě nejčistější způsob, ale (nikdy bych nevěřil že ti budu vyčítat nedostatečnou pragmatičnost zrovna já :D ) …

Modelová situace…

  1. mám helper/makro na gravatary, chci ho v celém projektu vždy ve všech šablonách, jak na to?
    • zaregistrovat ho v každém jednom presenteru a komponentě?
    • zaregistrovat ho ve svém base control a base presenteru?
    • napsat do konfigurace jeden řádek?
  2. mám helper na formátování ceny, o kterém vím že ho budu potřebovat v celém projektu (stovky šablon komponent i presenterů)
    • zaregistrovat ho v každém jednom presenteru a komponentě?
    • zaregistrovat ho ve svém base control a base presenteru?
    • napsat do konfigurace jeden řádek?

Je jasné, že když je regnu v base controlu a base presenteru, tak to bude o trochu čistější (nějak je tam musím dostat, musejí si o závislosti pro makra říct), protože stejně všechny moje komponenty a presentery od nich dědí.

Na druhou stranu, není to tak dávno co jsi začal propagovat že base presentery jsou anti-pattern (což jsou, ale označit něco za anti pattern a ukázat lepší řešení, jsou dvě různé věci), nehledě na to že se stejně hromadné utíkání od base presenterů přes noc nekonalo.

Jak bys předložené reálné potřeby z praxe řešil?

David Grudl
Nette Core | 8218
+
0
-

Jak řešíš to, že máš DB connection, jenž potřebuješ v celém projektu? (Hromada tříd.)

  • Environment?
  • nestatický ServiceLocator?
  • Dependency Injection?

(To je naprosto stejná otázka. Použij, co chceš. Ale nechtěj po mně, abych v Nette nebo v příkladech používal něco jiného, než DI.)

David Matějka
Moderator | 6445
+
0
-

a nemely by se smazat i defaultni helpery a makra? (treba tyhle)
myslim, ze budes souhlasit, ze ne. Jsou to proste core veci, ktere jsou casto potreba v kazde aplikaci a v kazde sablone. Ale v ramci urciteho projektu se proste ten seznam maker/helperu, ktere potrebujes vsude, rozsiruje. Gravatary, obrazky, ceny, rozsirena makra pro formulare… A znovu a znovu registrovat v desitkach a stovkach komponent proste nedava smysl. Nehlede na to, ze pri podobnych commitech bych to musel vsude upravovat.

Je naprosto zásadní rozdíl mezi nastavením cesty pro cache a instalací vlastních maker. To první je totiž komponentě šumák, kdežto to druhé je pro ni esenciální a není-li nastaveno přesně tak, jak vyžaduje, dojde k chybě.

Stejne tak pokud bude nastavena spatna cesta k cache, dojde k chybe. Nebo nebude existovat nejaka z techto promennych, dojde k chybe. Proste uz ted jsou sablony zavisle na globalni konfiguraci a rict „Tak, tyhle makra, tyhle helpery a tyhle promenne jsou core a jsou spolecne pro vsechny, vsechno ostatni uz se musi registrovat individualne, jinak je to antipattern“ nechapu – bud vse nebo nic :)

Pokud pomocí konfiguráku instalujete makra, vytváříte tím závislost presenterů či komponent na konfiguraci.

Kdyz bych to vzal do extremu, tak i registraci sluzeb v konfiguraku vytvarim zavislost presenteru, komponent ci jinych sluzeb na konfiguraci – spoleham na to, ze uz budou registrovany a spravne inicializovany.

David Grudl
Nette Core | 8218
+
0
-

@matej21: tebou uvedené helpery jsou natvrdo zadrátované v Control (tj. Control natvrdo volá onu TemplateFactory, která je registruje), takže je můžeš s klidem používat. Pokud se změní název třídy nějakého makra, změní se jeden řádek v oné TemplateFactory. Jaképak upravování všude?

Oprava, tohle jsem napsal blbě, nakonec kvůli zpětné kompatibilitě to Control ve verzi 2.2 zadrátované nemá, tady jsem předbíhal.

enumag
Člen | 2118
+
0
-

#trolling: A kde přesně komponenta specifikuje že je závislá na šablonovacím systému Latte?

Zkusím nastínit svůj pohled na tuto problematiku, který je trochu jiný než Davidův a poté nápad ke kterému jsem došel po přečtení Davidova příspěvku.

Už nějakou dobu používám níže uvedený způsob. Vzniklo to jako side-efekt mé adresářové struktury založené na PSR-4 (šablony mám zcela jinde než PHP třídy) a faktu že jsem chtěl mít možnost vytvářet themes a tedy pro jednu komponentu použít vždy šablonu z právě aktivní theme.

namespace App\FrontModule;
use Nette\Application\UI\Control;
class MyControl extends Control {
	// ...
	public function render($file)
	{
		$template = $this->getTemplate();
		$template->data = $this->someModel->getWhatever();
		$template->setFile($file);
		$template->render();
	}
}
{control myControl, $themeDir . '/@components/MyControl/default.latte'}

Zde je podstatná jedna věc. Komponenta samotná neví jaká šablona ji bude vykreslovat a taky ji to ani trochu nezajímá. Netuší jaká makra a helpery šablona použije, dokonce ani neví zda používá Latte, Twig, čisté PHP nebo něco úplně jiného. Tedy narozdíl od Davida nevnímám šablonu komponenty jako její nedílnou součást. Použitá makra a helpery tedy nejsou závislostí komponenty ale závislostí té šablony. Konfigurace maker a helperů z tohoto pohledu je konfigurace view vrstvy aplikace, tedy zajištění aby její šablony fungovaly. Zde už mi začíná dávat smysl makra registrovat v config.neon. Samozřejmě nic nebrání existenci více různých ITemplateFactory.

Po přečtení Davidova příspěvku jsem se nad tím zamyslel trochu více. Pokud komponenta nepotřebuje konfigurovat šablonu tak k čemu vlastně potřebuje ITemplateFactory? A vzato do důsledku, k čemu vlastně potřebuje ITemplate? Co takhle to celé otočit naruby?

class MyControl extends Control {
	public function getView()
	{
		return [
			'control' => $this,
			'flashes' => ...,
			'data' => $this->someModel->getWhatever(),
			// ...
		];
	}
}
// šablona, pro ukázku použito PHP
// reálně by se na to udělalo latte makro které by dostalo název souboru s šablonou, název komponenty a případně konkrétní instanci TemplateFactory
$templateFactory->createTemplate()
	->setFile($themeDir . '/@components/MyComponent/default.latte')
	->setParams($presenter['myControl']->getView())
	->render();

Je-li to celé blbost, prosím poučte mne. ;-)

Editoval enumag (19. 5. 2014 13:51)

David Matějka
Moderator | 6445
+
0
-

@David Grudl: chapu spravne, ze pokud bych si udelal vlastni implementaci TemplateFactory, ktera by mi registrovala makra, ktera potrebuju, je to v poradku?

Pokud se změní název třídy nějakého makra, změní se jeden řádek v oné TemplateFactory. Jaképak upravování všude?

viz tvoje:

Instalaci maker si proto, opakuji, musí komponenta, používá-li fixní šablonu, dělat sama.

Pokud tedy macroset (jako muj odkazovany) zmeni NS, musel bych to menit vsude

Editoval matej21 (19. 5. 2014 13:43)

David Grudl
Nette Core | 8218
+
0
-

enumag: tohle má smysl. Zopakuji z úvodního postu, že „instalaci maker si musí komponenta, používá-li fixní šablonu, dělat sama.“ Naopak, pokud cesta k šabloně se komponentně předává, instalaci maker a helperů dělat nemá.

V takovém případě můžeme třeba komponentě předat celý objekt Template a renderovat ji pomocí $template->render(NULL, $params) bez side efektů. Nebo úplně jinak.

David Grudl
Nette Core | 8218
+
0
-

matej21: přece nikde netvrdím, že je nutné, aby se addFilter apod. nacházelo ve zdrojovém kódu třídy komponenty, nebo že je nutné používat copy&paste.

enumag
Člen | 2118
+
0
-

@DG:

Jasně, to mne napadlo taky ale pak jsem si všiml že komponenta teoreticky ani nepotřebuje ten objekt Template a otočil to komplet. Je to ale v podstatě ekvivalentní. :-)

Vzato do důsledku, komponenta by pokud možno neměla používat fixní šablonu. Max může mít nějakou metodu která vrátí název výchozí šablony. V tom případě ale stále je na volajícím aby zajistil že ta šablona dostane potřebné závislosti (správně nakonfigurovaný latte engine etc.).

Mimochodem stejným způsobem by šlo řešit i formuláře. Rozdíl mezi rendererem a šablonou se poněkud vytrácí.

Nyní se vrátím k tvému úvodnímu příspěvku, na konci píšeš:

Pokud pomocí konfiguráku instalujete makra, vytváříte tím závislost presenterů či komponent na konfiguraci. Což je přesný opak inversion of control.

Jak jsme se přesvědčili, nejde (respektive nemusí jít) o závislost presenterů a komponent ale pouze o závislost šablon. To ti vadí také nebo ne? Pokud vadí, proč? Když chci komponentě předávat hotovu šablonu (nebo jen hotové šabloně předat parametry z komponenty) kde bych měl provádět tu konfiguraci pokud ne v configu?

Editoval enumag (19. 5. 2014 14:11)

Honza Marek
Člen | 1664
+
0
-

Však to přece může každý dělat stejně jako David. Když potřebujete nějaký helper nebo makro doplnit globálně k celému projektu, tak ho přece můžete vrazit do zdrojáků Nette. Nebo ne? ;)

Filip Procházka
Moderator | 4668
+
0
-

Tím že to netvrdíš (napsal jsi jenom jak to nedělat) dáváš velký prostor pro interpretaci.

Jak řešíš to, že máš DB connection, jenž potřebuješ v celém projektu? (Hromada tříd.)

Good point. Použít service locator u tříd v modelu bych nevydýchal :) Na druhou stranu mi dělá problém tohle dělat u všech šablon. Něco takového si hodně těžko obhájím před zbytkem týmu.

Ale nechtěj po mně, abych v Nette nebo v příkladech používal něco jiného, než DI.

Nic takového nikde netvrdím ani nechci. Co po tobě chci je, abys ukázal i jak to řešit, ne jenom jak to neřešit.

Takže to shrneme

  • buďto všechno napíšeme 200×, což je nejčistější ale neobhájím si to nikdy před týmem tak aby to dělali i z jiného důvod než jenom „procházka nám to nařidil“
  • nebo to dám do createTemplate() v mém BaseControl a BasePresenter
  • nebo si podědím TemplateFactory a přidám to tam a samozřejmě budu muset ve svém BaseControl a BasePresenter vyžadovat svou konkrétní TemplateFactory a nechat tu výchozí pro všechny cizí komponenty

nebo … ?

David Grudl
Nette Core | 8218
+
0
-

enumag: v případě komponent mám na mysli celou dobu tento typ renderování, tj. situaci, kdy je způsob vykreslení implementačním detailem, do něhož uživatel nevidí a nezasahuje (tedy ani nemá).

Pak je přesnější říct, že jde o závislost komponenty na konfiguraci, neboť o existenci šablony vlastně nevíme. (U presenterů je situace maliko jiná, protože oni vlastně nikdy nic nerenderují, ale o to teď nejde.)

Pokud komponentě předávám hotovou šablonu, je situace docela jiná, jde spíš o nestandardní řešení, a asi bych to v tomto vlákně dál nerozebíral. Nejspíš by se to mělo podchytit typově, tj. komponenta MyCoolControl bude vyžadovat šablonu MyCoolTemplate, čímž je jasné, že musí být správně nakonfigurovaná.

David Grudl
Nette Core | 8218
+
0
-

Filip: opět stejně jako s DB connection. Pokud ji potřebuji ve 200 třídách, buď napíšu 200 konstruktorů/setterů nebo budu dědit od společné třídy s tímto konstruktorem/setterem nebo použiji trait se setterem.

Analogicky totéž u komponent, jen místo setteru pro DB půjde o funkci createTemplate().

Honza Marek
Člen | 1664
+
0
-

@David Grudl: Doporučil bys, aby všechny presentery v aplikaci měly stejně nastavené šablony? A není to nečisté? Je to ok? A pokud komponenta patří jen do té aplikace a nedává smysl ji udělat univerzální pro další projekty, nejedná se náhodou o ÚPLNĚ to samé?

Filip Procházka
Moderator | 4668
+
0
-

Díky, jsem rád že už si rozumíme :) Společný předek je přijatelné řešení.

David Grudl
Nette Core | 8218
+
0
-

Honza Marek: určitě chci, aby všechny presentery a komponenty měly co se týče šablon stejně nastavené kešování, proto existuje TemplateFactory. Nerozumím té otázce na čistotu.

stekycz
Člen | 152
+
0
-

Ze všeho co tady čtu se mi asi nejvíce líbí řešení přes traity. Kód je na jednom místě a můžu ho bez problémů použít jako v presetneru tak v komponentě. Neměl by být ani problém použít stejnou traitu i pro šablony emailů a šablony mimo presentery a komponenty obecně. Velmi dobře si tak mohu i roztřídit skupiny maker a použít je jenom tam, kde je budu potřebovat. V případě vlastních maker v nějakém balíčku mi autor ani nemusí nutit dědit nějakou jeho třídu, ale jenom použití traity.

Šaman
Člen | 2658
+
0
-

Trochu mi to připadá, že se točíme v kruhu. Teď se teprve dostáváme k tomu o co mi šlo v mém dotazu a na co David stále neodpověděl. (Asi moje chyba vyjadřování , protože jsi odpověděl na jinou otázku – nešlo mi o to nechat volbu TemplateFactory na konfiguraci, jde mi o injectování jediné TemplateFactory do všech potomků BaseControl a BasePresenter).

Společný předek, na tom jsme se dohodli. Mám kód (obsahuje pravidla pro dohledání šablony, nebo třeba nastavení maker o kterém je zde) a chci ho předat do BaseControl i BasePresenter. Ten kód je samozřejmě v externí třídě, protože ho nechci opakovat na dvou místech a protože kompozice.

  1. Buď použiju konstruktor injection a každá komponenta bude mít obsazený první parametr konstruktoru. Fajn, potomci budou mít druhý, případně další. A pokud se časem rozhodnu přidat do BaseControl další závislost? Jsem v pr.. takovém menším construction hellu. Situace není tak hrozná jako u presenterů, protože nemáme tak rozvětvenou dědickou strukturu, ale je nepříjemná.
  2. Použiju inject metodu (zde vhodné i z toho důvodu, že tato závislost není životně důležitá, i potomku našeho BaseControl mohu implicitně zadat soubor s šabonou) a teď musím při každém vytvořením třídy komponenty pomocí kontejneru řešit autowired: yes. (Nebo si psát vlastní, složitější továrničku.)

3. Anebo si to natvrdo (skrytě) vytáhnu z kontejneru, případně natvrdo nakódím v obou předcích.

Utekla mi nějaká možnost, která tohle řeší? Pokud ne, mohlo by Nette zapnout autowiring pomocí inject metod? Ať už implicitně, nebo jediným přepínačem v konfiguraci? Podle mě je method injection zcela legální a čistý způsob (ač konstruktor bývá často vhodnější) a nevím, proč jej diskriminuješ. Vede to k tomu, že už vím o dvou řešeních, která toto omezení obcházejí (myslím, že Kdyby a určitě tato extension, resp. obecně tato rada).

P.S. Nechceme po tobě abys to používal ty, nebo Nette. Ale v tomhle případě mám pocit, že se nás snažíš vychovávat a proto znesnadňuješ zcela legitimní způsob injectování, zřejmě proto, že ty už jsi ho překonal. (Nebavím se tu o anotaci @inject, která vyžaduje public property a proto porušuje jedno zpravidel čistého OOP. Bavím se o metodách – setterech – které jsem speciálně pojmenoval aby na nich fungoval autowiring. Tyto metody jsou součástí api a zcela veřejně se k daným závislostem přiznávají se zachováním plné zapouzdřenosti. Jediný důvod proti nim mě napadá ten, že umožňují prasit. To se nevyřeší jejich omezením – které mnoho lidí obchází – ale spíše osvětou. To už je ale téma mimo technické záležitosti Nette.)

David Grudl
Nette Core | 8218
+
0
-

Šaman: neumím ti odpovědět jinak, než jako předtím.

a: Má tvoje komponenta metodu render, která vykreslí řetězec?
b: Má.
a: Používá se uvnitř metody render PHP šablona, Latte šablona nebo DOM?
b: To ti může být úplně ukradené, to je věc její vnitřní implementace.
a: Jak mám komponentě předat konfiguraci maker té šablony?
b: Jaké šablony? O šabloně nic nevíš, nešťourej se ve vnitřní implementaci, po tom je ti kulový.
a: Jak si teda ta komponenta nainstaluje makra a filtry?
b: Tak, jak bude chtít. Zavolá addFilter(), zavolá nějakou třídu, která to udělá, nech to na ní, nic neřeš a hlavně – nic neinjektuj. :-)

(k @injekt a inject() tu prosím neodbočujme)

Šaman
Člen | 2658
+
0
-

Já už vážně nevím, jestli se mě snažíš schválně vytrollit, nebo se nedokážu vyjádřit, nebo přehlížím tak extrémně jasné řešení, že brání tomu, abychom si rozumněli.

1. Od začátku jsem se bavil o inject metodách, TemplateFactory byl jen konkrétní příklad.

2.

David Grudl napsal(a):
b: Tak, jak bude chtít. Zavolá addFilter(), zavolá nějakou třídu, která to udělá, nech to na ní, nic neřeš a hlavně – nic neinjektuj. :-)

Jak tu třídu zavolá? Staticky? Pokud ne, tak si ji přece musí nějak injectovat a v tom je jádro pudla.

A neřeš? Jak to myslíš, když píšu tu komponentu? A když popatnácté registruji stejné filtry pomocí addFilter() a zjišťuji, že jsou všechny mé render metody podobné jak vejce vejci, samozřejmě začnu přemýšlet nad tím, tak ten podobný, až stejný kód vyčlenit na jediné místo.

David Grudl
Nette Core | 8218
+
0
-

Nikoho netrolím, nemám důvod, takže si prostě nerozumíme.

Pokud máš v patnácti třídách registraci stejných filtrů, tak tu registraci copy & paste do nové třídy a metody a volej jen tu metodu. Nebo to dej to společného předka. Co je proboha na tom nepochopitelného? A jak to souvisí s injektováním?

Šaman
Člen | 2658
+
0
-

Takže za c) extrémně jednoduché řešení. Ale nevznikla mi právě teď neviditelná závislost, když to volám staticky? Protože přesně o tohle mi šlo, jen jsem si tu třídu chtěl injectovat a metodu volat dynamicky.

(Začínám tušit, že jádro pudla je v nepochopení toho, co je vlastně závislost a co je jen vyčleněný kód.)

Editoval Šaman (19. 5. 2014 17:13)

David Grudl
Nette Core | 8218
+
0
-

To je jako kdyby jsi chtěl každé použití globální nebo statické funkce nahradit callbackem, který bys injektoval. Proč? Volání čistých deterministických funkcí, které nemají postranní efekty a se stejnými argumenty vrací vždy stejnou hodnotu, není závislost.

Viz http://www.zdrojak.cz/…storu-v-php/?…

Šaman
Člen | 2658
+
0
-

Ok, tohle mi nedocházelo.

Statické třídy jsem bral jako zlo, použitelné jen pro obecné funkce jako Strings::toUpper($string). Tedy vesměs něco, co patří spíš do /vendor/others, než do /app.

Díky za trpělivost. :)

David Grudl
Nette Core | 8218
+
0
-

Jsem rád, že si rozumíme. Jinak statickou metodu můžeš klidně nahradit za (new MyTemplate)->registerFilters($template), ale nic se tím nemění.

David Matějka
Moderator | 6445
+
0
-

@dg: a jak bys resil, kdyby helpery potrebovaly nejakou zavislost (cache, databaze..) – vyzadovat vse, co ty helpery potrebuji a to pak predat „MyTemplate“? (to se mi fakt nelibi). nebo registrovat „MyTemplate“ jako sluzbu? (ale tim se vracime v diskuzi zpatky :)) nebo nejaky jiny reseni?

David Grudl
Nette Core | 8218
+
0
-

Matej21: těch možností je více. Začněme tím, že ve hře není žádná template factory a komponenta si řeší šablonu sama, pak budou tyto služby jejími závislostmi. Stejně tak i v případě, že bychom použili jen nějaký helper jako tady.

Pokud by tu závislost jen delegovala dál do nějakého filtru/helperu, bylo by vhodnější místo ní předávat rovnou ten nakonfigurovaný filtr.

Pokud by takových filtrů bylo víc, nebo zejména kdyby bylo víc komponent, které je potřebují, bylo by načase předávat rovnou template factory, jejímiž závislostmi by byly ony služby.

V komponentě bych ale nemohl chtít obecnou Nette\Bridges\ApplicationLatte\TemplateFactory, chtěl bych vlastní template factory, která garantuje, že tam budou ty očekávané filtry.

Vytvoření takové komponenty v presenteru by už nebylo úplně triviální, protože je třeba jí předat template factory. Proto bych si vytvořil v konfigu továrnu na tuto komponentu a do presenteru předával už ji.

mishak
Člen | 94
+
0
-

TL;DR Ano je to anti-pattern ale stejně tak filtry nebo použití translatoru.

Jen pro upřesnění rixxi/latte-filters staví na 2.2 a nikdy nebude jejím cílem instalace maker ale jen registrace filtru (předtím se jim říkalo runtime helper), především dynamických. Je to čistě pragmatická knihovna co nedělá nic jiného, než že mi usnadňuje šablonování napříč presentery a komponentami. Jediným cílem je oddělení šablon od jádra aplikace a odlehčení komponentám. Prakticky slouží k zaručení že generické helpery budou podporovat všechny šablony napříč systémem. Ať už jde o translator nebo lokalizovaný výpis dat (datum, měna, peníze) které Nette vůbec neřeší nebo nešťastně (např.: helper ne-helper translator).

Jestli někde přidávat filtry je správné a někde není je absurdní. Filtry jsou negarantovaný hashmap, taky kvůli ním nedědím template a v ní 20 funkcí jen abych garantoval konzistenci filtru. Stejně tak nedědím templateFactory protože by to byl overkill co mě bude ještě víc omezovat.

Editoval mishak (19. 5. 2014 21:56)

mystik
Člen | 308
+
0
-

Zkusil bych přispět do diskuze s jedním modelovým případem. Hlavním argumentem proč ne TemplateFactory je, že by to zhoršilo přenositelnost komponent. Pokud ale řešíme přenositelnost tak je podle mě nutné uvažovat o jedné další věci:

Mám nějakou obecnou komponentu třetí strany, kterou použiju ve svém projektu. Ale potřebuju upravit/doplnit její vzhled, protože mi ten výchozí nezapadá do mojí aplikace. Na to potřebuju změnit její šablonu. A to tak, že v téhle její nové šabloně potřebuju nějaké svoje obecné helpery/makra.

Jaké by podle vás bylo nejlepší řešení těchto situací?

Btw za mě se mi hodně líbí navržené řešení od enumag, které by zodpovědnost za vykreslení přeneslo z kompomenty ven. Prostě zrušit závislost komponenty na její šabloně.

Asi bych se zkusil zamyslet jestli je vykreslení opravdu zodpovědností komponenty a mělo by být jen jejím implementačním detailem.

Editoval mystik (20. 5. 2014 18:42)