I have a variable suppose that is:
$menustr; this variable contains code html and some twig parts for example:
$menustr .= '<li><a href="{{ path("'. $actual['direccion'] .'") }}" >'. $actual['nombre'] .'</a></li>';
I need that the browser take the code html and the part of twig that in this momen is the
"{{ path(~~~~~) }}"
I make a return where i send the variable called "$menustr" and after use the expresion "raw" for the html code but this dont make effective the twig code.
This is te return:
return $this->render('::menu.html.twig', array('menu' => $menustr));
and here is the template content:
{{ menu | raw }}
Twig can't render strings containing twig. There is not something like an eval function in Twig1..
What you can do is moving the path logic to the PHP stuff. The router service can generate urls, just like the path twig function does. If you are in a controller which extends the base Controller, you can simply use generateUrl:
$menuString .= '<li>'. $actual['nombre'] .'</li>';
return $this->render('::menu.html.twig', array(
'menu' => $menuString,
));
Also, when using menu's in Symfony, I recommend to take a look at the KnpMenuBundle.
EDIT: 1. As pointed by #PepaMartinec there is a function which can do this and it is called template_from_string
You can render Twig template stored in a varible using the template_from_string function.
Check this bundle: https://github.com/LaKrue/TwigstringBundle
This Bundle adds the possibility to render strings instead of files with the Symfony2 native Twig templating engine:
$vars = array('var'=>'x');
// render example string
$vars['test'] = $this->get('twigstring')->render('v {{ var }} {% if var is defined %} y {% endif %} z', $vars);
// output
v x y z
In your case i would be:
return $this->render('::menu.html.twig', array(
'menu' => $this->get('twigstring')->render($menustr, $vars)
));
Related
I'm using Laravel 9 and running PHP 8.2.
In blade views I can use the following example blade component:
<x-frontend.module-button href="#" text="Example button" />
But when using a HEREDOC like so:
$html = <<<HTML
<x-frontend.module-button href="#" text="This will not output anything" />
HTML;
The x-component doesn't output anything at all, and just to confirm that yes it does work in a standard blade view.
Is this just a limitation of using a HTML HEREDOC or is there some way around this? The only thing I tried was adding a special global function called component that returns the component view like so:
function component(string $componentName, array $data = [], bool $render = true)
{
$view = view('components.'.$componentName, $data);
return $render ? $view->render() : $view;
}
And echoing that out within the HEREDOC, but then that kinda defeats the purpose of using the HEREDOC in the first place.
Any idea's or suggestions welcome, thank you!
From Laravel 9 you can now render a Blade template inline using Illuminate\Support\Facades\Blade::render($someString).
I don't have an example of your custom Blade component so I mocked one up:
#props([
'html',
'text'
])
<div>
<button>{{ $text }}</button>
</div>
Then to render this component via a string:
<?php
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Blade;
Route::get('/', function () {
$html = Blade::render(<<<HTML
<x-frontend.module-button href="#" text="This will not output anything" />
HTML);
return view('welcome', compact('html'));
});
Note: To get this to render correctly I had to disable the auto-escaping feature of Blade, so my welcome.blade.php simply contained {!! $html !!}.
Is there an alternative to using template_from_string() for evaluating strings that contain calls to custom Twig functions (or any other complex Twig code for that matter)?
This is a simplified version of the code I have right now.
{% set content = {
'item_1' : {
'images' : {
'src': 'assets/images/img_1-600.jpg',
'srcset': "{{ assets('assets/images/img_1-600.jpg') }} 600w, {{ assets('assets/images/img_1-1200.jpg') }} 1200w",
}
}
# additional items follow
}
{% for data in content %}
<img src="{{ data.images.src }}" srcset="{{ include(template_from_string(data.images.srcset)) }}" alt="">
{% endfor %}
The assets() function simply returns a revisioned version of the static asset for the given path (i.e. assets('assets/images/img_1-600.jpg) renders as something like 'assets/images/img_1-600-a4c2254a6d.jpg).
The problems start with img srcset attribute which can become pretty complex and usually contains references to multiple assets that need to return revisioned values for static assets.
Now I know that I could modify the assets() function to support that kind of complex scenario but I'd like to keep things simple and let assets() only handle 1:1 transformations.
The only way to achieve this by using functionality provided by Twig seems to be template_from_string() combined with include, which in itself is not terrible but it does kind of look bulky for a simple operation as evaluating and rendering a string.
Not to mention that template_from_string requires StringLoaderExtension() to be loaded by default, which again I'd like to avoid using for performance reasons.
While the concatenation approach is not a bad idea, after much consideration I came to the conclusion that this is one of those places where having a custom Twig function makes much more sense.
The main argument in favor of using a function compared to simple concatenation is that with a function there is no need to worry about properly formating the template code (i.e. forget a space between the asset and the size descriptor in the srcset attribute value).
This approach also completely eliminates any need to use template_from_string() or additional extension dependencies.
Here is the final solution.
The template code, plain and simple, with plenty of overview.
{% set content = {
'item_1' : {
'images' : {
'src': 'assets/images/img-600.jpg',
'srcset': srcset([
[ assets('assets/images/img-600.jpg'), '600w' ],
[ assets('assets/images/img-800.jpg'), '800w' ],
[ assets('assets/images/img-1000.jpg'), '1000w' ],
[ assets('assets/images/img-1200.jpg'), '1200w' ]
]),
}
}
# additional items follow
}
{% for data in content %}
<img src="{{ data.images.src }}" srcset="{{ data.images.srcset }}" alt="">
{% endfor %}
The actual Twig function called srcset, that is used to generate a value for the srcset attribute from provided data in the above template.
public function srcset(array $srcset)
{
$output = [];
foreach ($srcset as $set) {
// Both parameters need to exist or the set isn't added to the output...
if (!(isset($set[0]) && isset($set[1]))) {
// ... just some exception handling that isn't of relevance for the question
// ...
continue;
}
$output[] = sprintf('%s %s', $set[0], $set[1]);
}
return implode(', ', $output);
}
To avoid any extra plugins, you could just concat the variables
{% set content = {
'item_1' : {
'images' : {
'src': 'assets/images/img_1-600.jpg',
'srcset': assets('assets/images/img_1-600.jpg')~' 600w,'~assets('assets/images/img_1-1200.jpg')~' 1200w',
}
}
} %}
I am trying to use the url Twig function with Silex to generate a route, but when I use the variable name that I have passed to the template it generates a warning that I have not supplied the parameter.
This is the array I am passing to the template:
[
"total_pages" => $pages,
"current_page" => $page,
"route_name" => "gallery_album",
"route_parameter" => "groupname",
"route_value" => $groupname
]
And in the template I am trying to use:
{{ url(route_name, {route_parameter: route_value, 'page': page} ) }}
(The page variable value is worked out in the template)
This is part of a pagination template that I am building so I need the parameter to be a variable so it can be applied to different pages. This is the error I get when I run this:
I feel this is something that is very simple, I am just missing something fundamental.
It thinks that route_parameter is a string key name and not a variable:
You can do for example:
{% set params = {'page': page, (route_parameter): route_value } %}
{{ url(route_name, params) }}
You can use {{ app->path}} or {{ app->url }}
if you using Silex\Application\UrlGeneratorTrait in you Application class
or alternative using this
{{ app.url_generator.generate('homepage') }}
I use Twig to generate LaTeX documents. Twig's default delimiter syntax clashes badly with LaTeX's curly braces. Simply escaping LaTeX is no option as it makes the code completely unreadable. I know I can define custom delimiters globally, but I don't want to rewrite all of my HTML templates to use the new syntax.
I also know about verbatim sections but those make the code truly ugly:
\ihead{
{% endverbatim %}
{{ title }}
{% verbatim %}
}
Is there a way I can change the syntax for just the current template or a set of templates, something like:
{% set_delimiters({
'tag_comment' : ['<%#', '%>'],
'tag_block' : ['<%' , '%>'],
'tag_variable' : ['<%=', '%>'],
'interpolation': ['#<' , '>']
}) %}
As you can see, it's not recommanded to use this feature Customizing the Syntax
BTW here's a quick and easy example to explain how to use custom delimiters in symfony:
service.yml
services:
templating_lexer:
public: true
parent: templating.engine.twig
class: Acme\YourBundle\Twig\TwigLexerEngine
TwigLexerEngine
namespace Acme\YourBundle\Twig;
use Symfony\Bundle\TwigBundle\TwigEngine;
class TwigLexerEngine extends TwigEngine
{
public function setTwigLexer($lexer)
{
$this->environment->setLexer($lexer);
return $this;
}
}
Your controller
public function yourAction()
{
$lexer = new \Twig_Lexer($this->get('twig'), array(
'tag_comment' => array('{*', '*}'),
'tag_block' => array('{', '}'),
'tag_variable' => array('{$', '}'),
));
$templating = $this->get('templating_lexer');
$templating->setTwigLexer($lexer);
return $templating->renderResponse('YourBundle::template.html.twig');
}
I want to use Twig (v1.15.0) in my project in order to replace our homemade template engine. It uses specific delimiters to replace variables, [[...]], or to manage localized strings, [% ... %].
<table>
<tr>
<td>[%myLocalizedString%]</td>
<td>[[myVarToReplace]]</td>
</tr>
</table>
I don't want to modify all existing templates to replace each delimiter, for legacy and compatibility reasons.
For variables, it is not a big deal, I just have to set the Twig lexer's options :
$twig = new Twig_Environment();
$lexer = new Twig_Lexer($twig, array(
'tag_comment' => array('{#', '#}'),
'tag_block' => array('{%', '%}'),
'tag_variable' => array('[[', ']]'), // was array('{{', '}}')
'interpolation' => array('#{', '}'),
));
$twig->setLexer($lexer);
In the case of localization delimiters is not as simple. Initialy I wanted to do something like that :
$twig = new Twig_Environment();
$lexer = new Twig_Lexer($twig);
$lexer->addDelimiter('tag_localize', array('[%', '%]'), 'functionToCall');
But it does not seems to be implemented yet.
The ultimate solution is to extend the Lexer class and use it in my twig environnement.
But I would like to avoid that.
Is there any better solution ?
I'm sure you know this already: in Twig you do translations using the I18n extension. That extension provides a "trans" tag: see i18n docs
I see no way you can convert your translation syntax [%Hello world%] into the Twig way {% trans %}Hello world{% endtrans %} by extendling the Lexer class, because {% is a block element and trans a tag defined in the I18n extension. You could build your own translation logic, but I think it is much easier to create a preprocessor that replaces [% with {% trans %] and %} with {% endtrans %}.
I imagine it could work like this (untested):
class MYTwigEnvironment extends Twig_Environment {
public function compileSource($source, $name = null) {
/*
* code to replace '[%' with '{% trans %}' in $source
* comes here ...
*/
return parent::compileSource($source, $name = null);
}
}
As far as I understand, this way the template caching should stay intact.
regards