Bamboo Twig, Beyond Twig in Drupal 8

Kevin Wenger
8 min readSep 22, 2018

--

The idea behind Bamboo Twig comes from a need to enhance layout management through code in Drupal 8. These best practices and codes are often needed and allow easy template fragmentation while keeping projects maintainable and scalable.

During a project development, there is a crucial rule to follow: Decouple code to ensure reliability and ease maintainability. With a low coupling code, making a change in a module won’t affect other modules, and avoid having to change the implementation of other modules using the same resources.

Drupal 8 excels in this domain: it allows you to generate independent blocks, to create entities (Nodes, Taxonomies, Comments, Files, …) for the content, to link fields to them or even to add them some ViewModes (Teaser, Card, Full, …). These features fill one objective: decouple business logic by favouring small components. The whole Drupal Core is written according to this philosophy.

Although, even if the whole system is developed following a decoupled and independent model, Drupal 8 has big shortcomings in it’s Twig implementation. The Core exposes only around 10 tools for Twig, which gives it an extremely low extensibility and reliability level.

Maintaining via code is easier than clicking.

One of the most flagrant example is the block management. It is impossible to manage different blocks without using a monolithic visual interface, Block Layout. Using this, we are quickly forced to maintain a whole lot of blocks that appear on few pages only. That forces us to keep block display logic inside the Drupal configuration. This becomes a real nightmare when the website grows in size and customisation.

Without falling in the “Business Logic into Frontend” paradigm, this feature is made, in my opinion, to manage the main blocks of your CMS such as Header, Footer, Breadcrumbs, Menus… As for the other blocks, it would be so much more agreeable, maintainable and scalable to handle their call directly from their associated contexts, in other words: from their Twig template.

Here is how is born the Bamboo Twig module.

What is Bamboo Twig and how does it work?

Layout like a boss

Bamboo Twig will plug directly into the Drupal 8 Twig system, and will extend its functionalities with a multitude of additional Functions and Filters.

My approach was to use submodules to allow you to activate the features you need, instead of bloating your Twig environment with the full toolkit, which you might not want. The activation of many dozens of Twig Functions and Filters could also have a non-negligible impact on performances. So follow my advice and only activate the sub-modules you really use. To install Bamboo Twig, launch the command composer require drupal/bamboo_twig and activates the submodules one by one!

Write programs that do one thing and do it well.
- Peter H. Salus in A Quarter-Century of Unix (1994)

So one of the main feature of this module is its capacity to render ViewModes of an entity or Block directly via a Twig Function. I will not list you the whole range of features inside of Bamboo Twig, as there are more than 25 Filters and Functions, and let you discover them directly in the official documentation. But here is a quick list of the main features :

Renderer

This module allows you to handle the rendering of all Drupal 8 Objects. Entities, Fields, forms, all the way down to a specific block region, everything can be rendered using the internal Drupal 8 logic.

The most common use case being the rendering of a specific entity. For example, Render a teaser for a Post type node.
This module is often use to render any Blocks, Forms or ImageStyles.

{# Render node with teaser viewmode #}
{% for article in articles %}
{{ bamboo_render_entity('node', article.nid.value, 'teaser') }}
{% endfor %}

{# Render the `system_powered_by_block` block #}
{{ bamboo_render_block('system_powered_by_block') }}

{# Render a the CronForm #}
{{ bamboo_render_form('system', 'CronForm') }}

{# Render the title of node 1 #}
{{ bamboo_render_field('title', 'node', 1) }}

{# Get thumbnail HTML markup from image with fid 12. #}
{{ bamboo_render_image(12, 'thumbnail') }}

{# Get thumbnail URL from image `public://antistatique.jpg`. #}
{{ bamboo_render_image_style('public://antistatique.jpg', 'thumbnail') }}

Loader

This module allows you to handle the loading of all Drupal 8 Objects. Unlike the Renderer which manages the display, the Loader is closer to the code by allowing to directly manipulate entities.

{# Load the current user #}
{% set user = bamboo_load_currentuser() %}

{# Load the entity node with nid 1 #}
{% set node = bamboo_load_entity('node', 1) %}
{# show the entity title in the current context lang (page language) #}
{{ node.title.value }}

{# Keep in mind, when loading an entity it will fetch it in the current context lang. #}
{# But When you will access a EntityReferenceField or Paragraph #}
{# the given entity is always in his own original language #}
{# (not in the current context lang neither in the entity lang). #}
{# You should then use the bamboo_i18n_get_translation filter. #}
{# show the entity name in his original lang #}
{{ node.field_referenced_tags.entity.name.value }}
{# show the entity name in the current context lang (page lang) #}
{{ node.field_referenced_tags.entity|bamboo_i18n_get_translation.name.value }}

Internationalisation

Drupal 8 offers its own implementation format_date in order to use \Drupal\Core\Datetime\DateFormatter, allowing date formatting while still using the internationalisation system. However, this one only works when receiving a Drupal\Core\Datetime\DrupalDateTime Object. The Bamboo Twig Filter is capable of processing a Sting, a Timestamp or a Date Object for a much simpler and flexible use.

<time datetime="{{ node.field_published_at.date|date('c') }}">
{{ node.field_published_at.date|bamboo_i18n_format_date('long_date_only') }}
</time>

{# Get the entity translations in the current context lang (page language) #}
{{ entity|bamboo_i18n_get_translation.title.value }}

{# Get the entity translations in french #}
{{ entity|bamboo_i18n_get_translation('fr').title.value }}

Files

This module expose on of the most useful Filters of Bamboo Twig: bamboo_file_url_absolute. Although the Drupal Core makes available the file_url Function, that generates a relative URL based on a URI, the core does not have a method to generate absolute URLs. bamboo_file_url_absolute meets this need.

{{ bamboo_file_url_absolute('public://drupal.jpg') }}

Twig Extensions

This module packages all the functionalities found in the very famous package “Twig Extension” — twig-extensions.readthedocs.io. Inside of it, you will find all the most useful filters and functions when developing with Twig. It also contains some other useful methods like strpad and some others.

{# String manipulations #}
{{ "This is a very long sentence."|bamboo_extensions_truncate(23, true, '...') }}

{# Array manipulations #}
{% set ar = {'a': 'apple', 'b': 'orange', 'c': 'citrus'}|bamboo_extensions_shuffle %}

{# Time diff #}
{{ '24-07-2014 17:28:01'|bamboo_extensions_time_diff('24-07-2014 17:28:02') }}

{# Pad a string #}
{{ "Alien"|bamboo_extensions_strpad(10, ' ') }}

Configurations

This submodule allows you to recover your configuration values that can come from three different configuration types include into the Drupal 8 Core: State API, Settings & Config API.

{# Get Settings #}
{{ bamboo_settings_get('hash_salt') }}

{# Get Config #}
{{ bamboo_config_get('system.site', 'mail') }}

{# Get State #}
{{ bamboo_state_get('system.cron_last') }}

Token

Full implementation of Token directly accessible in your Twig templates.

{{ bamboo_token('site:name') }}

{% set article = bamboo_load_entity('node', 1) %}
{{ bamboo_token('node:title', {'node': article}) }}

Security

Allows you to test roles or permissions for a given user, or the current one.

{# Does the current user has the given role/permission ? #}
{{ bamboo_has_role('anonymous') }}
{{ bamboo_has_permission('administre site configuration') }}

{# Does the current user has the given roles ? #}
{{ bamboo_has_roles(['authenticated', 'administrator']) }}
{{ bamboo_has_roles(['authenticated', 'administrator'], 'OR') }}

{# Does the current user has the given roles ? #}
{{ bamboo_has_permissions(['administre site configuration']) }}
{{ bamboo_has_permissions(['administre site configuration', 'bypass node access'], 'OR') }}

Stability & Testing

Why do most developers fear to make continuous changes to their code? They are afraid they’ll break it! Why are they afraid they’ll break it? Because they don’t have tests.”
- Robert C. Martin, The Clean Coder: A Code of Conduct for Professional Programmers

For all Open-source projects, it is crucial to respect the conventions and the Bests Practices of the used tool. To be the closest possible of the Drupal 8 conventions, whether it is in architecture or code implementation, we integrated several tools to automate Quality control, such as PHPMD, PHPCD, PHPCPD.

This module integrates more than 34 unit tests for a total of 89 assertions. This to guarantee a complete stability to module users and compatibility with the future versions of Drupal. We do not content with launching daily tests on the current version of Drupal 8 but we also launch them against the next major versions under development.

Why CI ?

To go the extra mile, we also have Github repository mirror] to integrate StyleCI and TravisCI for the continuous assessment outside of the Drupal environment. This allows us to have a better external contributions and code reviews management.

By using Bamboo Twig, you are certain that your code will be supported on all of the set of Drupal 8.x version, with a special attention towards Drupal 9.

The last word

Great power involves great responsibility.”
- Franklin D. Roosevelt, 32nd U.S. President

Bamboo Twig brings much more, whether it is for its Homogenisation ideology, for the constant contribution of tools or for its ease of use, it is, however, necessary to be vigilant: Bamboo Twig can be dangerous!

Indeed, when you use it, you will be capable of making a large number of operations directly from Twig templates and it will make you capable of the best as well as the worst.
Do not let yourself take bad habits and keep in mind that it is necessary to use this module sparingly, to avoid integrating logic into your templates or using wildly the functions and filter Twig to solve — Quick and Dirty — problems which sometimes deserve a bigger reflection and attention.

Use cases

Here are some use cases in which Bamboo Twig exposes its full potential:

{# Display a Block Entity #}
{# Render the `stark_messages` Block entity #}
{{ bamboo_render_entity('block', 'stark_messages') }}

{# Display un Block Plugin #}
{# Render the 'system_powered_by_block' block #}
{{ bamboo_render_block('system_powered_by_block') }}

{# Display the Node NID N°1 #}
{{ bamboo_render_entity('node', 1) }}
{{ bamboo_render_entity('node', 1, 'teaser') }}

{# Display a Paragraph #}
{{ bamboo_render_entity('paragraph', paragraph.target_id) }}

{# Display all Paragraphs, in field `field_paragraphs` #}
{% for paragraph in node.field_paragraphs %}
{{ bamboo_render_entity('paragraph', paragraph.target_id) }}
{% endfor %}

{# Display a Contact Form, in a field `field_contact_form` #}
{{ bamboo_render_entity('contact_form', node.field_contact_form.target_id) }}

{# Display a Form #}
{{ bamboo_render_form('system', 'CronForm') }}

{# Display a field #}
{{ bamboo_render_field('title', 'node', 1) }}

{# Display a image (fid 1) with a given ImageStyle #}
{{ bamboo_render_image(1, 'thumbnail') }}

{# Display an URL of image with a given ImageStyle #}
{# Get thumbnail from image 'public://antistatique.jpg'. #}
{{ bamboo_render_image_style('public://antistatique.jpg', 'thumbnail') }}

{# Display a Views #}
{{ bamboo_render_views('who_s_new', 'block_1') }}

{# Truncate a string #}
{{ "This is a very long sentence."|bamboo_extensions_truncate(2, false, '...') }}

{# Use Token #}
{{ bamboo_token('site:name') }}

{# Display date diff between two dates #}
{{ '24-07-2014 17:28:01'|bamboo_extensions_time_diff('24-07-2014 17:28:06') }}

Sources

The official documentation.

For the most curious of you, here are some complementary information sources that inspired the creation of this article.

Robert C. Martin (Juillet 17, 2008). Clean Code: A Handbook of Agile Software Craftsmanship.

Emmanuel Deloget (02 octobre 2014). Le principe “ouvert/fermé”. Retrieved from http://blog.emmanueldeloget.com/

Robert C. Martin (Juillet 17, 2008). Object Oriented Software Construction.

Fabien Potencier (Septembre 9, 2009). Découpler votre code pour assurer la réutilisabilité et la maintenabilité. Retrieved from http://slideshare.net/

--

--

Kevin Wenger
Kevin Wenger

Written by Kevin Wenger

Swiss Web Developer & Open Source Advocate @antistatique | @Webmardi organizer | Speaker | Author | Drupal 8 Core contributor

Responses (1)