This Symfony2 bundle provides integration for the Smarty3 template engine.
Contents
This bundle was created to support Smarty in Symfony2, providing an alternative to the Twig template engine natively supported.
An effort was made to provide, where possible, the same user configuration and extensions available for the Twig bundle. This is to allow easy switching between the two bundles (at least I hope so!).
Smarty is a template engine for PHP, facilitating the separation of presentation (HTML/CSS) from application logic. This implies that PHP code is application logic, and is separated from the presentation.
Some of Smarty’s features: [1]
See the Smarty3 Manual for other features and information on it’s syntax, configuration and installation.
[1] | http://www.smarty.net/docs/en/what.is.smarty.tpl |
This bundle is licensed under the LGPLv3 License. See the LICENSE file for details.
This can be done in several ways, depending on your preference. The first method is the standard Symfony2.1 method.
Using composer:
Add SmartyBundle in your composer.json‘:
{
"require": {
"noiselabs/smarty-bundle": "dev-master"
}
}
Now tell composer to download the bundle by running the command:
$ php composer.phar update noiselabs/smarty-bundle
Composer will install the bundle to your project’s vendor/noiselabs directory.
Using the vendors script:
Add the following lines in your deps file::
[NoiseLabsSmartyBundle]
git=git://github.com/noiselabs/SmartyBundle.git
target=bundles/NoiseLabs/Bundle/SmartyBundle
Now, run the vendors script to download the bundle:
$ php bin/vendors install
Using submodules:
If you prefer instead to use git submodules, then run the following:
$ git submodule add git://github.com/noiselabs/SmartyBundle.git vendor/bundles/NoiseLabs/Bundle/SmartyBundle
$ git submodule update --init
Add the NoiseLabs namespace to your autoloader:
<?php
// app/autoload.php
$loader->registerNamespaces(array(
// ...
'NoiseLabs\\Bundle' => __DIR__.'/../vendor/bundles',
));
Enable the bundle in the kernel:
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
new NoiseLabs\Bundle\SmartyBundle\SmartyBundle(),
);
}
# app/config/config.yml
# ...
framework:
templating: { engines: ['twig', 'smarty'] }
You can render a Smarty template instead of a Twig one simply by using the .smarty extension in the template name instead of .twig. The controller below renders the index.html.smarty template:
// src/Acme/HelloBundle/Controller/HelloController.php
public function indexAction($name)
{
return $this->render('AcmeHelloBundle:Hello:index.html.smarty', array('name' => $name));
}
Like Symfony2 PHP renderer or Twig, Smarty provides template inheritance.
Template inheritance is an approach to managing templates that resembles object-oriented programming techniques. Instead of the traditional use of {include ...} tags to manage parts of templates, you can inherit the contents of one template to another (like extending a class) and change blocks of content therein (like overriding methods of a class.) This keeps template management minimal and efficient, since each template only contains the differences from the template it extends.
Example:
layout.html.smarty:
<html>
<head>
<title>{block name=title}Default Page Title{/block}</title>
</head>
<body>
{block name=body}{/block}
</body>
</html>
mypage.html.smarty:
{extends 'file:AcmeHelloBundle:Default:layout.html.smarty'}
{block name=title}My Page Title{/block}
{block name=body}My HTML Page Body goes here{/block}
Output of mypage.html.smarty:
<html>
<head>
<title>My Page Title</title>
</head>
<body>
My HTML Page Body goes here
</body>
</html>
Instead of using the file:AcmeHelloBundle:Default:layout.html.smarty syntax you may use file:[WebkitBundle]/Default/layout.html.smarty which should be, performance wise, slightly better/faster (since this is a native Smarty syntax).:
{extends 'file:[WebkitBundle]/Default/layout.html.smarty'}
To load a template that lives in the app/Resources/views directory of the project you should use the following syntax:
{extends 'file:base.html.smarty'}
Please see Symfony2 - Template Naming and Locations to learn more about the naming scheme and template locations supported in Symfony2.
{include} functions work the same way as the examples above.:
{include 'file:WebkitBundle:Default:layout.html.smarty'}
{include 'file:[WebkitBundle]/Default/layout.html.smarty'}
{include 'file:base.html.smarty'}
Important: Note the usage of the file: resource in the {extends} function. We need to declare the resource even if the Smarty class variable $default_resource_type is set to 'file'. This is required because we need to trigger a function to handle ‘logical’ file names (only mandatory if you are using the first syntax). Learn more about resources in the Smarty Resources webpage.
The .html.smarty extension can simply be replaced by .smarty. We are prefixing with .html to stick with the Symfony convention of defining the format (.html) and engine (.smarty) for each template.
As exemplified in the Symfony Cookbook it is possible to make a variable to be accessible to all the templates you use by configuring your app/config/config.yml file:
# app/config/config.yml
smarty:
# ...
globals:
ga_tracking: UA-xxxxx-x
Now, the variable ga_tracking is available in all Smarty templates:
<p>Our google tracking code is: {$ga_tracking} </p>
Smarty[Bundle] 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.
This extension tries to provide the same funcionality described in Symfony2 - 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]}
Assetic is an asset management framework for PHP. This extensions provides support for it’s usage in Symfony2 when using Smarty templates.
Assetic combines two major ideas: assets and filters. The assets are files such as CSS, JavaScript and image files. The filters are things that can be applied to these files before they are served to the browser. This allows a separation between the asset files stored in the application and the files actually presented to the user.
Using Assetic provides many advantages over directly serving the files. The files do not need to be stored where they are served from and can be drawn from various sources such as from within a bundle:
{javascripts
assets='@AcmeFooBundle/Resources/public/js/*'
}
<script type="text/javascript" src="{$asset_url}"></script>
{/javascripts}
To bring in CSS stylesheets, you can use the same methodologies seen in this entry, except with the stylesheets tag:
{stylesheets
assets='@AcmeFooBundle/Resources/public/css/*'
}
<link rel="stylesheet" href="{$asset_url}" />
{/stylesheets}
You can also combine several files into one. This helps to reduce the number of HTTP requests, which is great for front end performance. It also allows you to maintain the files more easily by splitting them into manageable parts. This can help with re-usability as you can easily split project-specific files from those which can be used in other applications, but still serve them as a single file:
{javascripts
assets='@AcmeFooBundle/Resources/public/js/*,
@AcmeBarBundle/Resources/public/js/form.js,
@AcmeBarBundle/Resources/public/js/calendar.js'
}
<script src="{$asset_url}"></script>
{/javascripts}
In the dev environment, each file is still served individually, so that you can debug problems more easily. However, in the prod environment, this will be rendered as a single script tag.
Here is a list of the possible attributes to define in the block function.
assets: A comma-separated list of files to include in the build (CSS, JS or image files)
debug: If set to true, the plugin will not combine your assets to allow easier debug
filter: A coma-separated list of filters to apply. Currently, only LESS and YuiCompressor (both CSS and JS) are supported
combine: Combine all of your CSS and JS files (overrides debug)
output: Defines the URLs that Assetic produces
var_name: The variable name that will be used to pass the asset URL to the <link> tag
as: An alias to var_name. Example: as='js_url'
vars: Array of asset variables. For a description of this recently added feature please check out the Johannes Schmitt blog post about Asset Variables in Assetic.
Note: Unlike the examples given in the Asset Variables in Assetic, which uses curly brackets for the vars placeholder we are using square brackets due to Smarty usage of curly brackets as syntax delimiters. So js/messages.{locale}.js becomes js/messages.[locale].js.
Example using all available attributes:
{javascripts
assets='@AcmeFooBundle/Resources/public/js/*,
@AcmeBarBundle/Resources/public/js/form.js,
@AcmeBarBundle/Resources/public/js/calendar.js',
@AcmeBarBundle/Resources/public/js/messages.[locale].js
filter='yui_js'
output='js/compiled/main.js'
var_name='js_url'
vars=['locale']
}
<script src="{$js_url}"></script>
{/javascripts}
For further details please refer to the Symfony documentation pages about Assetic:
Templates commonly refer to images, Javascript and stylesheets as assets. You could hard-code the path to these assets (e.g. /images/logo.png), but the SmartyBundle provides a more dynamic option via the assets function:
<img src="{asset}images/logo.png{/asset}" />
<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}
Coming soon.
To generate URLs from a Smarty template you may use two block functions (path and url) provided by the RoutingExtension.
<a href="{path slug='my-blog-post'}blog_show{/path}">
Read this blog post.
</a>
Absolute URLs can also be generated.:
<a href="{url slug='my-blog-post'}blog_show{/url}">
Read this blog post.
</a>
Please see the Symfony2 - Routing for full information about routing features and options in Symfony2.
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.
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:
Note: 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.
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 Symfony2 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}
access granted
{else}
access denied
{/if}
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 example:
services:
smarty.extension.your_extension_name:
class: Fully\Qualified\Extension\Class\Name
arguments: [@service]
tags:
- { name: smarty.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:
<?php
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.
<?php
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.
<?php
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().
<?php
namespace NoiseLabs\Bundle\SmartyBundle\Extension;
class GoogleExtension extends Extension
{
public function getGlobals()
{
return array(
'ga_tracking' => 'UA-xxxxx-x'
);
}
}
The example below uses YAML format. Please adapt the example if using XML or PHP.
app/config/config.yml:
smarty:
options:
# See http://www.smarty.net/docs/en/api.variables.tpl
allow_php_templates:
allow_php_templates:
auto_literal:
autoload_filters:
cache_dir: %kernel.cache_dir%/smarty/cache
cache_id:
cache_lifetime:
cache_locking:
cache_modified_check:
caching:
caching_type:
compile_check:
compile_dir: %kernel.cache_dir%/smarty/templates_c
compile_id:
compile_locking:
compiler_class:
config_booleanize:
config_dir: %kernel.root_dir%/config/smarty
config_overwrite:
config_read_hidden:
debug_tpl:
debugging:
debugging_ctrl:
default_config_type:
default_modifiers:
default_resource_type: file
default_config_handler_func:
default_template_handler_func:
direct_access_security:
error_reporting:
escape_html:
force_cache:
force_compile:
locking_timeout:
merge_compiled_includes:
php_handling:
plugins_dir:
smarty_debug_id:
template_dir: %kernel.root_dir%/Resources/views
trusted_dir:
use_include_path: false
use_sub_dirs: true
globals:
# Examples:
foo: "@bar"
pi: 3.14
regenerated. So if there was a cached template, but it’s expired, Smarty will run a single compile_check before regenerating the cache.
Vítor Brandão - noisebleed@noiselabs.org ~ twitter.com/noiselabs ~ blog.noiselabs.org
See also the list of contributors who participated in this project.
Bugs and feature requests are tracked on GitHub.