EDIT: Dec 3 2016
Want to learn how to add custom extensions(filters) to twig? see this answer by lxg
Do you just need to find the twig equivalent for ucwords? see this answer by Javier Eguiluz
I found several posts on calling php functions from twig, that show it should be supported, however it doesn't seem to work.
{{ ucwords( item|replace({'_':' '}) ) }}
results in :l
Slim Application Error
The application could not run because of the following error:
Details
Type: Twig_Error_Syntax Message: The function "ucwords" does not exist
in "home.twig" at line 101
File:
/usr/share/dev89/html/vhosts/local/libs/vendor/twig/twig/lib/Twig/ExpressionParser.php
Line: 572
As #lxg said, it's not possible to call all PHP functions from Twig templates ... unless you want to do that and define your own filters/functions. Instead of a drawback, this is a good thing to "force" you to create good templates that don't contain too much logic.
Anyway, in this particular case, Twig already contains a filter called title which applies the "title case", which is equivalent to the ucwords() PHP function:
{{ item|replace({'_':' '})|title }}
Update: Twig 2.x comes with the capitalize filter which does exactly that.
It is not true that all PHP functions are available in Twig. Only a few Twig filters and functions go by the same names as their equivalents in PHP.
But you can easily create your own Twig extension for ucwords – filter as well as function:
<?php
namespace Acme\TestBundle\Twig;
class UcWordsExtension extends \Twig_Extension
{
public function getFunctions()
{
return [
new \Twig_SimpleFunction('ucwords', 'ucwords')
];
}
public function getFilters()
{
return [
new \Twig_SimpleFilter('ucwords', 'ucwords')
];
}
public function getName()
{
return 'ext.ucwords';
}
}
The first parameter of Twig_SimpleFunction/Twig_SimpleFilter is the name of the function/filter in Twig. The second parameter is a PHP callable. As the ucfirst function already exists, it is sufficient to pass its name as a string.
Test in Twig:
{{ "test foobar"|ucwords }} {# filter #} <br>
{{ ucwords("test foobar") }} {# function #}
Returns:
Test Foobar
Test Foobar
Why not use title filter?
I was looking for a filter that will work as like ucwords() php function and I found this title filter in Twig Documentation.
Usage example;
{{ 'i am raziul islam'|title }}
Outputs: I Am Raziul Islam
You can use the capitalize twig filter:
{{ item | capitalize }}
Related
There is a way to get PHP __LINE__ equivalent for Twig?
It's almost impossible to search __LINE__ on Google as exact word...
The purpose is purely for debugging a long twig file containing nested twig code inside a complex Js code, to console.log it.
The version of twig I'm using is 2.12.5.
Thanks
First things first:
As seen in the PHP documentation the (magic) constant will return the current line number of the file.The "problem" here is that twig converts templates to PHP in order to render them.
This means if you were actually were to be able to use __LINE__ inside a template, it would report back the line number from either a temporary PHP file or a cached PHP file, wether you have caching enabled or not.
TLDR: Using __LINE__ inside a template is going to report back "false"/useless information.
However, you can easily extend twig and even introduce new tags which you then can use inside templates. These customs tags allow you to modify/alter the compilation of the template's PHP file.
The interesting part here is that the "compiler" is able to provide your custom tag on which specific line the tag was called.
We can even create a custom tag, register it with twig and let the tag report back the line number in the parsed template.
Step 1 - Create a TokenParser
The TokenParser is responsible for parsing the template and allows you to choose a name for your tag. The code below will be responsible to create a simple, empty tag named line
<?php
namespace MyProject\Base\Twig\TokenParser;
use \MyProject\Base\Twig\Node\Line as LineNode;
class Line extends \Twig_TokenParser {
public function parse(\Twig_Token $token)
{
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
return new LineNode(new \Twig_Node(), $token->getLine(), $this->getTag());
}
public function getTag()
{
return 'line';
}
}
Step 2 - Create a Node
The Node is responsible for converting the template code to actual PHP code
<?php
namespace MyProject\Base\Twig\Node;
class Line extends \Twig_Node {
public function __construct(\Twig_NodeInterface $body, $lineno, $tag = null) {
parent::__construct(['body' => $body,], array(), $lineno, $tag);
}
public function compile(\Twig_Compiler $compiler) {
$compiler->write('echo '.$this->getLine().';')
->write(PHP_EOL);
}
}
Step 3 - Register the tag with twig
<?php
namespace MyProject\Base\Twig;
class MyProjectTwigExtension extends Twig_Extension {
public function getTokenParsers() {
return [
new \CMS4U\Base\Twig\TokenParser\Line(),
];
}
public function getName() {
return 'MyProjectTwigExtension';
}
}
<?php
$twig->addExtension(new \MyProject\Base\Twig\MyProjectTwigExtension());
If everything is good, you can now use the custom tag {% line %} wherever you like in any template
Foo
Bar
FooBar
Current line number is {% line %} {# 4 #}
I'm working on application written in symfony2 and I want to send email after some action/event... the problem is, that the users can define something like "email templates" which are stores in db like simple string, for example: "This is some email from {{ user }}" and I need to render body for that email which should use that template...
In symfony documentation from this link: https://symfony.com/doc/2.0/cookbook/email/email.html#sending-emails the method for render view is $this->renderView and it expects the path to file such as "bundle:controller:file.html.twig", but my template is simple string from database...
How can I render it?
Twig_Loader_String is deprecated and was always designed for internal use anyway. The usage of this loader is strongly discouraged.
From the API doc:
This loader should NEVER be used. It only exists for Twig internal
purposes. When using this loader with a cache mechanism, you should
know that a new cache key is generated each time a template content
"changes" (the cache key being the source code of the template). If
you don't want to see your cache grows out of control, you need to
take care of clearing the old cache file by yourself.
Also check out this issue: https://github.com/symfony/symfony/issues/10865
The best way I know to load a template from a String source are:
From a controller:
$template = $this->get('twig')->createTemplate('Hello {{ name }}');
$template->render(array('name'=>'World'));
as described here: http://twig.sensiolabs.org/doc/recipes.html#loading-a-template-from-a-string
From a twig template:
{{ include(template_from_string("Hello {{ name }}", {'name' : 'Peter'})) }}
as described here: http://twig.sensiolabs.org/doc/functions/template_from_string.html
Note, that the 'template_from_string' - function is not available by default and needs to be loaded. In symfony you would do this by adding a new service:
# services.yml
services:
appbundle.twig.extension.string:
class: Twig_Extension_StringLoader
tags:
- { name: 'twig.extension' }
This should work. Replace "Hello {{ name }}" with your template text, and fill the array that is passed into the render function with any variables that you need.
$env = new \Twig_Environment(new \Twig_Loader_String());
echo $env->render(
"Hello {{ name }}",
array("name" => "World")
);
Here's a solution that works with Symfony 4 (and possibly older versions as well, although I haven't tested it) and allows you to work with templates stored in the database the same way you would work with templates in the filesystem.
This answer assumes you're using Doctrine, but is relatively easy to adapt if you're using another database library.
Create the Template entity
This is an example class that uses annotations, but you can use whatever configuration method you're already using.
src/Entity/Template.php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="templates")
* #ORM\Entity
*/
class Template
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string", nullable=false)
*/
private $filename;
/**
* #var string
*
* #ORM\Column(type="text", nullable=false)
*/
private $source;
/**
* #var \DateTime
*
* #ORM\Column(type="datetime", nullable=false)
*/
private $last_updated;
}
The bare minimum fields are filename and source, but it's a very good idea to include last_updated or you'll lose the benefits of caching.
Create a DatabaseLoader class
src/Twig/Loader/DatabaseLoader.php
<?php
namespace App\Twig\Loader;
use App\Entity\Template;
use Doctrine\ORM\EntityManagerInterface;
use Twig_Error_Loader;
use Twig_LoaderInterface;
use Twig_Source;
class DatabaseLoader implements Twig_LoaderInterface
{
protected $repo;
public function __construct(EntityManagerInterface $em)
{
$this->repo = $em->getRepository(Template::class);
}
public function getSourceContext($name)
{
if (false === $template = $this->getTemplate($name)) {
throw new Twig_Error_Loader(sprintf('Template "%s" does not exist.', $name));
}
return new Twig_Source($template->getSource(), $name);
}
public function exists($name)
{
return (bool)$this->getTemplate($name);
}
public function getCacheKey($name)
{
return $name;
}
public function isFresh($name, $time)
{
if (false === $template = $this->getTemplate($name)) {
return false;
}
return $template->getLastUpdated()->getTimestamp() <= $time;
}
/**
* #param $name
* #return Template|null
*/
protected function getTemplate($name)
{
return $this->repo->findOneBy(['filename' => $name]);
}
}
The class is relatively simple. getTemplate looks up the template filename from the database, and the rest of the methods use getTemplate to implement the interface that Twig needs.
Add the DatabaseLoader to your service config
config/services.yaml
services:
App\Twig\Loader\DatabaseLoader:
tags:
- { name: twig.loader }
Now you can use your database templates in the same way as filesystem templates.
Rendering from a controller:
return $this->render('home.html.twig');
Including from another Twig template (which can be in the database or filesystem):
{{ include('welcome.html.twig') }}
Rendering to a string (where $twig is an instance of Twig\Environment)
$html = $twig->render('email.html.twig')
In each of these cases, Twig will check the database first. If getTemplate in your DatabaseLoader returns null, Twig will then check the filesystem. If the template isn't available in the database or the filesystem, Twig will throw a Twig_Error_Loader.
Clone the native twig service and replace the filesystem loader with the native twig string loader:
<service id="my.twigstring" class="%twig.class%">
<argument type="service" id="my.twigstring.loader" />
<argument>%twig.options%</argument>
</service>
<service id="my.twigstring.loader" class="Twig_Loader_String"></service>
Usage example from within a controller:
$this->get('my.twigstring')->render('Hello {{ name }}', array('name' => 'Fabien'));
The best way to do it is to use template_from_string twig function.
{{ include(template_from_string("Hello {{ name }}")) }}
{{ include(template_from_string(page.template)) }}
See documentation of template_from_string
See why it is not a good idea to use Twig_Loader_Chain or Twig_Loader_String for that purpose on this github issue by stof.
As of Twig 1.10, the Twig Engine doesn't support rendering strings. But there is a bundle available which adds this behavior called TwigstringBundle.
It adds the $this->get('twigstring') service wich you can use to render your strings.
(As September '19, the current version of Twig is 2.X, and version 3 is around the corner; so this is only applies to very old versions of Twig).
This work for me:
$loader = new \Twig\Loader\ArrayLoader([
'Temp_File.html' => 'Hello {{ name }}!',
]);
$twig = new \Twig\Environment($loader);
echo $twig->render('Temp_File.html', ['name' => 'Fabien']);
https://twig.symfony.com/doc/2.x/api.html
FYI, This feature was suggested to be added in the core of Twig as of 1.11.0, but will be needed to be activated by the developper.
With Symfony 2.2 you can use the Twig_Chain_Loader
How to register another (custom) Twig loader in Symfony2 environment?
I recently had to implement a CMS used by multiple parties where each party could completely customize their templates. To achieve this I implemented a custom Twig Loader.
The most difficult part was coming up with a naming convention for the templates guaranteed not to overlap with any existing templates, for example <organisation_slug>!AppBundle:template.html.twig.
In case the template was not customised, the template AppBundle:template.html.twig would have to be loaded as fallback template.
However, this is not possible with the Chain Loader (AFAIK) because there the template name cannot be modified. Therefore I had to inject the default loader (i.e. the loader chain) into my loader and use it to load the fallback template.
Another solution would be to pass the request stack or the session to the template loader, making it possible to automatically detect the organisation, but this is difficult because the security component depends on the templating subsystem, causing circular dependency issues.
$message = \Swift_Message::newInstance()
->setSubject('Hello Email')
->setFrom('send#example.com')
->setTo('recipient#example.com')
->setBody('hai its a sample mail')
;
$this->get('mailer')->send($message);
I'm using Slim 3 and Slim Twig-View. I want to add a Twig function (or filter, not sure what is the difference?) which generates a random string, and doesn't take any input.
I was able to add a filter like this:
$twig->getEnvironment()->addFilter(
new \Twig_Filter('guid', function(){ return generateGUID(); })
);
But I can't seem to use it without providing some dummy input:
{{ 0|guid }} This will work
{{ guid }} This will not work
How can I use my guid filter/function without providing any input?
A filter always apply on something, it filters something.
What you want is a function, indeed.
The extending Twig page of the documentation is an incredible source of information on that matter.
At first glance, I would even have said you should define a tag for this but the documentation on the tag, explicitly says:
If your tag generates some output, use a function instead.
Source: https://twig.symfony.com/doc/3.x/advanced.html#tags
So indeed, in order to define a function:
Functions are defined in the exact same way as filters, but you need to create an instance of \Twig\TwigFunction:
$twig = new \Twig\Environment($loader);
$function = new \Twig\Twig_Function('function_name', function () {
// ...
});
$twig->addFunction($function);
So more specifically for you:
$container->get('view')->getEnvironment()->addFunction(
new Twig_SimpleFunction('guid', function(){ return generateGUID(); })
);
Will be accessible via:
{{ guid() }}
Other worth reading:
extending twig, in Slim documentation
you can achieve the same with a macro
I've been searching the internet for this, but I can't find anything regarding this.
I'm creating a simple twig template that is going to be used on multiple locations, but requires some variables.
I want to be able to do something like this:
{% if some_variable is not defined %}
<h1>Some variable was not defined.<h1>
-- stop rendering the rest of the template --
{% endif %}
{{ some_variable }} is defined here.
The reason I'm asking this is really simple.
I don't want my entire template to be indented in one or more if-statements, since it'll clutter the entire file very easy.
I know the workaround would be to create multiple templates, but multiple files for a simple condition sounds kind of overkill to me.
If this doesn't exist natively, I'm okay creating an extension for this if anyone can tell me how- and if this can be achieved.
Thanks in advance!
P.S. Don't answer with {% else %}, thats exactly the thing I'm trying to avoid here...
What you ask for is not natively supported.
To achieve such a thing you would need to go through a lot of trouble.
Twig templates are compiled into PHP, extended by the base template of Twig it self. When looking through in the base template you'll see that eventually the function doDisplay will be called. An example of the contents of this function is as follows
protected function doDisplay(array $context, array $blocks = array())
{
// line 1
echo "\t<div id=\"null_wrapper\">
\t\t<div class=\"invoice_price\">\t\t\t
\t\t\t<div>
\t\t\t\t";
// line 4
echo twig_escape_filter($this->env, $this->getAttribute((isset($context["forms"]) ? $context["forms"] : $this->getContext($context, "forms")), "getTextfield", array(0 => "#label_Quantity#", 1 => "txt_new_quantity", 2 => ((array_key_exists("txt_quantity", $context)) ? (_twig_default_filter((isset($context["txt_quantity"]) ? $context["txt_quantity"] : $this->getContext($context, "txt_quantity")), 1)) : (1)), 3 => ((array_key_exists("errors", $context)) ? (_twig_default_filter((isset($context["errors"]) ? $context["errors"] : $this->getContext($context, "errors")), "")) : ("")), 4 => "", 5 => "smallinput"), "method"), "html", null, true);
echo "
\t\t\t</div>
\t\t\t<div class=\"clear\"></div>
\t\t\t<div>
\t\t\t\t";
// line 8
echo twig_escape_filter($this->env, $this->getAttribute((isset($context["forms"]) ? $context["forms"] : $this->getContext($context, "forms")), "getTextfield", array(0 => "#label_Unit_price#", 1 => "txt_new_price_excl", 2 => ((array_key_exists("txt_new_price_excl", $context)) ? (_twig_default_filter((isset($context["txt_new_price_excl"]) ? $context["txt_new_price_excl"] : $this->getContext($context, "txt_new_price_excl")), "")) : ("")), 3 => ((array_key_exists("errors", $context)) ? (_twig_default_filter((isset($context["errors"]) ? $context["errors"] : $this->getContext($context, "errors")), "")) : ("")), 4 => "", 5 => "smallinput"), "method"), "html", null, true);
echo "<span>";
echo twig_escape_filter($this->env, getSiteConfigValue("CURRENCY"), "html", null, true);
echo "</span>
\t\t\t</div>
\t\t\t<div class=\"clear\"></div>
\t\t\t<div>
\t\t\t\t";
As you can see the output is sent to the browser immediately (and catched by ob_start in the base template), so even you could exit out of a template the chance exist you'll end up with broken HTML.
TL:DR The only way to achieve such a thing is to override the compiler of twig, which compiles the twig template into PHP, perhaps you could write your own node, as this renders/compiles as well
I might be missing something of the original intent, but I found this easy to achieve by extending Twig with a template function that I called cancel
public function cancel($msg = '') {
throw new Twig\Error\Error("Process cancelled with msg: $msg");
}
As you can see, it simply throws an Exception. Outside the template this is caught and handled.
In the template I write this:
{{ cancel('My reason for cancelling') }}
And, as far as I can tell from my tests, the template processing stops there.
To add the extension follow the normal procedure.
I've got a situation where some variables sent to my Twig templates are plain old variables, so I want them to be html-escaped (as is the default behaviour). But other variables sent to my templates are really objects with __toString() renderers... and some of these objects send out raw HTML (e.g. from a WYSIWYG editor like TinyMCE or CKEditor).
Ideally I'd like for my template designers to not have to use the |raw filter on the objects, but instead somehow have the objects tell Twig that they're already escaped.
In other words, I'm trying to mimc the behavior of a Twig function that sets is_safe, but without requiring template designers to use a function.
E.g. I could write a Twig function using the is_safe parameter in its definition and be able to have this in my templates:
{{ figure_out_what_to_do(something) }}
(where the figure_out_what_to_do knows to inspect the "something" object to ascertain whether or not it needs to be escaped). BUT to me this is no better than having to remember to put |raw after every output of "something". So instead I'd like to be able to do this:
{{ something }}
...and have Twig recognize that something is an object and hence ask it whether or not it needs to be escaped.
I'm guessing the answer is "no", but figured I'd ask in case someone who knows more about Twig internals has any pointers for me.
Thanks.
In the __toString() method you could instead of return the html output, do this : return new Twig_Markup($html, 'UTF-8'); thus marking it as a safe and not to be escaped
Instead of returning a new \Twig_Markup object in __toString() (which causes a fatal error since it must return a string) you could extend \Twig_Markup:
class Something extends \Twig_Markup {
public function __toString() {
return $this->safeValue;
}
public function count() {
return mb_strlen($this->safeValue);
}
}
Twig looks at the object to see if it is an instance of \Twig_Markup when deciding to escape the string or not. Here’s the source for \Twig_Markup:
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig;
/**
* Marks a content as safe.
*
* #author Fabien Potencier <fabien#symfony.com>
*/
class Markup implements \Countable
{
protected $content;
protected $charset;
public function __construct($content, $charset)
{
$this->content = (string) $content;
$this->charset = $charset;
}
public function __toString()
{
return $this->content;
}
public function count()
{
return \function_exists('mb_get_info') ? mb_strlen($this->content, $this->charset) : \strlen($this->content);
}
}
class_alias('Twig\Markup', 'Twig_Markup');
As you can see in the source code, \Twig_Markup implements \Countable. That’s why I’ve overridden the implementation of public function count() in my example.