Symfony/Twig: Stop rendering the rest of a template - twig

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.

Related

PHP __LINE__ equivalent for twig

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 #}

Twig function/filter with no input?

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

puppet and inline_epp and using hiera deliver content

It's possible to use inline_epp's using string variable like this ?
"htaccess": {
"content": [
"<% include stdlib -%>",
"AuthType Basic ",
"AuthUserFile <%= $auth_file %>",
"Require valid-use"
],
"params": {
"auth_name": "Bootstrap content for provisioning",
"auth_file": "file_path"
}
}
and (some.pp):
$htacces = $sub_attrs['htaccess']
$content = $htacces['content']
$data = join($content, "\n")
$auth_file = "sgsdgsdfgsdfg"
notice inline_epp($data)
This is result in the notice line : Invalid EPP: This Name has no
effect. A value was produced and then forgotten (one or more preceding
expressions may have the wrong form)
Idea is use hiera data to deliver content to epp .
Using puppet 5.5.2 and Ubuntu 16.04
There looks like there are a few things going on here.
For one thing, is the syntax of that notice function correct? The Puppet Cookbook shows notice with either brackets notice("message") or in Puppet's class syntax notify { "message": }).
Also, inline_epp takes a hash of the parameters used to fill in the EPP. See the Puppet documentation on inline_epp. In your case, that'd be something like inline_epp($data, { auth_file => $auth_file }).
And lastly, EPP needs a line at the top listing its parameters. In your case it should be something like <%- | String $auth_file | -%>. See the Puppet documentation on EPP files.

How to call ucwords in twig?

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 }}

Jade with Express - how to disable ReferenceError

i want replace empty string instead of ReferenceError. following code :
p #{data.data.data}
ReferenceError occured when render template that i want disable it.
Stumbled onto the same thing, but passing an empty object felt wrong. I suggest handling the possibility in the template with something like:
- if(data)
p #{data.data.data}
- else
p No data for you!
Or specify a placeholder inline
p #{data.data.data ? data.data.data : 'No data'}
Pass an empty object if there is no value when rendering:
res.render('view/index', {data: your_data_variable || {} });

Resources