Vlastní makro v Nette 2.2

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
tomaskrejci
Člen | 25
+
0
-

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
+
0
-

Diky, mohli byste mi ale prosim poradit trochu vic „polopaticky“? Kdyztak s nejakym prikladem reseni.

Diky za pochopeni.

David Kudera
Člen | 455
+
+1
-

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
+
0
-

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
+
+1
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
+2
-

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
+
0
-

Čisté Latte by control potřebovat nemělo, ale já tam používám UI\LatteFactory

Tomáš Jacík
Člen | 147
+
0
-

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
+
0
-

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 ;-)