Custom template class for presenter and control (experimental feature)

17 days ago

David Grudl
Nette Core | 6928
+
+16
-

By default, $template in controls and presenters is object of type Nette\Bridges\ApplicationLatte\Template. Any variables can be inserted into it because it behaves like a plain PHP object (like stdClass is used).

However, it is very easy to provide a code completion in PHPStorm, it describes this two-year-old article on Nette blog.

/**
 * @property ArticleTemplate $template
 */
class ArticlePresenter extends Nette\Application\UI\Presenter
{
	function renderDefault()
	{
		$this->template->lang = 'cs';
		...
	}
}

/**
 * @property string $lang
 * @property int $page
 * @property string[] $menu
 * @property Model\Page $article
 */
class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
}

At the moment, @mesour is finishing a new version of the PHPStorm Latte plugin, where the code completion will also work. It will be a big bomb! The testing version is here, you have to join Slack.

You will only have to add the template class name to the template:

{templateType App\Presenters\ArticleTemplate}

{$lang}
....

Strict Template

In the master of nette/application is implemented a new way how to work with custom templates.

First, you can simply change the class of $template. The easiest way is to follow the convention and create a class with the same name as the presenter/control class, only with Template suffix instead of Presenter or Control. As in the case of ArticleTemplate and ArticlePresenter. Then Nette uses it automatically.

Second, instead of inheriting from a dynamic class Nette\Bridges\ApplicationLatte\Template, we can use a strict class Nette\Bridges\ApplicationLatte\StrictTemplate that uses Nette\StrictObject. So we will really have to define all properties:

class ArticleTemplate extends Nette\Bridges\ApplicationLatte\StrictTemplate
{
	public string $lang;
	public int $page;
	/** @var string[] */
	public array $page;
	public Model\Page $article;
}

If we forgot to define a property, or if we mistyped the name, an exception will pop up.

17 days ago

dkorpar
Backer | 79
+
+2
-

Best thing ever. I've been searching for this to happen and implement for a long long time: https://github.com/…te/issues/31#…

This is something that boost framework quality to new dimension.

15 days ago

David Šolc
Member | 4
+
0
-

I think, that it is pretty common to put different variables into templates (in one Presenter for different templates/render methods/latte files). Is automatic use of one custom Template class for the whole presenter ok?

The easiest way is to follow the convention and create a class with the same name as the presenter/control class, only with Template suffix instead of Presenter or Control. As in the case of ArticleTemplate and ArticlePresenter. Then Nette uses it automatically.

To be more strict I would like to create Template class for each of these instead of one for the whole presenter. When I saw Nette\Bridges\ApplicationLatte\StrictTemplate I was immediately thinking about creating and using these for each render method/latte file, so I can create these and give them to frontend developer. He is not going to need to look into presenters – which variables are put into templates, but he will click through from latte file to custom Template class. When we take into account new possibilities of Latte plugin for PHPStorm. I can force a latte file to use a Template class via “{templateType xxx}” and make suggestions.

class ArticleDefaultTemplate extends Nette\Bridges\ApplicationLatte\StrictTemplate
{
	/** @var Model\Article[] */
	public array $articles;
}

class ArticleDetailTemplate extends Nette\Bridges\ApplicationLatte\StrictTemplate
{
	public Model\Article $article;
}


class ArticlePresenter extends Nette\Application\UI\Presenter
{
	function renderDefault(): void
	{
		/** @var ArticleDefaultTemplate $template */
		$template = $this->createTemplate(ArticleDefaultTemplate::class);
		$template->articles = $articles;
		$this->sendTemplate($template);
	}

	function renderDetail(): void
	{
		/** @var ArticleDetailTemplate $template */
		$template = $this->createTemplate(ArticleDetailTemplate::class);
		$template->article = $article;
		$this->sendTemplate($template);
	}
}
// Article.default.latte
{templateType ArticleDefaultTemplate}

{foreach $articles as $article}
  {$article->getName()}
{/foreac}

// Article.detail.latte
{templateType ArticleDetailTemplate}

{$article->getName()}

Last edited by David Šolc (2020-01-06 17:15)

15 days ago

David Grudl
Nette Core | 6928
+
0
-

You described it absolutely accurately. I just add that in this case there will be no need for /** @var ArticleDetailTemplate $template */.

13 days ago

David Grudl
Nette Core | 6928
+
+3
-

In the last dev versions of Latte and nette/application is a new macro {templatePrint ClassName}, which generates the blueprint of template class. Output looks like this:

class Template extends Nette\Bridges\ApplicationLatte\Template
{
	public \App\Module\Wiki\Presenters\ViewPresenter $presenter;
	public string $mediaPath;
	public array $langs;
	public string $lang;
	public \App\Module\Wiki\Model\Page $page;
	public array $sideBar;
}


/**
 * @property \App\Module\Wiki\Presenters\ViewPresenter $presenter
 * @property string $mediaPath
 * @property array $langs
 * @property string $lang
 * @property \App\Module\Wiki\Model\Page $page
 * @property array $sideBar
 */
class Template extends Nette\Bridges\ApplicationLatte\Template
{
}