Twig Templates

The Advanced Views plugin templates utilize the Twig engine, a modern and widely adopted template engine.

Benefits and Why we use Twig instead of PHP templates

For many developers coming from the old-school WordPress theme templates approach, the advantages of using Twig may not be immediately apparent.

First and foremost, it's important to understand that Twig is built on top of PHP, so it's essentially a PHP tool. When comparing Twig and PHP directly, it may seem that Twig has fewer capabilities than PHP. However, this is actually one of its strengths. Using Twig for templates is often better than the native PHP approach because Twig:

a) Enforces separation of Logic and Presentation

One of the most significant issues with classic PHP templates in WordPress is the tendency to mix logic and presentation within the same template file. If you examine templates in many traditional WP themes, you'll find that they contain not only presentation markup but also direct data retrieval and processing. This results in spaghetti code that is prone to bugs and challenging to maintain.

Twig was specifically designed for generating presentation templates. It confines you to the task of output generation and prevents you from mixing it with other tasks like data retrieval and processing.

c) Includes automatic auto-escaping

In WordPress, developers are required to manually escape variables when generating markup to prevent cross-site scripting (XSS) vulnerabilities. With Twig, you don't need to worry about escaping variables anymore. Twig automatically escapes all variables passed into the template by default.

If you have a specific case where you want to pass HTML content without escaping, you can do so by using the |raw filter in Twig, making it easy to handle both escaped and unescaped content safely.

Using Twig's automatic auto-escaping feature can help improve the security of your templates by reducing the risk of XSS attacks and making your code more maintainable.

b) Generates more clear and readable code

Even when using PHP templates exclusively for output generation, employing a template engine like Twig is still a preferred choice because it results in cleaner code. To illustrate, consider the examples below:

In PHP:

<?php foreach ($items as $item) { ?>
<div class="item">
    <p class="name"><?php echo esc_html($item['name']); ?></p>
    <p class="surname"><?php echo esc_html($item['surname']); ?></p>
    <a class="link" href="<?php echo esc_url($item['link_url']); ?>"><?php echo esc_html($item['link_title']); ?></a>
</div>
<?php } ?>

In Twig:

{% for item in items %}
<div class="item">
    <p class="name">{{ item.name }}</p>
    <p class="surname">{{ item.surname }}</p>
    <a class="link" href="{{ item.link_url }}">{{ item.link_title }}</a>
</div>
{% endfor %}

Even in this small example, it's evident that Twig leads to more readable code. Once you become accustomed to Twig, looking at real-life PHP templates with numerous variable injections will make PHP's verbosity, which necessitates opening and closing tags and using 'echo' repeatedly, seem cumbersome.

Note: We are aware of the short PHP echo syntax. While it's somewhat better than the PHP example above, it's still less elegant than Twig. Additionally, the use of the short echo syntax is discouraged in plugins and themes by the WordPress codex and may produce fatal PHP errors on some hosting environments if the specific php.ini setting is disabled.

d) Wide usage

Wide usage is a bonus that can motivate you to learn Twig if you're unfamiliar with it yet.

Twig is used in many well-known software, like Symphony framework, Drupal and October CMS. And even though it isn't employed in WordPress out-of-the-box, it's used in 105+ WordPress plugins.

So you can level up your skills by learning this amazing template system.

Built-in Twig features

If you're new to Twig, we provide an overview of its key features below.

You can launch the snippets and play with them in your browser using the Twigfiddle service.

Variables

Using double curly brackets, you can easily inject variables into your markup. For instance:

{{ my_variable }}

When working with arrays, follow this syntax:

{{ my_array.my_key }}

You can also define variables directly within a template:

{% set my_var = my_rate * 10 %}
{% set my_var = my_rate ~ " USD" %}

Auto-escaping

With Twig, you don't need to worry about escaping variables anymore because Twig automatically escapes all variables passed into the template by default.

However, in some specific cases, you may still need to pass HTML without escaping. For example, when displaying a WYSIWYG field value, you should use the |raw filter like this: {{ my_var|raw }}. Without the |raw filter, HTML tags will be converted into HTML entities and displayed on the page as plain text.

Math

Twig allows you to perform any mathematical operations within brackets. For example:

{{ 5 + 10 }}

This will output 15.

Conditions

You can create different markup based on various conditions. For instance:

{% if my_variable == 'some_value' %}
    <div> Markup if the condition is met </div>
{% else %}
    <div> Markup if the condition is not met </div>
{% endif %}

The 'else' part is optional.

You can also use odd and even for comparison, such as:

{% if my_var is odd %}
    <div> Markup for odd values </div>
{% endif %}

Loop

Twig provides a straightforward way to iterate through arrays:

{% for item in my_array %}
    <div>{{ item }}</div>
{% endfor %}

Additionally, you can access the current index using the loop.index magic variable, which starts at "1". This can be useful for detecting the first element or distinguishing between odd and even indices.

Filters

Twig offers a wide range of filters that you can apply using the pipe symbol.

Here are some examples:

{{ my_var|abs }} {# Returns the absolute value of a number. #}
{{ my_var|capitalize }} {# Capitalizes the first character of a string. #}
{{ my_var|raw }}  {# Outputs a string without escaping HTML entities. #}
{{ my_var|upper }}  {#Converts a string to uppercase. #}
{{ my_var|lower }}  {# Converts a string to lowercase. #}
{{ my_var|round([precision, method]) }}  {# Rounds a number to the nearest integer. #}
{{ my_var|range(low,high[,step]) }} {# Generates a range of numbers or letters. #}
{{ my_var|date("d/m/Y") }} {# Formats a date or time according to a specified pattern. #}
{{ my_var|date_modify("+1 day") }} {# Modifies a date or time by adding or subtracting a specified interval. #}
{{ my_var|default("Default Value") }} {# Provides a default value if the variable is undefined or empty. #}
{{ my_var|replace({"search":"replace"}) }} {# Replaces occurrences of a specified substring with another. #}
{{ my_var|random(from[,max]) }} {# Generates a random number or selects a random item from an array. #}

Click on a specific filter name on this page in the Twig Docs to access more detailed information.

Note: We have created a custom build of Twig (to securely integrate it with WordPress), so some filters from the official list may be missing. You can safely use the filters listed above or experiment with any filter from the official list and let us know if it doesn't function as expected.

Functions

Functions, unlike filters, serve different purposes and have distinct syntax.

The key distinction between filters and functions lies in how they handle variables. Filters modify the variable they are applied to, as in "var|round," while functions operate on arguments independently.

{{ date("now") or date("10/10/2010") }} {# Creates a Date object from string (usually, used for comparison) #}

Dates

When working with date filters and functions, it's important to consider the variable type. Strings containing dates and Date objects are distinct entities, and you should keep the following in mind:

  1. Date objects cannot be displayed directly; you need to convert them into strings for this purpose.

  2. When using conditional statements like {% if %}, you cannot directly compare date strings; you should first convert them into Date objects for comparison.

The "date" filter returns a string, whereas the "date_modify" filter and "date" function return Date objects. Refer to the information below for more details.

Date filter (for output)

Converts a Date object into a string based on the specified format. If you provide a string, the filter will automatically parse the date using strtotime and then convert it into a string in the requested format.

{{ "now"|date }} {# displays the current time #}
{{ my_date|date("Y") }} {# displays the year from the variable #}
{{ "10-10-2010"|date("d") }} {# displays the day from the string #}
{{ "+1 day"|date }} {# displays tomorrow's date #}

Date_modify filter (for modification)

Modifies a Date object according to the argument provided and returns the modified Date object. If you pass a string, the filter will automatically parse the date using strtotime.

{{ my_date|date_modify("+1 week") }} {# adds one week to the date #}
{{ "10-10-2010"|date_modify("+1 day") }} {# adds one day to the date #}

Keep in mind that this function returns a Date object, not a string. If you want to display the modified date, you should use the "date" filter to print the Date object in a specific format.

{{ my_date|date_modify("+1 week")|date("d/m/Y") }} {# modifies and prints the date #}

Date function (for comparison)

Converts a string into a Date object, which is useful for comparison purposes.

{{ date("10-10-2010") }} {# creates a Date object from a string #}
{% if date(first_date) > date(second_date) %}
{{ first_date|date("d/m/Y") }}
{% endif %}

Keep in mind that this function also returns a Date object, not a string. If you want to display the Date object, you should use the "date" filter to print it in a specific format.

{{ date("now")|date("d/m/Y") }}

Available date formats

  • you can use any formats with the |date filter

  • you can use most of the available date formats for the date() function. For example, "m-d-Y" (10-20-2011), "M d, Y" (October 20, 2011), or "d.m.Y" (20.10.2011).

Tip: When you use the date parsing functions and filters, we recommend using the '.timestamp' property of the date field. This will save you from dealing with date parsing issues. For example, both US (e.g., mm/dd/yyyy) and European (e.g., dd/mm/yyyy) formats use the same delimiter, so the European format will be ignored in favor of the US format. This behavior is in line with the default behavior of the strtotime function.

In this way using 'datepicker.timestamp|date("d-m-Y")' is the safe way which avoids it.

Comments

{# All text within these brackets is available only for editors 
and will be removed from the final output. #}

Our functions (Pro)

We extended Twig to include additional WordPress-related functions, in addition to its built-in functions. You can find these extended functions listed below.

Note: These functions are only available in Advanced Views Pro.

{{ _query_argument('my-category-id') }} 
{# retrieves the argument from the current page URL ($_GET in PHP) #}

{{ _is_user_with_role("role") }} 
{# checks if user has the specified role. 
You can pass user id as the second argument, or it'll use the current user id #}

{{ _is_user_logged_in() }} 
{# calls is_user_logged_in() from WP #}

{{ _site_url("/my-page") }} 
{# calls site_url() from WP #}

{{ _home_url() }} 
{# calls home_url() from WP #}

{{ __("Some label") }} 
{# translates the label. 
You can pass the text domain as the second argument, 
or it'll use the current theme's domain #}

Our filters (Pro)

{{ "Some label"|translate }} 
{# Translation filter, alternative to the '__' function above #}

Custom functions (Pro)

In the Pro version of the plugin, you can add your own functions.

add_filter('acf_views/twig/custom_functions', function (array $customFunctions): array {
    return array_merge($customFunctions, [
        [
            'prefixer',
            function ($firstArg) {
                return 'prefix2' . $firstArg;
            }
        ]
    ]);
});

add_filter('acf_views/twig/custom_functions', function (array $customFunctions): array {
    return array_merge($customFunctions, [
        [
            'prefixerWithHtml',
            function ($firstArg) {
                return '<strong>prefix</strong>' . $firstArg;
            },
            ['is_safe' => ['html',],]
        ]
    ]);
});

Note: every item of the array represents the custom function. First argument is the function name, second is the callback. Third argument is optional, here you can pass any function settings supported by Twig.

For the function callback, you can define as much arguments as you need.

Now you can call your functions in twig templates of your Views and Cards.

{{ prefixer(myVar) }}
{{ prefixerWithHtml(myVar) }}

Custom filters (Pro)

In the Pro version of the plugin, you can add your own filters.

add_filter('acf_views/twig/custom_filters', function (array $customFunctions): array {
    return array_merge($customFunctions, [
        [
            'prefixer',
            function ($value, $firstArg) {
                return $firstArg . $value;
            }
        ]
    ]);
});

add_filter('acf_views/twig/custom_filters', function (array $customFunctions): array {
    return array_merge($customFunctions, [
        [
            'prefixerWithHtml',
            function ($value, $firstArg) {
                return '<strong>'.$firstArg.'</strong>' . $value;
            },
            ['is_safe' => ['html',],]
        ]
    ]);
});

Note: every item of the array represents the custom filter. First argument is the filter name, second is the callback. Third argument is optional, here you can pass any filter settings supported by Twig.

For the filter callback, you can define as many arguments as you need.

Now you can call your filters in twig templates of your Views and Cards.

{{ myVar|prefixer("prefix") }}
{{ myVar|prefixer("prefix") }}

Last updated