Pergunta

Currently I place my function in a class and pass an instance of this class into template and call my required function as a class method.

{{ unneededclass.blah() }}

I need to do like below

{{ blah() }}

Is it possible?

Foi útil?

Solução

Update 5/14/2015

Commenters point out that I'm mostly wrong. If you really need a function, and not a filter or macro, you can do it as suggested in the Twig docs:

$twig = new Twig_Environment($loader);
$function = new Twig_SimpleFunction('blah', function () {
   // ...
});
$twig->addFunction($function);

And use like

{{ blah() }}

In short, no, this is not possible.

However, hope is not lost!

Filters

If this function blah() of yours is meant to modify an existing variable, then it is a filter.

An example:

//in your PHP
function format_date($date_string,$format_string) {
    return date($format_string,strtotime($date_string));
}
$twig_env->addFilter('format_date',new Twig_Filter_Function('format_date'));

{# in your template #}
{{ some_date|format_date('n/j/Y') }}

(The first argument is the variable you are filtering, the second is supplied by normal means)

Macros

If, as you have indicated above, your function simply outputs HTML, then it is a good candidate for a macro.

An example:

{# in your template #}
{% macro say_hello() %}
<p>Oh! Hello, world!</p>
{% endmacro %}

{# ... later on ... #}
{{ _self.say_hello() }}

Or with parameters:

{% macro input(name,value,type) %}
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}

{{ _self.input('phone_number','867-5309') }}
{{ _self.input('subscribe','yes','checkbox') }}

Why?

The thing to remember is that Twig templates represent a view, in terms of MVC. This means they are isolated in terms of their environment, and can only represent the context you pass them via the data array you pass in the $template->render() method.

This is a good thing, as it decouples your presentation from your logic and data. If you can arbitrarily call functions, then you suddenly increase that coupling, which is a bad thing.

The other reason for this is the way PHP handles callbacks. Think about how you would have to pass that function into your template... Probably something like this:

function blah() {
    return "<p>Oh! Hello, world!</p>";
}

$template = $twig_env->loadTemplate('template.html');
echo $template->render(array('blah'=>'blah'));

In your template, the context variable blah is now just a string containing 'blah'.

In vanilla PHP, when you use variable functions like this (try to use a string variable like a function), it (more or less) performs a lookup for that function, then calls it. You are not passing the function, just it's name.

The thing is, you cannot possibly pass a function into a template, because PHP's only mechanism for doing this is by name-string, and once inside a template, that name is no longer a function name and just a string.

A little bit long winded, but I hope that helps!

If you want more documentation, the official docs are here.

Outras dicas

I was as lost as you were, my friend, but after searching the web for an answer and found none, I decided to see if I could do it myself. Therefore, I wanted to post my solution here (even though I know this post is old), because this is the first hit on google if you search for this problem.

Here it go, it is quite simple actually:

I made a class which contains my functions and variables, for example:

class functionContainer{
        function getRandomNumber()
        {
                return rand();
        }
}
$values = array(
'functions'=> new functionContainer()
);

So now we have $values as an array, which contains this object with a function "getRandomNumber()".

When rendering the template file, include this class as a value:

$twig->render('random.html', $values);

This way, inside the template file, you can just call this method to call the function and get your result:

{{ functions.getRandomNumber }}

Although you cannot PHP callable directly, twig is extensible. You can add a callable filter, so you can apply to PHP functions passed to template.

namespace My\Twig\Extension;

class LambdaFilter extends \Twig_Extension {

    public function getName() {
        return 'lambda_filter';
    }

    public function getFilters() {
        return array(
            new \Twig_SimpleFilter('call', array($this, 'doCall'))
        );
    }

    public function doCall() {
        $arguments = func_get_args();
        $callable = array_shift($arguments);
        if(!is_callable($callable)) {
            throw new InvalidArgumentException();
        }
        return call_user_func_array($callable, $arguments);
    }

}

Now if you pass variable my_func to template, you can do my_func|call(arg1, arg2). You can even do higher order functions "array_filter"|call(my_array, my_func) and you can always do more things in filter like accepting array as parameters and so on.

Full answer: http://twig.sensiolabs.org/doc/advanced.html#id2

I prefer to use Twig Extension like this:

namespace Some\Twig\Extensions;

class MenuExtensions extends \Twig_Extension
{
    public function getFunctions()
    {
        return array(
            new \Twig_SimpleFunction('sidebar_menu', [$this, 'generate_sidebar_menu']),
        );
    }

    public function generate_sidebar_menu($menu){
        return $menu;
    }

    public function getName()
    {
        return 'menu';
    }
}

In template:

{{ sidebar_menu('some text') }}

Using Anonymous class

Create anonymous class in manager/controller/service ..

     $functions = new class($router)
    {
        public function __construct($router)
        {
            $this->router = $router;
        }

    public function getRowUrl(FileManager $fileManager)
    {
        if ($fileManager->isNode()) {
            return $this->router->generate('...', ['parent' => ...]);
        }
        return $this->router->generate('...', ['entity' => ...]);
    }
};

Paste parameter into view

$params=[
            'functions' => $functions

        ];



    return new Response($this->twig->render('...:index.html.twig', $params));

Use function in view

 {% set rowUrl = functions.rowUrl(entity) %}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top