Vlastní makro v Nette 2.2
- tomaskrejci
- Člen | 25
Ahoj,
muzete mi prosim poradit jak vytvorit vlastni makro v Nette 2.2? Pokud to delam
„postaru“, tak mi to vrati
Nette\Bridges\ApplicationLatte\Template::registerFilter() is deprecated.
Snazim se to tu dohledat, ale nikde jsem nenalezl komplexni odpoved.
Diky moc.
- tomaskrejci
- Člen | 25
Diky, mohli byste mi ale prosim poradit trochu vic „polopaticky“? Kdyztak s nejakym prikladem reseni.
Diky za pochopeni.
- David Kudera
- Člen | 455
no.. myslím, že úplně nejjednodušší je, je registrovat tak, jako to dělá samo nette.. Tohle je bych řekl dobrý příklad.
A když už mám takovou třídu vytvořenou, tak ji takhle nehezky zaregistruji v configu, aby to makro bylo dostupné všude (možná že to není ideální mít to globální, ale u maker dělám osobně výjimku).
nette.latteFactory:
setup:
- 'App\MyMacros::install(?->getCompiler())'(@self)
- Tomáš Jacík
- Člen | 147
Dnes jsem si také potřeboval vyrobit vlastní makro. Nakonec jsem to zaegistroval takto:
public function templatePrepareFilters($template)
{
\App\LatteMacros::install($template->getLatte()->getCompiler());
}
MacroSet pak vypadá takto:
class LatteMacros extends Latte\Macros\MacroSet
{
public static function install(Latte\Compiler $compiler)
{
$me = new static($compiler);
$me->addMacro('staticUrl', array($me, 'macroStaticUrl'));
}
public function macroStaticUrl(MacroNode $node, PhpWriter $writer)
{
$node->modifiers = preg_replace('#\|safeurl\s*(?=\||\z)#i', '', $node->modifiers);
return $writer->using($node, $this->getCompiler())
->write("echo %escape(\App\LatteMacros::calculateHash(\$_presenter, %node.word))");
}
public static function calculateHash($presenter, $url)
{
// $wwwDir = $presenter->context->parameters['wwwDir'];
$wwwDir = '/home/web/shop/';
$url_min = explode('.', $url);
$url_min[sizeof($url_min)-1] = 'min.' . $url_min[sizeof($url_min)-1];
$url_min = implode('.', $url_min);
$hash = apc_fetch('static_file_hash_' . $url_min);
if ($hash)
{
return '/static/' . $url_min . '?v=' . $hash;
}
$hash = apc_fetch('static_file_hash_' . $url);
if ($hash)
{
return '/static/' . $url . '?v=' . $hash;
}
$file = $wwwDir . 'static/' . $url;
$file_min = $wwwDir . 'static/' . $url_min;
if (!is_file($file))
return $url;
if (is_file($file_min))
{
$url = $url_min;
$file = $file_min;
}
$content = file_get_contents($file);
$hash = hash("crc32b", $content);
apc_add('static_file_hash_' . $url, $hash);
$content = NULL;
unset($content);
return '/static/' . $url . '?v=' . $hash;
}
}
Řeším ale trochu jiný problém. Jak do toho volání macroStaticUrl dostat presenter (potřebuju basePath a wwwDir) aby to nebyla prasárna? Takto když mám funkci kterou volám v šabloně tak tam z šablony ten presenter dostanu, ale HttpRequest z něj vytáhnout nejde.
Tu funkci calculateHash prosím berte jako pracovní verzi :-) Rád bych se jí úplně zbavil a řešil to přímo v macroStaticUrl když přijdu na to jak tam dostat potřebná data. Ideálně kdyby ten MacroSet šel nějak použít, když bude služba.
- David Kudera
- Člen | 455
No osobně bych řekl, že volání takových maker může bý úplně jednoduchý. Prostě jen předat argumenty do nějaké služby, která zbytek vykoná. Takže to calculateHast (nebo cokoliv v budoucnu) bych jednoduše vyčlenil do nějaké služby, kde si jednoduše nechám injectnout HttpRequest.
Otázka pak je, jak ve vygenerovaném kódu z makra přistoupit ke službě. Už se to tu párkrát řešilo, ale tak, že třeba přistoupím k contextu na presenteru přímo a vytáhnu si službu.. To se mi osobně nelíbí. Pak jsem ale našel, jak to řeší kdyby/translation. Je to sice na první pohled o něco složitější a zamotanější, ale líbí se mi, že nemusím přistupovat ke contextu a podobným věcem.
- TemplateHelpers si zaregistruje pár helperů (teď filterů)
- Přímo v Translatoru si nechá vytvořit instanci latte filterů, kde předá aktuální translator.
- Dál se filtry zaregistrují
- Taky se použije třída s makry přímo na latte
- No a nakonec jsou tu samotná makra
, kde jde vidět, že ty přistupují k
$template->nazevFilteru()
, teď by to jen v novým nette bylo jako$template->invokeFilter('nazevFilteru')
.
Výhoda je taky to, že např. u testů nemusím vytvářet celý presenter jako u těch maker, které přistupují v šabloně ke $context. Stačí mi v testech vytvořit šablonu, registrovat makra a helpery a je to :-)
Jen je to prostě složitější…
- Tomáš Jacík
- Člen | 147
Doufal jsem že to půjde jednodušejc než psát vlastní extension :-)
Mám pár dotazů:
- Kdy se volá TranslationExtension::loadConfiguration? Nebo si to pustí Nette samo, když tu extension zaregistruju v configu?
- Není to divné vytvářet si filter, abych ho mohl potom pou6ít v makru? To pak bude fungovat i zápis |translate v šabloně, ne?
- David Kudera
- Člen | 455
Tohle jde napsat i jen za použití neonu ;-)
- ano, loadConfiguration si volá nette samo
- a to je právě kvůli tomu, aby se nemuselo dělat v tom makru něco jako
$_presenter->context->getByType('app/mySuperService')->calculateHash(%node.args);
tohle není čisté, ale funguje to.. Špatný je to, že to sahá na context takhle skrytě a navíc v šabloně. Místo toho když se to udělá takhle složitě, tak je aspoň pocit, že je to nějak „čistě“ (aspoň u mě :-D). Navíc to nepotřebuje presenter a volání zůstává stejný jako u makra. To makro si totiž samo sáhne na filter
- Tomáš Jacík
- Člen | 147
Dobře, takto to chápu že je to čistější. Můžeš mě prosím ještě nasměrovat jak napsat ten test bez presenteru?
Jinak bych tedy rád věděl, jak to udělat jen za použití neonu :-)
Díky za pomoc.
- David Kudera
- Člen | 455
znovu zneužiji kdyby/translation: link . Akorát jsem zapomněl, že to sice nepotřebuje presenter, ale control jo :-D to je ostuda..
Každopádně takhle pro doplňky třeba je to ideální, protože by bylo zbytečný pro ně dělat testovací presenter a tak všechno.. A nejspíš vlastně v reálné aplikaci to jinak nikoho trápit nebude, takže. Jako bych s presenterem nic neřekl…
No a config:
services:
- App\SuperService\SuperService
-
class: App\SuperService\Helpers
create: @App\SuperService\SuperService::createTemplateHelpers
latte.factory:
setup:
- 'App\SuperService\Macros::install(?->getCompiler())'(@self) # vím, hodně nehezké
- addFilter('calculateHash', [@App\SuperService\Helpers, calculateHash])
je to jen z hlavy, ale snad by to mohlo jít
Edit: jo starší nette nepotřebovalo ani control:
$template = new FileTemplate;
$engine = new Engine;
$template->registerHelperLoader(array($superService->createTemplateHelpers(), 'loader'));
$template->registerFilter($engine);
Macros::install($engine->getCompiler());
v novým jsem to ale zatím nijak nezkoumal a jen použil z translatoru
Editoval David Kudera (29. 8. 2014 18:31)
- Filip Procházka
- Moderator | 4668
Čisté Latte by control potřebovat nemělo, ale já tam
používám UI\LatteFactory
- Tomáš Jacík
- Člen | 147
registerHelperLoader už je v novém Nette deprecated, ale s tím si snad nějak poradím.
Ještě jednou mockrát díky.
- David Kudera
- Člen | 455
Tomáš Jacík napsal(a):
registerHelperLoader už je v novém Nette deprecated, ale s tím si snad nějak poradím.
Ještě jednou mockrát díky.
To jsem jen poslal jako ukázku, která mi funguje na starším nette. V novým je to registrovaný automaticky přes to addFilter, co je přímo v neonu ;-)