I am using Volt and PHP in phalcon volt partial
I want to iterate a loop and in the loop I have php code that accept a parameter
this is my code
{% for header in headers %}
<th>
<?=gettext( {{header}} );?>
</th>
{% endfor %}
here header is a parameter that is used in php code
but I get this error
what is the right way to rewrite this code
You can configure some services in a better way to help you solve this issue.
If you define your view service like:
$di->set('view', function () {
$view = new View();
$view->setDI($this);
$view->setViewsDir(__DIR__ . '/views/');
$view->registerEngines([
'.volt' => 'voltShared'
]);
return $view;
});
You can define your voltShared service improving the volt compiler with custom functions in this way:
$di->setShared('voltShared', function ($view) use ($di) {
$config = $this->getConfig();
$volt = new VoltEngine($view, $this);
$volt->setOptions([
'autoescape' => false,
'compileAlways' => true,
'stat' => true,
'compiledSeparator' => '_',
'compiledPath' => $config->application->cacheDir.'volt/',
]);
// We add some custom functions
$compiler = $volt->getCompiler();
$compiler->addFunction('gettext', function ($resolvedArgs) {
return 'gettext('.$resolvedArgs.')';
})
return $volt;
});
I'm guessing you are trying to use a raw PHP function here, so correct me if I'm wrong. Unfortunately I forget the exact reason this is (I learned a long time), but you have to manually register PHP functions in the Volt Engine in order to use them. This can be done using the addFunction method of the Engine will allow you to add them. I thought it had been resolved that this wasn't needed anymore, but it was reported in https://github.com/phalcon/cphalcon/pull/12841.
Anyways, using addFunction: I've used the below to get around this:
foreach (get_defined_functions()['Internal'] as $functionName) {
$voltEngine->addFunction($functionName, $functionName);
}
You have to put this in the code that initializes the volt engine.
Reading documentation shows that echo command is using {{ }} instead of normal PHP tag <?= ?>:
{% for header in headers %}
<th>
{{ gettext(header) }}
</th>
{% endfor %}
Related
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 know it's one of the common and frequently asked questions about twig, but i get stuck and confused
Problem: Function output will be escaped and instead of /?some_var=value&maybe_another_var=another_value I get %2F%3Fsome_var%3Dvalue%26maybe_another_var%3Danother_value which completelly ruins my anchor-tags
Goal: I'm trying to prevent auto_urlencoding which happens all the time with my custom function.
What I use: a utilphp::util::add_query_arg() method and a custom Twig-Function (Twig_SimpleFunction)
How I use it: in the code
$twig->addFunction( new \Twig_SimpleFunction('util_add_query_args',
function(){
return \utilphp\util::add_query_arg( func_get_args() );
},
array('is_safe' => array('all')) //also tried array('html') but didn't worked
));
How I use it: in the tempalte
<img src="//{{app_host_name}}/{{ other_url_segments}}{{ util_add_query_args( {'fontStyle' : style } , data.query_string )}}">
this should append &fontStyle=value_from_variable to the given query string and return it as is
data.query_string — may already contain a querysting like ?some_var=value&maybe_another_var=another_value but it can be different from one request to another. In some cases it's empty, so in order to get a valid query sting i have to use util::add_query_arg() since it also covers such cases
Didn't work:
adding |raw to {{ util_add_query_args( {'fontStyle' : style } , data.query_string ) }}
adding
{% autoescape false %}
<img src="//{{app_host_name}}/{{ other_url_segments}}{{ util_add_query_args( {'fontStyle' : style } , data.query_string )}}">
{% endautoescape %}
What do I missing?! How to solve this problem?
Alright, it seems like func_get_args() was "the bad guy". Do not know why exactly, but since I changed
function(){
return \utilphp\util::add_query_arg( func_get_args() );
}
to
function($args_arr, $query_string){
return \utilphp\util::add_query_arg( $args_arr, $query_string );
}
it works... and no {% autoescape false %} or |raw needed too.
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 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)
));
SETUP:
Twig 1.13.1
PHP 5.4.3
PROBLEM:
I am needing help setting up a custom tag that calls a function that i have already built...
Current Code:
Template Code
{% set stories = get_latest_stories(2, sports) %}
{% for story in stories %}
{{ story.headline }} <br>
{% endfor %}
Controller
$function = new Twig_SimpleFunction('getViewStories', function (section, limit) {
return news_stories::getStories(section,limit);
});
$twig->addFunction($function);
$twig->render("storyList.html");
GOAL:
No with that said I would like to use a custom tag like
{% get_latest_stories 2 sports %}
to call the same function as above. The new way looks nicer and is easier to follow
Why not fetch your stories in the controller instead of the template? This does not seem like a job for the view layer...
So, something like this:
$twig->render("storyList.html", array(
'stories' => news_stories::getStories($section, $limit)
));
Then, you'll have a stories variable available in your template.
here is simple example how to write twig extension
Following code is taken from my unfinished project
function file_import($value){
//some code here
return $value;
}
$app['twig']->addFunction('file_import', new Twig_Function_Function('file_import'));
usage
{{ file_import('value') }}