Differences between Twig and Volt

I'm in the midst of writing a basic application using Phalcon & Volt and plan on comparing the performance with the same application, written using plain PHP and Twig. Whilst re-writing this application, I've noticed a few things which either work differently in Volt or do not work at all like Twig.

Consider the following layout and child template.

base.layout.volt

<html>
    <head>
        {% block head %}
        <title>{{ title | default('Base Layout') }}</title>
        {% endblock %}
    </head>
    <body>
        {% block content %}{% endblock %}
    </body>
</html>

child.template.volt

{% extends 'base.layout.volt' %}

{% set title = 'Child Title' %}
{% set heading = 'Child Heading' %}

{% block head %}
    {{ super() }}
    <style type="text/css">
        .message { color: #336699; }
    </style>
{% endblock %}

{% block content %}
    <h1>{{ heading }}</h1>
    <p class="message">
        {{ message }}
    </p>
{% endblock %}

The above works fine in Twig, however if you try to render this with Volt you'll receive an error message like:

Child templates only may contain blocks in..

This error is caused by setting the variable values title & heading outside a block definition. While I understand adding HTML content outside of a block definition would never be allowed, I don't understand why variables cannot be set this way? In my oppinion those variables title & heading are global and should be declared in the same scope as the view variables and override them if they exist.

The only alternative I can think of is to change the templates to something like below, however the way Twig handles declaring variables feels less restrictive. I'd love to see this changed or added as a feature to Volt if possible.

base.layout.volt

{% block variables %}{% endblock %}
<html>
    <head>
        {% block head %}
        <title>{{ title | default('Base Layout') }}</title>
        {% endblock %}
    </head>
    <body>
        {% block content %}{% endblock %}
    </body>
</html>

child.template.volt

{% extends 'base.layout.volt' %}

{% block variables %}
    {% set title = 'Child Title' %}
    {% set heading = 'Child Heading' %}
{% endblock %}

{% block head %}
    {{ super() }}
    <style type="text/css">
        .message { color: #336699; }
    </style>
{% endblock %}

{% block content %}
    <h1>{{ heading }}</h1>
    <p class="message">
        {{ message }}
    </p>
{% endblock %}
edited Jun '17

Hi Adam, well volt is a quite simple than twig but is pre compiled

Probably you have to define all the content variables in controller respecting little more mvc pattern

Good luck

To make your life a little easier, I think you could just define the variables in the head block, rather than creating a new variables block. Personally that's what I would do. It doesn't seem too cludgy either - the variables are being defined in the context in which they are being used.

Thanks both of you for your input.

Degiovanni Emilio Without boring you with all the inner details of what I'm trying to build, the end user is in control of what they create in the templates so defining all the variables up-front in the controller is not an option.

Dylan Anderson Yes you make a good point, however I think having a block called variables or global_variables makes the purpose of that block clearer to the end user.

edited Jun '17

After looking through the source code of Phalcon, I believe I've found a fairly elegant solution to my original question.

But first, I came across something I feel should be documented, which could be harmful for developers if they're not familiar with Phalcon.

Lets assume the developer is creating a templating system where the templates are created by the end user. The developer would like the end user to know nothing about the server the application is running on.

Now consider the following code (it's not completely functional but hopefully you get the point).

$container = new FactoryDefault();

$container->setShared('voltService', function($view, $container)
    {
        $configuration = $container->get('configuration');
        $volt = new Volt($view, $container);
        $volt->setOptions($configuration->voltOptions->toArray());

        return $volt;
    }
);

$container->setShared('view', function()
    {
        $configuration = $this->get('configuration');

        $view = new View();
        $view->setViewsDir($configuration->application->viewsPath);
        $view->registerEngines(
            [
                '.template' => 'voltService',
            ]
        );

        return $view;
    }
);

By passing an instance of Phalcon\Di to the Phalcon\Mvc\View\Engine constructor, it then makes the information set in the Di container accessible to templates. For example:

<html>
    <head>
        {% block head %}
        <title>{{ request.getHttpHost() }}</title>
        {% endblock %}
    </head>
    <body>
        {{ request.getRawBody() }}
    </body>
</html>

Having access to information in the Di container is definitely helpful when you're in control of the templates, but this can actually be quite dangerous if not. I'm guessing developers like myself who are new to Phalcon, would not be aware of this as it's not apparent in the documentation.



4.8k
edited Jun '17

Are there any big differences in Phalcon 3?

I am looking at content where you have data repeated. You might replace "March schedule" with "{{ month }} schedule". If {{ month }} is used in many places and is now loaded from schedule_month_display, you might want {% set month = {{ schedule_month_display }} %} at the top of the template.

Using the example above, I would replace ".message { color: #336699; }" with ".message { color: {{ message_colour }}; }" and set message_colour up the top.



4.8k

One difference between Twig and Volt is the documentation for use stand alone. Almost everything about the Volt compiler assumes you are in a view.