Custom template class for presenter and control (experimental feature)
- David Grudl
- Nette Core | 8227
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}
....
Real Templates
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.
Update: I have found a solution to simplify the whole
system, so that there is no need to think about which template ancestor to
inherit. StrictTemplate
will not exist.
Real Templates II.
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.
It is up to you to decide whether the class should be strict. This will be
done by adding use Nette\SmartObject
.
So template class should look like this:
class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
// use Nette\SmartObject; it is up to you
public string $lang;
public int $page;
/** @var string[] */
public array $page;
public Model\Page $article;
}
- dkorpar
- Member | 136
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.
- David Šolc
- Member | 5
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)
- David Grudl
- Nette Core | 8227
You described it absolutely accurately. I just add that in this case there
will be no need for
/** @var ArticleDetailTemplate $template */
.
- David Grudl
- Nette Core | 8227
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
{
}
- dakur
- Member | 493
@DavidGrudl
It is up to you to decide whether the class should be strict. This will be done by adding use Nette\SmartObject.
Am I wrong or is side-effect of adding SmartObject
that it
enables magic methods __get()
/__set()
as well? Isn't
there some object in Nette which ensures only strictness? Currently I am not
able to write strictly and without magic, which is probably the most clean way
(academically). Thank you.
Last edited by dakur (2020-03-09 13:11)
- Marek Bartoš
- Nette Blogger | 1274
I would also like to see e.g. StrictObject, which would only implement ObjectHelpers::strictSet() etc, without magic properties and events, just to fix php behavior. Maybe even without support for unsetting properties
This package should have very similar behavior https://github.com/…trictObjects
Last edited by Mabar (2020-03-09 13:19)
- David Grudl
- Nette Core | 8227
@dakur: Strictness is just achieved using __get()/__set()
methods. There is no other way to do this in PHP yet.
@Mabar: magic properties and events must be explicitly allowed. Unlike Nette\Object, SmartObject does not allow you to use magic properties unless you enable it directly. It is therefore unnecessary to have two traits. It would be confusing for users.