How to use callbacks in services
- dsar
- Backer | 53
Hi guys,
I would like to have a sort of persistent/global variables in Latte (such as globals in twig for Symfony).
I used this solution (adapted to work with modern Nette) but I don't like it, it smells really bad :-)
In my opinion the right place is to use onCreate() of the templateFactory, but I'm not able to set the right syntax to make it work (unfortunately the documentation and unit testing files don't help much about it).
parameters:
globals:
testkey: testval
services:
[...]
latte.templateFactory:
setup:
- '@self::onCreate[] = function(){$template->setParameters(%globals%);}'
This (of course) doesn't work.
The problem is twofold: %globals% doesn't work in that kind of string, also
passing an array directly doesn't work because of syntax error (Expected
function, method or property name, 'onCreate[] […] given).
Any idea?
Thank you
- Marek Bartoš
- Nette Blogger | 1244
I would use CompilerExtension, programming in neon is not much flexible and
is error-prone. Your neon example would fail in case
of $templateFactory->createTemplate($control, NotADefaultTemplate::class);
namespace Example;
use Nette\Bridges\ApplicationLatte\DefaultTemplate;
use Nette\Bridges\ApplicationLatte\Template;
use Nette\Bridges\ApplicationLatte\TemplateFactory;
use Nette\DI\CompilerExtension;
use Nette\DI\Definitions\ServiceDefinition;
final class ExampleExtension extends CompilerExtension
{
public function beforeCompile(): void
{
parent::beforeCompile();
$builder = $this->getContainerBuilder();
$templateFactoryDefinition = $builder->getDefinitionByType(TemplateFactory::class);
assert($templateFactoryDefinition instanceof ServiceDefinition);
$templateFactoryDefinition->addSetup(
[self::class, 'doStuff'],
[$templateFactoryDefinition, $builder->parameters['globals'] ?? []]
);
}
/**
* @param array<mixed> $globals
*/
public static function doStuff(TemplateFactory $templateFactory, array $globals): void
{
$templateFactory->onCreate[] = static function (Template $template) use ($globals): void {
if ($template instanceof DefaultTemplate) {
$template->setParameters($globals);
} else {
foreach ($globals as $key => $value) {
if (property_exists($template, $key)) {
$template->$key = $value;
}
}
}
};
}
}
- dsar
- Backer | 53
Thank you, it works :-)
However with getDefinitionByType(TemplateFactory::class) gives Service of type ‘Nette\Bridges\ApplicationLatte\TemplateFactory’ not found but it does work with getDefinition(‘latte.templateFactory’).
Furthermore, $builder->parameters seems to be empty in this stage, but at this point for this case I prefer to use a separate configuration.
Here the working solution:
extensions:
latteGlobals: App\Extensions\LatteGlobalsExtension
latteGlobals:
testkey: testval
<?php
declare(strict_types=1);
namespace App\Extensions;
use Nette\Bridges\ApplicationLatte\DefaultTemplate;
use Nette\Bridges\ApplicationLatte\Template;
use Nette\Bridges\ApplicationLatte\TemplateFactory;
use Nette\DI\CompilerExtension;
use Nette\DI\Definitions\ServiceDefinition;
final class LatteGlobalsExtension extends CompilerExtension
{
public function beforeCompile(): void
{
parent::beforeCompile();
$builder = $this->getContainerBuilder();
//$templateFactoryDefinition = $builder->getDefinitionByType(TemplateFactory::class);
$templateFactoryDefinition = $builder->getDefinition('latte.templateFactory');
assert($templateFactoryDefinition instanceof ServiceDefinition);
$templateFactoryDefinition->addSetup(
[self::class, 'setGlobals'],
[$templateFactoryDefinition, $this->config ?? []]
);
}
public static function setGlobals(TemplateFactory $templateFactory, array $globals): void
{
$templateFactory->onCreate[] = static function (Template $template) use ($globals): void {
if ($template instanceof DefaultTemplate) {
$template->setParameters($globals);
} else {
foreach ($globals as $key => $value) {
if (property_exists($template, $key)) {
$template->$key = $value;
}
}
}
};
}
}
Last edited by dsar (2021-02-21 14:18)
- dsar
- Backer | 53
Fetching by type only works if I force the type as below:
services:
latte.templateFactory:
class: Nette\Bridges\ApplicationLatte\TemplateFactory
without that, it gives:
Service of type 'Nette\Bridges\ApplicationLatte\TemplateFactory' not found.
is that normal?
Last edited by dsar (2021-02-16 16:27)
- Marek Bartoš
- Nette Blogger | 1244
Created PR fixing this issue. And yes, it's normal – service is autowired
only by it's type (which is often deduced from factory, but in this case is
explicitly set to Nette\Application\UI\TemplateFactory
)