DEV Community

david duymelinck
david duymelinck

Posted on

What do we want to solve with a template engine?

While I'm a big fan of template engines, a while ago I wanted to challenge my fandom with reality to see if it still makes sense.

The reasons I'm in favor of a template engine;

  • As little logic as possible in templates.
  • Escaping data
  • Composable HTML sections

My favorite template engine is Twig. I don't like Blade because the templates are .php files. Is is too tempting to add PHP code in the templates, and that is what a lot of people do.

Lets start the challenging

Escaping data

The default in template engines is escape all data or use a filter/other delimiters if you don't want the data to be escaped.
The question is are there scenarios where you want not escaped data in a template to be escaped?
I can't remember a case where I answered yes on that question.
The data that is not escaped has been checked for malicious content before it is stored.

So at the moment I'm thinking a template engine should trust the data coming from the application.

As little logic as possible in templates

It is a common practice to use filters or functions to transform data in a template. In Drupal I made it a habit to use a preprocess function to manipulate the data to make as frictionless as possible to use it in the template. And in the template I add the variables as a comment with the type and their shape when it makes sense.

I think the preprocessing should also happen when using a template engine. This makes the template engine less bloated if the engine forces you to add every filter or function to the engine. And for engines that allow you to add filters or functions to the individual templates, they can be cleaned up.

The thing I'm struggling with is, should the preprocessor also add metadata the template can use. For instance when there is an array should you add the count of that array as a variable.

{% if users|length == 0 %}

{% elseif users|length == 1 %}

{% else %}

{% endif %}
{# versus #}
{% if usersCount == 0 %}

{% elseif usersCount == 1 %}

{% else %}

{% endif %}
Enter fullscreen mode Exit fullscreen mode

As a developer it feels more natural to use the usersCount variable because I want to to keep the function count as low as possible.
On the other hand this feels like I want to move template logic to the application.

{% set usersCount = users|length %}
{% if usersCount == 0 %}

{% elseif usersCount == 1 %}

{% else %}

{% endif %}
Enter fullscreen mode Exit fullscreen mode

The example above makes the most sense to me. But it clashes with wanting to remove filters from the template engine.

Composable HTML sections

This is the core of the template engine for me. I have no preference for the syntax, using pseudo-tags or the extends and block pattern.

Creating a consistent solution

Every template has to be renderable and the engine should be replaceable.
Not all templates need preprocessing or data transforming so those should be optional.

namespace Template

interface PreProcessable
{
   public function preprocess(): mixed;
}

interface Transformable
{
   public function transform(): mixed;
}

interface Template
{
   public function render(): string;
}

interface PreProcessedTemplate implements Template, PreProcessable
{}

interface TransformedTemplate implements Template, Transformable
{}

interface PreProcessedTransformedTemplate implements Template, PreProcessable, Transformable
{}
Enter fullscreen mode Exit fullscreen mode

I would use a class to make the instance creation easier, but there are other developers that prefer traits to achieve the same result. So I'm going to leave it with the interfaces.

Conclusion

I'm amazed that over the years I pushed two of the three features I think of when I'm thinking of template engines out of the template scope.

I liked the evaluation of what is application logic and what is template logic. Over the years it seems the line was slowly fading in either direction.

With this knowledge I will see how I approach templates in the future.

Top comments (0)