6. Extensions

SmartyBundle extensions are packages that add new features to Smarty. The extension architecture implemented in the SmartyBundle is an object-oriented approach to the plugin system available in Smarty. The implemented architecture was inspired by Twig Extensions.

Each extension object share a common interest (translation, routing, etc.) and provide methods that will be registered as a Smarty plugin before rendering a template. To learn about the plugin ecosystem in Smarty take a look at the Smarty documentation page on that subject.

The SmartyBundle comes with a few extensions to help you right away. These are described in the next section.

6.1. Actions Extension

This extension tries to provide the same funcionality described in Symfony - Templating - Embedding Controllers.

Following the example presented in the link above, the Smarty equivalents are:

Using a block function:

{render attributes=['min'=>1,'max'=>3]}AcmeArticleBundle:Article:recentArticles{/render}

Using a modifier:

{'AcmeArticleBundle:Article:recentArticles'|render:['min'=>1,'max'=>3]}

6.2. Assetic Extension

See chapter Assetic for complete documentation about Assetic support in SmartyBundle.

6.3. Assets Extension

Templates commonly refer to images, JavaScript, stylesheets and other assets. You could hard-code the path to these assets (e.g. /images/logo.png), but SmartyBundle provides a more dynamic option via the asset modifier:

<img src="{'images/logo.png'|asset}" />

or asset block:

<link href="{asset}css/blog.css{/asset}" rel="stylesheet" type="text/css" />

This bundle also provides the assets_version function to return the version of the assets in a package. To set the version see the assets_version configuration option in Symfony’s Framework Bundle.

Usage in template context:

{assets_version}

6.4. Form Extension

Form extension provides support for Symfony Forms and it is described in its own chapter. Go there now.

6.5. Routing Extension

To generate URLs from a Smarty template you may use two block functions (path and url) provided by the RoutingExtension.

path block:

<a href="{path slug='my-blog-post'}blog_show{/path}">
    Read this blog post.
</a>

path modifier:

<a href="{'blog_show|path:['slug' => 'my-blog-post']}">
    Read this blog post.
</a>

Absolute URLs can also be generated.

url block:

<a href="{url slug='my-blog-post'}blog_show{/url}">
    Read this blog post.
</a>

url modifier:

<a href="{'blog_show'|url ['slug' => 'my-blog-post']}">
    Read this blog post.
</a>

Please see the Symfony - Routing for full information about routing features and options in Symfony.

6.6. Translation Extension

To help with message translation of static blocks of text in template context, the SmartyBundle, provides a translation extension. This extension is implemented in the class TranslationExtension.

You may translate a message, in a template, using a block or modifier. Both methods support the following arguments:

count
In pluralization context, used to determine which translation to use and also to populate the %count% placeholder (only available in transchoice);
vars
Message placeholders;
domain
Message domain, an optional way to organize messages into groups;
locale
The locale that the translations are for (e.g. en_GB, en, etc);

trans block:

{trans}Hello World!{/trans}

{trans vars=['%name%' => 'World']}Hello %name%{/trans}

{trans domain="messages" locale="pt_PT"}Hello World!{/trans}

<!-- In case you're curious, the latter returns "Olá Mundo!" :) -->

trans modifier:

{"Hello World!"|trans}

{"Hello %name%"|trans:['%name%' => 'World']}

{"Hello World!"|trans:[]:"messages":"pt_PT"}

Message pluralization can be achieved using transchoice:

Warning

Unlike the examples given in the Symfony documentation, which uses curly brackets for explicit interval pluralization we are using square brackets due to Smarty usage of curly brackets as syntax delimiters. So {0} There is no apples becomes [0] There is no apples.

transchoice block:

{transchoice count=$count}[0] There is no apples|[1] There is one apple|]1,Inf] There is %count% apples{/transchoice}

transchoice modifier:

{'[0] There is no apples|[1] There is one apple|]1,Inf] There is %count% apples'|transchoice:$count}
<!-- Should write: "There is 5 apples" -->

The transchoice block/modifier automatically gets the %count% variable from the current context and passes it to the translator. This mechanism only works when you use a placeholder following the %var% pattern.

6.7. Security Extension

This extension provides access control inside a Smarty template. This part of the security process is called authorization, and it means that the system is checking to see if you have privileges to perform a certain action. For full details about the Symfony security system check it’s documentation page.

If you want to check if the current user has a role inside a template, use the built-in is_granted modifier.

Usage:

{if 'IS_AUTHENTICATED_FULLY'|is_granted:$object:$field}
    <a href="...">Delete</a>
{else}
    <!-- no delete for you -->
{/if}

Note

If you use this function and are not at a URL behind a firewall active, an exception will be thrown. Again, it’s almost always a good idea to have a main firewall that covers all URLs.

6.7.1. Complex Access Controls with Expressions

Note

The expression functionality was introduced in Symfony 2.4.

In addition to a role like ROLE_ADMIN, the isGranted method also accepts an Expression object.

You can use expressions inside your templates like this:

{if '"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())'|expression|is_granted}
    <a href="...">Delete</a>
{/if}

In this example, if the current user has ROLE_ADMIN or if the current user object’s isSuperAdmin() method returns true, then access will be granted (note: your User object may not have an isSuperAdmin method, that method is invented for this example).

For more details on expressions and security, see the section Complex Access Controls with Expressions in the Symfony book.

6.7.2. Using CSRF Protection in the Login Form

The security extension also adds a modifer to support CSRF Protection in login forms. Please read Using CSRF Protection in the Login Form from the Symfony Documentation for general CSRF Protection setup. The template for rendering should look like this:

<input type="hidden" name="_csrf_token" value="{'authenticate'|csrf_token}">

6.8. Enabling custom Extensions

To enable a Smarty extension, add it as a regular service in one of your configuration, and tag it with smarty.extension. The creation of the extension itself is described in the next section.

  • YAML
    services:
        smarty.extension.your_extension_name:
            class: Fully\Qualified\Extension\Class\Name
            arguments: [@service]
            tags:
                - { name: smarty.extension }
    

6.9. Creating a SmartyBundle Extension

Note

In version 0.1.0 class AbstractExtension was simply named Extension. Please update your code when migrating to 0.2.0.

An extension is a class that implements the ExtensionInterface. To make your life easier an abstract AbstractExtension class is provided, so you can inherit from it instead of implementing the interface. That way, you just need to implement the getName() method as the Extension class provides empty implementations for all other methods.

The getName() method must return a unique identifier for your extension:

namespace NoiseLabs\Bundle\SmartyBundle\Extension;

class TranslationExtension extends AbstractExtension
{
    public function getName()
    {
        return 'translator';
    }
}

Plugins

Plugins can be registered in an extension via the getPlugins() method. Each element in the array returned by getPlugins() must implement PluginInterface.

For each Plugin object three parameters are required. The plugin name comes in the first parameter and should be unique for each plugin type. Second parameter is an object of type ExtensionInterface and third parameter is the name of the method in the extension object used to perform the plugin action.

Please check available method parameters and plugin types in the Extending Smarty With Plugins webpage.

namespace NoiseLabs\Bundle\SmartyBundle\Extension;

use NoiseLabs\Bundle\SmartyBundle\Extension\Plugin\BlockPlugin;

class TranslationExtension extends Extension
{
    public function getPlugins()
    {
        return array(
            new BlockPlugin('trans', $this, 'blockTrans'),
        );
    }

    public function blockTrans(array $params = array(), $message = null, $template, &$repeat)
    {
        $params = array_merge(array(
            'arguments' => array(),
            'domain'    => 'messages',
            'locale'    => null,
        ), $params);

        return $this->translator->trans($message, $params['arguments'], $params['domain'], $params['locale']);
    }
}

Filters

Filters can be registered in an extension via the getFilters() method.

Each element in the array returned by getFilters() must implement FilterInterface.

namespace NoiseLabs\Bundle\SmartyBundle\Extension;

use NoiseLabs\Bundle\SmartyBundle\Extension\Filter\PreFilter;

class BeautifyExtension extends Extension
{
    public function getFilters()
    {
        return array(
            new PreFilter($this, 'htmlTagsTolower'),
        );
    }

    // Convert html tags to be lowercase
    public function htmlTagsTolower($source, \Smarty_Internal_Template $template)
    {
        return preg_replace('!<(\w+)[^>]+>!e', 'strtolower("$1")', $source);
    }
}

Globals

Global variables can be registered in an extension via the getGlobals() method.

There are no restrictions about the type of the array elements returned by getGlobals().

namespace NoiseLabs\Bundle\SmartyBundle\Extension;

class GoogleExtension extends Extension
{
    public function getGlobals()
    {
        return array(
            'ga_tracking' => 'UA-xxxxx-x'
        );
    }
}