Experimental Project

This is a sandbox project, which contains experimental code for developer use only.

The drupal 7 implementation of TWIG template language.

Updated 22-11-2012 for version 7.0.2.0

This project adds TWIG as a second theme engine to drupal 7. This project is mostly intended for those who want to use twig in D7 and are not afraid to move files into the correct place in their drupal installation. Don't worry to much, it's only folder and one file.

Beware this version is not 100% downards compatible with the previous version.

Changes

  • Compiled templates are now stored in a private folder. Be sure to set one at
    admin/config/media/file-system
  • Removed the old custom function call node as the functionallity is now
    available native in TWIG
  • Rebuild the way templates are discovered and internally stored.
  • Added drupal_static construction to the disk expensive calls.
  • Moved the autoloader into the project, patching of the config is not needed.

General information and installation

For more information about Twig, visit http://twig-project.org

To use this engine you will need to clone Twig from git
(https://github.com/fabpot/Twig.git) and manually copy the contents of /lib/Twig
into sites/all/libraries in a folder called Twig

The contents of TFD from this module needs to be moved into libraries as well.

So you end up with a folder structure like this.

sites/all/libraries/Twig/
sites/all/libraries/Twig/Error/
sites/all/libraries/Twig/Extension/ ... etc
sites/all/libraries/TFD/Autoloader.php
sites/all/libraries/TFD/Loader/ ... etc

Then move the twig.engine file into ./themes/engines/twig/ so that is on the
same level as the phptemplate engine drupal comes with.

Usage

To create a theme for twig, simply set engine = twig in your theme.info and
start creating templates

Twig for Drupal 7

The implementation of this Twig engine is purely for Drupal 7. The implementation of Drupal 8 most likely will be different from this!
However most extensions should be compatible with Drupal 8 as they are done as an extension of Twig instead of Drupal.

This is a 100% compatible drop in replacement for the default drupal phptemplate system, with one restriction due to a core bug that is unsolvable at this moment because of the backportability and structural changes between Drupal 7 and 8. You can NOT extend a phptemplate based theme with a twig theme. See http://drupal.org/node/1545964 for more information. However drupal modules that do not provide twig based templates will work as long as the template is within the module folder. If you want to extend or change the modules default template you MUST convert it to a twig template within your theme.

By convention twig for drupal templates are postfixed with .tpl.twig

Installation.

TODO

Usage

My first twig theme.

Declaring a theme as a twig theme works exactly the same as with a phptemplate based theme. Start with creating a folder in your themes structure, add a .info file and a tempate.php if needed. The only difference is, instead of engine = phptemplate you use engine = twig

For examplae

name = Twiggy
description = a cool twig based theme
engine = twig
core = 7.x

There is nothing more needed to use twig as engine.

Twig for Drupal specific extensions

Besides the default twig tags, filters and functions this version comes with a few extra bells and whistles that can make taming the drupal theming layer a bit easier.

Including and extending templates

One of the cool features of twig is the extendability of templates. The behaviour is adapted to drupal using a symfony2 like aproach of resolving templates within the current theme and the base theme if needed. Please note, that loading templates from other themes then the current enabled theme and the base theme for that theme is not supported!

The golden rule to remember is, every '/' in the path from 'templates' is replaced by a ':'
And the base theme is referred by it's machine name seperated by '::'

Asume you have a theme called twiggy that extends mothertwig and all templates are in the /template folder of that theme.

Loading a template from the current theme 'template' folder.

{% include 'node-default.tpl.twig' %}

Loading a template from a subfolder called pages as seen from the current theme 'template' folder

{% include 'pages:node-default.tpl.twig' %}

Loading a template from the base theme 'template' folder

{% include 'mothertwig::node-default.tpl.twig' %}

Loading a template from a subfolder called pages as seen from the base theme.

{% include 'mothertwig::pages:node-default.tpl.twig' %}

The template locations are cached, so if you add a new template, the normal routine of clearing the theme cache in drupal is required!

Tags

with

The 'with' tag allows a scope-shift into a defined array. The format is as follows:

The with construct sets the argument as the current context. If an 'as name' is defined,the variable is defined as that local name.
The two flags sandboxed or merged define an additional behaviour, allowing to sandbox the contents of the construct from the current scope (which results in having only the defined variables in the current scope), or merging them with the current scope respectively.

If none of the flags is defined, the current context is stacked in the context as _parent. After the end of the with construct, thecontext is restored.

Usage :

{% with expr [as localName] [, expr2 [as localName2], [....]]  {sandboxed|merged} %}
    content
{% endwith %}

Assume the following context:

array(
   'foo' => array(
      'name' => 'Foo',
      'id'   => 1
   ),
   'bar' => array(
      'name' => 'Bar',
      'id'   => 2
   )
)
{% with foo %}
   {{ id }}: {{ name }} {# would output: "1: Foo" #}
{% endwith %}                                               
{% with foo as baz %}
   {{ baz.id }}: {{ baz.name }} {# would output: "1: Foo" #}
{% endwith %}                                               
{% with foo as bar, bar as foo %}
   {{ bar.id }}: {{ bar.name }} {# would output: "1: Foo" #}
   {{ foo.id }}: {{ foo.name }} {# would output: "2: Bar" #}
{% endwith %}

This tag originally was contributed by Gerard van Helden

switch

The 'switch' tag allows a switch case construction in your template. By default the case constructs are not fallthrough, which means that there is no need to break from a switch like you normally would in PHP. You can use the fallthrough parameter at a case to overrule the default breaking behaviour. Default is the non obligatory catch all constructor. The case constructor is case sensitive!

{% switch bar %}
    {% case 'beer' %}
        Here is a cold beer.
    {% case 'water' %}
        The tap is in the toilet
    {% case 'howdie' fallthrough %}
        Hello stranger,
    {% case 'hello' %}
        What do you want to drink?
    {% default %}
        say what?
{% endswitch %}

'beer','water' and 'hello' produce a single line, yet 'howdie' produces both the outcome of the 'howdie' and 'hello'
And if bar is empty or none of the other case, it will produce 'say what?'

This tag originally was contributed by Gerard van Helden

unset

Unset removes a variable from the current context, it is the opposite of the twig set tag.
Usefull if you assigned a local scope set and want to remove that for later use in the template

{% set foo = 'bar' %}
    do something with foo
{% unset(foo) %}

Filters

defaults

Returns a merged array of defaults upon the variable the filter is called.

{{ foo|defaults({'bar','baz'}) }}

dump()

dumps the variable in a smart way. The dump filter checks if the 'devel' module is loaded, and if so by default does a kpr($variable); else it uses the PHP var_dump($variable)

However you can specify the method to use by a single parameter.

Default PHP:

  • dump('v') maps to var_dump();
  • dump('p') maps to print_r();

Devel module powered:

  • dump('k') maps to kpr();
  • dump('r') maps to dpr();
  • dump('d') maps to dpm();

Please note, by using a parameter the method is forced, yet checked if callable. So using dump('d') when the devel module is not enabled will not fallback and will print nothing.

url

Maps a variable to the drupal url() and checkurl() methods. When the variable is numeric, it is asumed to be a node and prepended by /node/ before passing it to the checkurl() method.

<a href="{{node.nid|url}}">click me</a>

will produce a fully path_auto extended url to the given node.

<a href="{{'<front>'|url}}">home</a>

will produce a link to the frontpage.

See the drupal documentation for the options you can use as parameters.

t

A one to one mapping to the drupal t() method.

ucfirst

A one to one mapping to the php ucfirst() method.

children

Removes al the '#meta' data from the first level of a 'render array of doom'.

date_format

A strongly discouraged in favour of a proper field fieldformatter unix timestamp to date filter.

Usage: |date_format('format','method').
Where method is either date or strftime and the format is a apropriate format for the used function.
Defaults to strftime('%d-%m-%Y %H:%M')

Functions

render

A 'smarter' render function

If you want to render just the contents of a single value field, you normally would do
something like this {{ r(field.foobar[0]) }} because of the field wrappers.
But typing the extra [0] can be a bit a over-verbose.
This render method checks if the array you pass has a single [0] key, and if so pass that to the render() method.
in any other case the whole array is passed trough.

For extra RSI preventing convenience a shortcut version {{ r() }} is available to.

hide

A 100% mapped version of the drupal hide() method. Unlike the above mentioned render this function does nothing smart.

For extra RSI preventing convenience a shortcut version {{ h() }} is available to.

dump

The function equivalent of the above documented dump filter. Parameter and usage are the same.

{{ dump(var,'method') }}

flatten

Flattens a typical drupal select field array into a more usable format.

input  = array( 0 => array ('value' => 1),
               1 => array ('value' => 4)
            )                                                             
output = array(0 => 1 ,1 => 4)

theme_get_setting

A one on one mapping to the drupal theme_get_setting(); method.

module_exists

A one on one mapping to the drupal module_exists(); method.

Tests

Test are mostly used in {% if constraints %}

property

Checks if the given property excists in a 'render array of doom'.

     $var = array("#theme" => 'aaaa')
      {% if var is property('theme') %}
        do something
      {% endif %}

numeric

checks if the given value is a number.

convenience mapping

For developers convenience, the twig default binary or & and are mapped to || & && however the original style of using them in a if function is strongly encouraged.

HOOKS (API)

All drupal hooks are called upon rebuilding of theme registry and thus on the 'cache clear all' call in Drupal

hook_twig_token

Add your own tokens (tags) to the tokenparser. These new tokens should extend the Twig_TokenParser.
In most circumstances you will actually never extend the tokenparser as this is a language construct rather then extending display logic.
Also

See twig documentation for more information on creating your own tokenparser.

module_twig_token($token){
    $token[] = new My_Twig_Token();

}

Project Information