Symfony2 twig function template variable - symfony

I'm trying to add a common twig function that will be called in layout but the result will be specific for each page depending on given parameters.
I don't want to add each parameters in the twig function.
Is there any way to find those parameters?
exemple:
layout.html.twig:
{{ my_twig_function() }}
list.html.twig
{% extends "::layout.html.twig" %}
{% if test is defined%}test is defined{% endif %}
myTwigExtension.php:
public function getFunctions()
{
return array(
'my_twig_function' => new \Twig_Function_Method($this, 'getParams'),
);
}
public function getParams()
{
// here a way to find all parameters passed to the list.html.twig
return "ok";
}
Any ideas?

Have you added your MyTwigExtension class as a service ?
services:
twig.twig_extension:
class: Namespace\NameBundle\Twig\MyTwigExtension
tags:
- { name: twig.extension }
arguments: []

Related

Symfony Twig Call Continer

I'm using Symfony 3 with Twig.
In every route I need to make a call to entity:
$posts = $this->getDoctrine()
->getRepository('AppBundle:Posts')
->findAll();
There is a way that I can do this globally?
And call it from the twig instead of in the route?
You can write a service that will do it and inject it as a twig global
#app/config.yml
twig:
globals:
posts_service: '#app_posts_service'
Then define the service
#app/services.yml
services:
app_posts_service:
class: AppBundle\Service\PostsService
arguments: ["#doctrine"]
Make sure your services file is getting imported into your config:
#app/config.yml
imports:
- { resource: services.yml }
Then define the service:
// src/AppBundle/Service/PostsService.php
namespace AppBundle\Service;
class PostsService
{
protected $doctrine;
public function __construct($doctrine)
{
$this->doctrine = $doctrine;
}
public function getAllPosts()
{
return $this->doctrine
->getManager()
->getRepository('AppBundle:Posts')
->findAll();
}
}
Then use it in any twig file like:
{%for post in posts_service.getAllPosts() %}
{{ post.title }} {# or whatever #}
{% endfor %}

is it possible to call an entity from view ( php template ) in symfony2

I got a dynamic content in layout which is takes vales from database. What is the best way to achieve this rather than passing values from controller.
Is it possible to call an entity from view? I am using php template.
there's no point in a MVC context to call a model entity without the use of a controller, at least you can fetch the updated content using a ajax call to a controller which returns a JsonResponse to avoid the page refresh
This is possible via the twig extension.
Register a twig extension
TWIG EXTENSION
Pass to __constructor() - #doctrine service
services.yml
my.twig.extension:
class: twig\namespace\path
arguments:
kernel: "#kernel"
doctrine: "#doctrine"
tags:
- { name: twig.extension }
Constructor of the new twig extension
protected $kernel;
protected $doctrine;
public function __construct($kernel, $doctrine)
{
$this->kernel = $kernel;
$this->doctrine = $doctrine;
}
Write up some method:
/** #var string $repository. Example: AppBundle:Product' */
public function myEntity($repository)
{
$manager = $this->doctrine->getManager();
return $manager->getRepository($repository);
}
Register myEntity method in twig extension:
public function getFunctions()
{
return array(
'myEntity' => new \Twig_Function_Method($this, 'myEntity'),
);
}
Now in your twig templates you can access any repository:
For example:
{#
myEntity('SomeBundle:coolEntity').find()
myEntity('SomeBundle:coolEntity').findAll()
myEntity('SomeBundle:coolEntity').findBy()
...
#}
{% for item in myEntity('SomeBundle:coolEntity').findAll() %}
{{ item.getId() }}
{% endfor %}

How do I check for the existence of a bundle in twig?

My application is made up with eight bundles, within my main layout I would like to check if a certain bundle exists so I can include a sub template, how do I go about doing this?
Thanks to #DonCallisto, I decided to make a twig function to use in my templates, the following is my twig extension.
<?php
namespace MG\AdminBundle\Twig;
use Symfony\Component\DependencyInjection\ContainerInterface;
class Bundles extends \Twig_Extension {
protected $container;
public function __construct(ContainerInterface $container) {
$this->container = $container;
}
public function getFunctions()
{
return array(
new \Twig_SimpleFunction(
'bundleExists',
array($this, 'bundleExists')
),
);
}
public function bundleExists($bundle){
return array_key_exists(
$bundle,
$this->container->getParameter('kernel.bundles')
);
}
public function getName() {
return 'mg_admin_bundles';
}
}
I then registered it in my services.yml
services:
mg_admin.bundles.extension:
class: MG\AdminBundle\Twig\Bundles
arguments: [#service_container]
tags:
- { name: twig.extension }
Now in my twig templates I can check for registered bundles like this:
{% if bundleExists('MGEmailBundle') %}
{% include 'MGEmailBundle:SideBar:sidebar.html.twig' %}
{% endif %}
$this->container->getParameter('kernel.bundles');
will return all registered bundles (class names). You could pass that list - or parse it direclty - into a controller and pass it to your twig view
Then you should easily reach your target
If the bundle you want to check is a specific bundle, and you know the main class name, the easiest way may be:
if (class_exists('Acme\CommentBundle\AcmeCommentBundle'))
{
// Bundle exists and is loaded by AppKernel...
}

How to render a controller in a custom Twig extension using Symfony2

I've written a custom Twig function that should render some HTML. My first idea was to create a new Controller for the rendering logic and use that in the Twig extension. But it does not work as it throws this error when calling {{ button() }} in a template:
FATALERROREXCEPTION: ERROR: CALL TO A MEMBER FUNCTION GET() ON A NON-OBJECT IN /FOO/VENDOR/SYMFONY/SYMFONY/SRC/SYMFONY/BUNDLE/FRAMEWORKBUNDLE/CONTROLLER/CONTROLLER.PHP LINE 106
The Twig extensions basically work (I've already implemented some simple helpers not shown here).
The Controller (unnecessary code stripped):
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class ButtonController extends Controller {
public function showAction()
{
[...]
return $this->render(
'AcmeDemoBundle:Default:button.html.twig', array($vars)
);
}
}
The Twig extension:
class AcmeExtension extends \Twig_Extension {
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('button', array($this, 'button'), array('is_safe' => array('html'))),
);
}
public function button()
{
$controller = new ButtonController();
return $controller->showAction();
}
}
If I understand, you want, when you call the {{ button() }} function, render a controller.
Why not doing a simple:
{{ render(controller('AcmeDemoBundle:Button:show')) }}
This avoid creating a tricky extension for such an easy task.
If you really, really want to call {{ button() }}, you can create a macro, such as:
{% macro button() %}
{{ render(controller('AcmeDemoBundle:Button:show')) }}
{% endmacro %}
{% from _self.templateName import 'button' %}
And you can now use {{ button() }} on the same template.

Get controller name in TWIG template

I am learning symfony2.3, and I am getting an error when I try to get controller name in twig template.
Controller:
namespace Acme\AdminBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function indexAction($name)
{
return $this->render('AcmeAdminBundle:Default:index.html.twig', array('name' => $name));
}
}
In my TWIG template:
{% extends '::base.html.twig' %}
{% block body %}
{{ app.request.get('_template').get('controller') }}
Hello {{ name }}!!!
{% endblock %}
Output:
Impossible to invoke a method ("get") on a NULL variable ("") in AcmeAdminBundle:Default:index.html.twig at line 3
I want output as "Default"
I am using symfony 2.3, I have also tried on symfony 2.1 but on both version generates the same error.
use this line to display a controller name in twig:
{{ app.request.attributes.get("_controller") }}
Many months ago I had the same problem as you, and "googling" I found a working code and I had adapted it to my necessities. Here we go:
1 - We need to define a TWIG extension for that. We create the folder structure Your\OwnBundle\Twig\Extension if you haven't defined yet.
2 - Inside this folder we create the file ControllerActionExtension.php which code is:
namespace Your\OwnBundle\Twig\Extension;
use Symfony\Component\HttpFoundation\Request;
/**
* A TWIG Extension which allows to show Controller and Action name in a TWIG view.
*
* The Controller/Action name will be shown in lowercase. For example: 'default' or 'index'
*
*/
class ControllerActionExtension extends \Twig_Extension
{
/**
* #var Request
*/
protected $request;
/**
* #var \Twig_Environment
*/
protected $environment;
public function setRequest(Request $request = null)
{
$this->request = $request;
}
public function initRuntime(\Twig_Environment $environment)
{
$this->environment = $environment;
}
public function getFunctions()
{
return array(
'get_controller_name' => new \Twig_Function_Method($this, 'getControllerName'),
'get_action_name' => new \Twig_Function_Method($this, 'getActionName'),
);
}
/**
* Get current controller name
*/
public function getControllerName()
{
if(null !== $this->request)
{
$pattern = "#Controller\\\([a-zA-Z]*)Controller#";
$matches = array();
preg_match($pattern, $this->request->get('_controller'), $matches);
return strtolower($matches[1]);
}
}
/**
* Get current action name
*/
public function getActionName()
{
if(null !== $this->request)
{
$pattern = "#::([a-zA-Z]*)Action#";
$matches = array();
preg_match($pattern, $this->request->get('_controller'), $matches);
return $matches[1];
}
}
public function getName()
{
return 'your_own_controller_action_twig_extension';
}
}
3 - After that we need to specify the service for TWIG to be recognized:
services:
your.own.twig.controller_action_extension:
class: Your\OwnBundle\Twig\Extension\ControllerActionExtension
calls:
- [setRequest, ["#?request="]]
tags:
- { name: twig.extension }
4 - Cache clear to make sure everything is ok:
php app/console cache:clear --no-warmup
5 - And now, if I'm not forgetting anything, you will be able to access those 2 methods in a TWIG template: get_controller_name() and get_action_name()
6 - Examples:
You are in the {{ get_action_name() }} action of the {{ get_controller_name() }} controller.
This will output something like: You are in the index action of the default controller.
You can also use to check:
{% if get_controller_name() == 'default' %}
Whatever
{% else %}
Blablabla
{% endif %}
And that's all!! I hope I helped you, mate :)
Edit: Take care about the clearing cache. If you don't use --no-warmup parameter maybe you will realize that nothing is shown in your templates. That's because this TWIG Extension uses the Request to extract the Controller and Action names. If you "warm up" the cache, the Request is not the same as a browser request, and the methods can return '' or null
Since Symfony 3.x, service request is replaced by request_stack, and Twig Extension declaration changed since Twig 1.12.
I will correct the answer of Dani (https://stackoverflow.com/a/17544023/3665477) :
1 - We need to define a TWIG extension for that. We create the folder structure AppBundle\Twig\Extension if you haven't defined yet.
2 - Inside this folder we create the file ControllerActionExtension.php which code is:
<?php
namespace AppBundle\Twig\Extension;
use Symfony\Component\HttpFoundation\RequestStack;
class ControllerActionExtension extends \Twig_Extension
{
/** #var RequestStack */
protected $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function getFunctions()
{
return [
new \Twig_SimpleFunction('getControllerName', [$this, 'getControllerName']),
new \Twig_SimpleFunction('getActionName', [$this, 'getActionName'])
];
}
/**
* Get current controller name
*
* #return string
*/
public function getControllerName()
{
$request = $this->requestStack->getCurrentRequest();
if (null !== $request) {
$pattern = "#Controller\\\([a-zA-Z]*)Controller#";
$matches = [];
preg_match($pattern, $request->get('_controller'), $matches);
return strtolower($matches[1]);
}
}
/**
* Get current action name
*
* #return string
*/
public function getActionName()
{
$request = $this->requestStack->getCurrentRequest();
if (null !== $request) {
$pattern = "#::([a-zA-Z]*)Action#";
$matches = [];
preg_match($pattern, $request->get('_controller'), $matches);
return $matches[1];
}
}
public function getName()
{
return 'controller_action_twig_extension';
}
}
3 - After that we need to specify the service for TWIG to be recognized:
app.twig.controller_action_extension:
class: AppBundle\Twig\Extension\ControllerActionExtension
arguments: [ '#request_stack' ]
tags:
- { name: twig.extension }
4 - Cache clear to make sure everything is ok:
php bin/console cache:clear --no-warmup
5 - And now, if I'm not forgetting anything, you will be able to access those 2 methods in a TWIG template: getControllerName() and getActionName()
6 - Examples:
You are in the {{ getActionName() }} action of the {{ getControllerName() }} controller.
This will output something like: You are in the index action of the default controller.
You can also use to check:
{% if getControllerName() == 'default' %}
Whatever
{% else %}
Blablabla
{% endif %}
I don't really see WHY you would need this.
You might better send parameters into your view.
But if you really need it this way, here's a solution:
Your error comes from the second get method
request = app.request // Request object
NULL = request.get('_template') // Undefined attribute, default NULL
NULL.get('controller') // Triggers error
If you want to get the controller called during the request you can access it via the key _controller of the request attribute
app.request.attribute.get('_controller')
Will return
Acme\AdminBundle\Controller\DefaultController::indexAction
You can then parse it the way you want.
Note that this won't return the controller instance, only its name and method called
To get the controller - {{ app.request.attributes.get('_controller') }}
To get the Action - {{ app.request.attributes.get('_template').get('name') }}
Found at - http://forum.symfony-project.org/viewtopic.php?f=23&t=34083
It can vary. If you're using annotations in the controller, e.g. #Template("AcmeDemoBundle:Default:index"), trying to access app.request.get('_template') in your Twig template will return a string e.g. "AcmeDemoBundle:Default:index". So you might need to access it like this:
{% set _template = app.request.get('_template')|split(':') %}
{% set controller = _template[1] %}
{% set bundle = _template[0] %}
If you're not using annotations then you can use the app.request.get('_template').get('_controller')
Controller:
{{ app.request.attributes.get('_template').get('controller') }}
Action:
{{ app.request.attributes.get('_template').get('name') }}
enjoy ;)

Resources