Template factory: TODO
- Honza Marek
- Člen | 1664
V Nette by měla být jedna globální template factory jako služba. Pak by bylo snadné například na jednom místě ve vlastní implementaci ITemplateFactory nebo v configu zaregistrovat helpery či makra pro celou aplikaci, což je velmi často potřeba. Nyní je potřeba registraci helperů nebo vlastních maker zařizovat jednak v nějakém BasePresenteru a i v nějaké BaseControle, protože vytváření šablony je někde napraseno ve třídě (asi) PresenterComponent.
Pokud by nějaká komponenta z addonů na tuto službu nechtěla spoléhat, použije DefaultTemplateFactory nebo si implementuje vlastní template factory.
Nemáte někdo chuť to implementovat?
- David Grudl
- Nette Core | 8229
Slovo globální vždycky zavání ;-)
Otázka je, co je to pro „celou aplikaci“. Jednotlivé komponenty určitě žádnou globální factory nechtějí, protože si věci řeší po svém (aby byly přenositelné). Totéž platí pro jednotlivé moduly. Takže spíš jde o to, jak ovlivnit template factory na úrovni modulů + komponent.
- Honza Marek
- Člen | 1664
Pokud přidáš nějaké helpery/makra do šablony, tak neovlivňuješ přenositelnost komponent, které využívají jen základní funkčnost šablon z nette.
To, že chceš mít k dispozici v celé aplikaci stejně nakonfigurované šablony je strašně častá situace. Nechceš se prostě starat o to, které fičury kde fungují, očekáváš je všude. Nakonec se to hodí i pokud budeš chtít upravit šablonu přenositelného datagridu.
Tu myšlenku bys neměl odsoudit na základě použití slovíčka globální. Máme DI a ty komponenty, které budou chtít jinou template factory ji přes setter nebo konstruktor dostanou.
- Filip Procházka
- Moderator | 4668
Proč né tohle? Kdyby se to ještě decánko učesalo, tak to imho splní účel perfektně.
- David Grudl
- Nette Core | 8229
Jednak by to chtělo přepsat ze šíleného konfiguračního jazyka do
normálního PHP, ale především tím
$presenter->getContext()->getService('nette.template')
se
prohlubuje onen „globální problém“.
- pawouk
- Člen | 172
Slovo globální je zde dle mého použito trochu ve špatném významu. Template factory jako servis ano, ale to přece neznamená že je globální. Nicméně s myšlenkou souhlasím a sám to již dlouhou dobu používám. Občas si třeba potřebuji jen vytvořit template někde mimo tak si prostě vytvářím přez TemplateFactory, což je definované v contextu. Pak jde jen o to přepsat BasePresenter a BaseControl metody createTemplate a je to. Jestli to bude Nette implementovat nebo ne je mi fuk, já to rozhodně budu používat tak jako do ted, což je přes (injektovaný) templateFactory.
- Filip Procházka
- Moderator | 4668
Kdybys mi to napsal tam, tak jsem to mohl před čtvrt rokem opravit :) A dneska to předělám.
- Milo
- Nette Core | 1283
Mě se moc nelíbí, aby se šablony vytvářely na jednom místě a cpalo se do nich úplně vše. Dřív jsem se o to snažil, až jsem jednoho dne kopíroval komponentu se šablonou z jedné aplikace do druhé a hle, najednou mi chybí pár helperů tady a pár maker tam a dopadlo to tak, že jsem zběsile copy-pastoval vše možný.
Mě by se líbilo, kdyby si šablona mohla říct, co chce. To by bylo porno.
{require Nette.urls}
{require Nette.Helpers.bytes}
Editoval Milo (14. 3. 2013 11:58)
- Honza Marek
- Člen | 1664
Je naprosto logický, že když chceš v šabloně nějaké helpery používat, tak je tam musíš zaregistrovat. To, že bude složitější registrace helperů, aby si lidi radši nic neregistrovali, to přece není řešení :D
- Milo
- Nette Core | 1283
Honza Marek napsal(a):
Je naprosto logický, že když chceš v šabloně nějaké helpery používat, tak je tam musíš zaregistrovat.
Souhlas :-) Ale při tom kopírování komponenty mi nedošlo, že se helpery registrují jinde. Byly to helpery, které jsem původně psal pro jinou šablonu ale pak se mi hodily i tady.
To, že bude složitější registrace helperů, aby si lidi radši nic neregistrovali, to přece není řešení :D
To byl jen pseudo-příklad. Nejde mi o to, aby se každý helper musel „requirnout“. Jen o to, aby si šablona mohla říct co chce a nečekat, že to tam prostě bude.
- David Grudl
- Nette Core | 8229
Klíčové je si uvědomit, že generování šablony není služba.
Úkolem komponenty je něco vykreslit. Jak to udělá (přímo HTML, Latte, jiný šablonovací jazyk, atd) je její interní věcí, která musí být imunní vůči prostředí, kde komponenta žije. Konfigurovatelné může být snad jen to, jestli generovat XHTML nebo HTML kód.
- pawouk
- Člen | 172
Componenta může mít defaultní vytvoření template tak jak má, ale pokud ji nastavíš template, tak se použije to co nastavíš. Stejně jako je Reflection u DB nebo Mailer u Emailu. Což tak defakto je žejo :-) tedy ještě by musela v Control přibýt metoda setTemplate() a neni co řešit…
Sám ovšem generování šablon jako službu používám a jsem s tím spokojen, ikdyž je pravda, že komponenta pak není izolovaná, ale to mě netrápí.
Editoval pawouk (14. 3. 2013 13:26)
- Honza Marek
- Člen | 1664
David Grudl napsal(a):
Klíčové je si uvědomit, že generování šablony není služba.
Úkolem komponenty je něco vykreslit. Jak to udělá (přímo HTML, Latte, jiný šablonovací jazyk, atd) je její interní věcí, která musí být imunní vůči prostředí, kde komponenta žije. Konfigurovatelné může být snad jen to, jestli generovat XHTML nebo HTML kód.
Máš nějakej lepší nápad jak řešit situaci, že chceš mít ve všech vlastnoručně napsaných komponentách a v presenterech stejně nastavené latte, protože nechceš řešit v rámci jednoho webu kde jaký helper funguje a nefunguje?
Udělat službu TemplateFactory mi přijde jako velmi užitečná věc a ostatně konkurence to tak má
Dále bych upozornil, že by to přispělo k zeštíhlení presenteru z five responsibilities principle na four responsibilities principle. A taky, že bych na to byl schopnej vyrobit pull request tak za hodinu práce, protože to mám celkem rozmyšlené. Nemusíš tedy argumentovat proti jen proto, abys to nemusel programovat ;)
- David Grudl
- Nette Core | 8229
Tohle jsou všechno, jeden vedle druhého, nevalidní argumenty.
Pokud chci mít ve všech komponentách a presenterech stejně nakonfigurovanou šablonu, nechám si je všechny vyrobit společnou továrnou.
Přičemž přenositelná komponenta by neměla být závislá na tom, jakou továrnou je vyrobená.
- Patrik Votoček
- Člen | 2221
David Grudl napsal(a):
Úkolem komponenty je něco vykreslit. Jak to udělá (přímo HTML, Latte, jiný šablonovací jazyk, atd) je její interní věcí, která musí být imunní vůči prostředí, kde komponenta žije. Konfigurovatelné může být snad jen to, jestli generovat XHTML nebo HTML kód.
Nevidím důvod proč by komponenta nemohla dostat informaci (pomoc / vykreslovatko) to jak se má vykreslit z venku.
Viz třeba renderer u formulářů, proč by tohle samé nemohlo být i u „normálních“ komponent?
Pokud chci mít ve všech komponentách a presenterech stejně nakonfigurovanou šablonu, nechám si je všechny vyrobit společnou továrnou.
Což momentálně nelze jinak než si udělat BasePresenter a BaseControl ve kterých bude stejný copy-paste kód. (Nebo se pletu?)
A to je imho to o co tady jde.
- David Grudl
- Nette Core | 8229
Copy-paste není potřeba, může tam být volání stejné factory.
A co tady jde, je použití globálna. Beru řešení, které nepoužije static, global nebo $context. Tj. něco, co je čistější, než současné nedokonalé řešení. Thats all.
- Honza Marek
- Člen | 1664
Z téhle rozdělané větve by mělo být zřejmé, jak to celé myslím – https://github.com/…late-factory
K mergnutelnosti chybí poladit nette extension a vyrobení testů.
Má smysl, abych pokračoval?
- Tomáš Votruba
- Moderator | 1114
Ping. Pro mne má
Zatím to vypadá dobře. Jaké máš ještě plány? Už je to nějak
použitelné? Budu rád za příklad.
- David Grudl
- Nette Core | 8229
Problém je v tom
$templateFactory = $this->templateFactory ?: $this->getPresenter()->getTemplateFactory();
.
Pokud si změním v aplikaci výchozí template factory, přestanou být komponenty přenositelné.
- Honza Marek
- Člen | 1664
Nesouhlasím.
Pokud někdo bude měnit template factory, bude to dělat z důvodu, aby rozšířil její funkčnost. Pokud tedy komponenta bude používat základní podmnožinu maker, helperů atd., tak nebude problém. Pokud bude komponenta potřebovat něco speciálního, stejně si musí šablony řešit po svém, takže ani tady změna nebude.
Ano, čistě hypoteticky si někdo může vyrobit template factory, která bude třeba používat Twig místo Latte. Pokud se ale do něčeho takového pustí, jistě zvládne i zavolat setter na template factory u té přenositelné komponenty.
- David Grudl
- Nette Core | 8229
Pokud tedy říkáš, že
- template factory slouží k rozšíření funkčnosti
- ale komponenty se to nedotkne, protože ta bude využívat základní sadu,
vyplývá z toho zbytečnost navrhované úpravy.
- Honza Marek
- Člen | 1664
Cyklíme se!
- Existují i nepřenositelné komponenty fungující jen v jedné konkrétní aplikaci.
-
Honza Marek napsal(a):
Nakonec se to hodí i pokud budeš chtít upravit šablonu přenositelného datagridu.
- enumag
- Člen | 2118
@dg: Zbytečné to není, CompilerExtension tím získá několik možností navíc:
- Přidávání helperů do šablony.
- Přidávání vlastních proměnných do šablony, tyto proměnné pak mohou
používat makra, které to extension registruje. Nyní když ve vlastním makru
potřebuji nějakou službu, musím po uživateli chtít aby mi tu službu
předal do šablony jako proměnnou nebo v makru použít
$_presenter->context->getByType(...)
.
Dle mého názoru je primárním účelem služby templateFactory právě možnost rozšíření přes CompilerExtension, nikoli možnost kompletní výměny templateFactory. Např. mohu mít addon který pomocí CompilerExtension zaregistruje makra a helpery, které pak bude používat komponenta z toho addonu – tj. komponenta bude přenositelná společně s tím extension, protože extension zařídí její závislosti.
- Honza Marek
- Člen | 1664
enumag napsal(a):
- Přidávání vlastních proměnných do šablony, tyto proměnné pak mohou používat makra, které to extension registruje.
Ideální stav u vlastních maker by byl takový, že makro je objekt a referenci na potřebnou službu už má v sobě.
- Filip Procházka
- Moderator | 4668
Problém je v tom, že makro se ti spouští jenom a pouze při komplikaci a když potřebuješ nějaké závislosti runtime, tak je tam přes makro těžko dostaneš :)
- enumag
- Člen | 2118
@Honza Marek: Přesně jak říká Filip, nejde o závislosti které jsou pořeba při kompilování latte → PHP, ale o závisloti které makro potřebuje při spuštění výsledného PHP kódu. Makro jako objekt vůbec nic neřeší protože i ten bys musel nějak dostat do té šablony, což jsme tam kde jsme byli.
- sifik
- Člen | 27
Nadruhou stranu se musí „globálnímu“ template factory nechat, že je to docela dost velká úspora času.
Když jsem na jeden ze svých projektů naimplementoval postup, který je detailně popsaný zde – https://forum.nette.org/…-pres-config, na content loadu jsem ušetřil více jak 100ms.
- Honza Marek
- Člen | 1664
Kdeže, David si z neznámého důvodu myslí, že všechny controly by si měly nastavovat šablony samy. Ne, že by se to mělo umožnit, ale že by se to mělo vynutit. Myšlenka template factory jako služby jde bohužel proti tomu :)
- enumag
- Člen | 2118
@Honza Marek: Brání nám něco v tom udělat to jako extension s traitami pro presenter a control? :-)
Pokud jde o přenositelnost komponent tak to řeší CompilerExtension distribuované společně s komponentou (viz zde) mnohem lépe imho. Rád bych znal Davidův názor na tuto možnost, ale bohužel se zatím nevyjádřil.
- mishak
- Člen | 94
Podobný problém řeším s rixxi/template-factory a rixxi/template-locator. Nejspíš o nich nevíte, ale než se dokopu napsat dokumentaci vyřeší se to v Nette :)
Editoval mishak (6. 1. 2014 21:01)
- Tomáš Votruba
- Moderator | 1114
@enumag: TemplateFactory jako extension – Zenify/TemplateFactory
Prosím pouze o podněty ke zlepšení. Díky.
- enumag
- Člen | 2118
@Tomáš Votruba:
Undefined $this->paramService
: https://github.com/…eFactory.php#….
Imho jsou lepší konstanty ve tvaru TAG_*: https://github.com/…xtension.php#….
Konfigurace extension imho není nutná když se totéž dá udělat pomocí tagů (a ty jsou zde vhodnější).
Myslím že by to mělo jít bez DIC, jen s předáním polí služeb + ITranslatoru (ten by měl povoleno NULL): https://github.com/…eFactory.php#….
Zde bych použil protected: https://github.com/…eFactory.php#….
Imho by to nemělo chtít Control, vytvořit šablonu potřebuju třeba i pro e-mail: https://github.com/…eFactory.php#L54, tzn. parametry ať si doplní Control/Presenter – v tvém případě TTemplateFactory trait. Že presenter / control pro vytvoření e-mailu stejně potřebuji kvůli odkazům není platný argument, generování odkazů by též mělo být služba.
Přemýšlím že by to mohla být generovaná továrna, ale na to by možná byly potřeba nějaké úpravy v Nette DI.
- Tomáš Votruba
- Moderator | 1114
@enumag: Díky za přehledný feedback. OK: 1. layouts, 2. tagy, 3. konfigurace, 5. protected, viz změny,
4. (pole služeb) Tedy předat nette.templateCacheStorage
,
Nette\Http\IResponse
, Nette\Caching\IStorage
,
Nette\Http\IRequest
a Nette\Localization\ITranslator
přes constructor, je tak?
6. (ne control) Zkusil jsem přesunout. Mohl bys mi případně ukázat konkrétní návrh?
7. (generovaná továrna) Zkoušel jsem, ale neuspěl.
- enumag
- Člen | 2118
@Tomáš Votruba:
4. Myslel jsem pole filtrů, helper loaderů a maker, ale neuvědomil jsem si že těch závislostí je tolik. Tzn. to co jsi uvedl bude lepší. Službu nette.templateCacheStorage bude nutné předat ručně v extension, v parametrech bude tedy první. Ostatní stačí přes autowiring, ITranslator bude poslední a s NULL.
6. Tohle souvisí ještě s jednou věcí – bylo by fajn aby cizí extension mohlo vynutit přítomnost určité služby v šabloně, aby tu službu mohlo používat nějaké makro definované stejným extension. Ještě jsem úplně nevymyslel kudy na to.
7. Ok, chápu.
Když tak koukám na 4 a 6 tak spolu vlastně taky souvisí, pokud by se 6 podařilo elegantně vyřešit tak TemplateFactory ubydou některé závislosti.
Editoval enumag (5. 2. 2014 13:30)
- enumag
- Člen | 2118
ad 6) Přemýšlím nad něčím typu ITemplateConfigurator, kterých by bylo více (přidávány pomocí tagu) a ta TemplateFactory by novou šablonu prohnala těmito službami. Ten konfigurátor by mohl vypadat asi takto:
class TranslatorTemplateConfigurator implements ITemplateConfigurator
{
private $translator;
public function __construct(ITranslator $translator = NULL) { ... }
public function configureTemplate(ITemplate $template) {
$template->translator = $this->translator;
}
}
Považujete to za dobrou cestu nebo za blbost?
EDIT: Pozn. šablona by imho vůbec neměla mít metodu setTranslator, ten helper by se přidal jinak.
EDIT2: Možná by se všechno (filtry, helpery, makra) mohlo přidávat skrz tyhle služby. TemplateFactory by pak jako jedinou závislost měla pole těch konfigurátorů a možná templateCacheStorage.
EDIT3: A ještě jedna střela od boku, možná úplná blbost – připravit jednu šablonu interně v té továrně a poté vracet klony.
Všechno co tu teď píšu jsou momentální nápady nad kterými jsem zatím moc nepřemýšlel tak to berte s rezervou. ;-)
Editoval enumag (5. 2. 2014 13:52)
- Tomáš Votruba
- Moderator | 1114
@enumag: Jsi rychlý jak blesk.
4. Takto nějak?
6. To už je na mne moc složité, nechám na ostatních.
+8. Ještě zvažuji dekompozici metod uvnitř factory. Už teď je to pro
mne nepřehledné. Něco jako setupMacros()
,
setupHelpers()
, setupFilters()
,
setupControl()
, setupPresenter()
a
setupPaths()
.
- David Matějka
- Moderator | 6445
@enumag: tohle vypada jako reseni :) zavislosti, helpery apod. by se pridavaly dobre, komplikovanejsi by to bylo s makrama.. ale to by se snad nechalo vyresit…
edit: nebo co takhle pridavat makra pres compiler extension? podobne jako to kdyby resi treba s IDatabaseTypeProvider ?
Editoval matej21 (5. 2. 2014 14:01)
- David Matějka
- Moderator | 6445
@Tomáš Votruba: user by taky nemusel byt zavisly na presenteru, nechal by se klasicky injectnout Nette\Security\User
- enumag
- Člen | 2118
@Tomáš Votruba:
4. Ano, takto.
8. Konfigurátory, které navrhuji by to asi vyřešily lépe.
Jsi rychlý jak blesk.
Jo, někdy až příliš… Mluvím/píšu dříve než si to stihnu promyslet.
@matej21:
Jo, makra to trochu komplikují… tam bude asi lepší ten provider jak píšeš.
Se službou User máš samozřejmě pravdu, nevšiml jsem si. :-)
Nějak se vracím ke 3 dny starému závěru – to RFC na TemplateFactory budu muset stejně napsat. :-D
Editoval enumag (5. 2. 2014 14:04)
- Tomáš Votruba
- Moderator | 1114
@matej21: User přesunut
Registrace maker je v poho, stačilo přidat Nette\Latte\Compiler.
Osobně bych byl spokojen, kdyby v šablonách pro presenter, komponentu a
email fungovaly helpery, makra a odkazy n:href
a
{link Presenter:action}
.
Napadá mne, že i presenter lze injectnout. Tím by tato továrničky byla zcela nezávislá.
public function __construct(Nette\Application\Application $application)
{
$this->presenter = $application->getPresenter();
}
Teď to nechám chvíli vychladnout. Stále prosím zkuste přicházet s konkrétními návrhy, lépe pull-requesty.
- David Matějka
- Moderator | 6445
@Tomáš Votruba:
Registrace maker je v poho, stačilo přidat Nette\Latte\Compiler.
jako kam? jestli do ITemplateConfigurator, tak rozhodne ne. sablona by nemela byt tolik zavisla na latte, krom toho se latte kompilace neprovadi tolikrat a ve stejnou dobu jako vytvareni sablony atd. jedine jako samostatnou sluzbu, ILatteConfigurator, ktery by se staral o registraci maker
Napadá mne, že i presenter lze injectnout. Tím by tato továrničky byla zcela nezávislá.
kdyz bezi aplikace, muze projit nekolika presenterama, at uz proste obycejny
error presenter, nebo rucni zmena pomoci ->forward()
apod. takze
by se minimalne musel vytahovat ten presenter vzdy pri vytvareni sablony. ale
stejne jsem proti tomu :) az se refaktoruje generovani linku a vytvori se pro to
samostatna sluzba, nebude potreba dostavat presenter do kazdy sablony