Symfony2 - Twig extension does not exist in Twig file - symfony

I am trying to register (read the docs) a Twig extension and everything seems to be correct except it's not being found in the Twig file.
Getting the following error:
The function "getPostCount" does not exist in AcmeDemoBundle:Page:index.html.twig at line 17
Can someone show me what I am doing wrong?
services.yml
acme.twig.acme_extension:
class: Acme\DemoBundle\Twig\PostExtension
tags:
- { name: twig. extension }
arguments:
em: "#doctrine.orm.entity_manager"
PostExtension.php
class PostExtension extends \Twig_Extension
{
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function getFilters()
{
return array(
);
}
public function getFunctions()
{
return array(
'getPostCount' => new \Twig_Function_Method($this,'getPostCount')
);
}
public function getPostCount($year, $month)
{
return $this->$em->getRepository('AcmeDemoBundle:Post')
->getPostCountsByMonth($year, $month);
}
public function getName()
{
return 'post_extension';
}
}
Twig
{{ getPostCount('2014', 'July') }}

In services.yml:
Remove the extra space in twig.extension.
tags:
- { name: twig.extension }

Related

set session value in symfony2 view

I want to set session value in Symfony2 view both in PHP and TWIG . I can get the session value in view like this
$app->getSession()->get('whatever');
But didn't know how to set the session value in view . Kindly any one help .
This is your twig extension you can put this code under YourBundle/Twig/SessionExtension.php
namespace YourBundle\Twig;
use Symfony\Component\HttpFoundation\Session\Session;
class SessionExtension extends \Twig_Extension
{
private $session;
public function __construct(Session $session) {
$this->session = $session;
}
public function getFilters()
{
return array(
new \Twig_SimpleFilter('setSession', array($this, 'setSession')),
);
}
public function setSession($key, $val){
$this->session->set($key, $val);
return true;
}
public function getName()
{
return 'session_extension';
}
}
Add to service for twig;
yourbundle.twig.session_extension:
class: YourBundle\Twig\SessionExtension
arguments:
session: "#session"
tags:
- { name: twig.extension }
Now , you can use in twig;
{{ 'test'|setSession('myKey', 'myValue') }}

Register Twig extensions and cache directory

I have problems with adding Twig extensions.
I have Bundle controllers extending custom BaseController class:
class DefaultController extends BaseController
And there's my BaseController class (only part of it).
class BaseController extends Controller {
public function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container = null)
{
parent::setContainer($container);
$this->onContainerSet();
}
public function onContainerSet()
{
// many other tasks
$this->get('twig')->addExtension(new \Twig_Extension_StringLoader());
$this->get('twig.loader')->addPath('../app');
$function = new \Twig_SimpleFunction('stars', function ($number, $maximum_stars = 5) {
$this->get('twig')->addGlobal('star_number',sprintf("%.1f",$number));
$this->get('twig')->addGlobal('star_max',$maximum_stars);
$full_stars = floor($number);
$half_stars = ($number - $full_stars) * 2;
$empty_stars = $maximum_stars - $full_stars - $half_stars;
$this->get('twig')->addGlobal('full_stars_number',$full_stars);
$this->get('twig')->addGlobal('half_stars_number',$half_stars);
$this->get('twig')->addGlobal('empty_stars_number',$empty_stars);
echo $this->renderView(
'views/stars.html.twig'
);;
});
$function2 = new \Twig_SimpleFunction('inurl', function ($anchor, $code) {
echo ''.$anchor."";
});
$this->get('twig')->addFunction($function);
$this->get('twig')->addFunction($function2);
}
}
The problem:
When I clear cache directory I have first message:
CRITICAL - Uncaught PHP Exception LogicException: "Unable to register
extension "string_loader" as extensions have already been
initialized." at ...\vendor\twig\twig\lib\Twig\Environment.php line
660 Context: {"exception":"Object(LogicException)"}
But when I reload page (cache folder is already created) it works fine (no exception).
However if I comment line:
// $this->get('twig')->addExtension(new \Twig_Extension_StringLoader());
and clear cache directory I have exception:
CRITICAL - Uncaught PHP Exception LogicException: "Unable to add
function "stars" as extensions have already been initialized." at
...\vendor\twig\twig\lib\Twig\Environment.php line 946 Context:
{"exception":"Object(LogicException)"}
So it seems that when cache directory doesn't exist from some reason adding any Twig extensions doesn't work (extensions have already been initialized) as I would like but when cache directory is already created everything works fine.
Question - how to solve it in the simplest way?
Create your class in YourBundle\Twig
class YourExtension extends \Twig_Extension
{
/**
* #var Router
*/
protected $router;
function __construct(Router $router)
{
$this->router = $router;
}
/**
* #return array
*/
public function getFilters()
{
return [
new \Twig_SimpleFilter('my_filter', [$this, 'myFilter'], ['is_safe' => ['html']]),
];
}
/**
* #return string
*/
public function myFilter(User $user)
{
return 'FILTERED: ' . $user->getName();
}
/**
* #return string
*/
public function getName()
{
return 'my_filter_extension';
}
}
Then, register your extension as a service: ( in this case I inject router as an argument )
yourbundle.twig.my_filter_extension:
class: Acme\YourBundle\Twig\YourExtension
arguments: [#router]
tags:
- { name: twig.extension }
If you want to enable Twig_Extension_StringLoader, add to your services:
yourbundle.twig.extension.loader:
class: Twig_Extension_StringLoader
tags:
- { name: 'twig.extension' }
Twig_Extension_StringLoader is not loaded by default.
What I finally did to achieve result (maybe someone will have similar problem in the future):
In config.yml I've added:
services:
yourbundle.twig.extension.loader:
class: Twig_Extension_StringLoader
tags:
- { name: 'twig.extension' }
yourbundle.twig.stars_extension:
class: Mnab\Twig\Stars
tags:
- { name: 'twig.extension' }
yourbundle.twig.inurl_extension:
class: Mnab\Twig\InternalUrl
tags:
- { name: 'twig.extension' }
in my BaseController I only left from question code:
$this->get('twig.loader')->addPath('../app');
but also added:
$this->get('twig')->addGlobal('internal_links',$this->internalLinks);
to use it in Twig extension
And I've create 2 classes:
<?php
//InternalUrl.php
namespace Mnab\Twig;
use Symfony\Component\DependencyInjection\ContainerInterface;
class InternalUrl extends \Twig_Extension {
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('inurl', array($this, 'inUrlFunction'), array('needs_environment' => true, 'is_safe' => array('html'))),
);
}
public function inUrlFunction(\Twig_Environment $env, $anchor, $code)
{
return ''.$anchor."";
}
public function getName()
{
return 'inurl_extension';
}
}
and
<?php
// Stars.php
namespace Mnab\Twig;
class Stars extends \Twig_Extension
{
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('stars', array($this, 'starsFunction'), array('needs_environment' => true, 'is_safe' => array('html'))),
);
}
public function starsFunction(\Twig_Environment $env, $number, $maximum_stars = 5)
{
$env->addGlobal('star_number',sprintf("%.1f",$number));
$env->addGlobal('star_max',$maximum_stars);
$full_stars = floor($number);
$half_stars = ($number - $full_stars) * 2;
$empty_stars = $maximum_stars - $full_stars - $half_stars;
$env->addGlobal('full_stars_number',$full_stars);
$env->addGlobal('half_stars_number',$half_stars);
$env->addGlobal('empty_stars_number',$empty_stars);
return $env->render(
'views/stars.html.twig'
);
}
public function getName()
{
return 'stars_extension';
}
}
Now it seems to work regardless of cache is created or not. So it seems to better register services when you want to use Twig Extensions than registering Extensions in Controller.

Doctrine connection in Twig extension - Symfony

According to what I've read online, it should be possible to create Doctrine connection inside Twig extensions file. I'd like to create an extension with filter that would recieve id of category and then return position of that category in the categery tree as it was established in database.
Twig extension file:
(Symfony/src/MyProject/AdminBundle/Twig/MyExtension.php)
<?php
namespace MyProject\AdminBundle\Twig;
class ToolboxExtension extends \Twig_Extension
{
protected $em;
public function __construct($em)
{
$this->em = $em;
}
public function getFilters()
{
return array(
new \Twig_SimpleFilter('path', array($this, 'categoryPath'))
);
}
public function categoryPath($category_id) {
$repository = $this->$em->getRepository('MyProjectAdminBundle:Category');
$category = $repository->findOneById($category_id);
$path = $repository->getPath($category);
return implode(">", $path);
return $category;
}
public function getName()
{
return 'toolbox_extension';
}
}
Services configuration file:
(Symfony/src/MyProject/AdminBundle/Resources/config/services.yml)
services:
myproject.twig.toolbox_extension:
class: MyProject\AdminBundle\Twig\MyExtension
tags:
- { name: twig.extension }
arguments:
em: "#doctrine.orm.entity_manager"
But whenever I use this filter categoryPath, Twig crashes. So the template is loaded only until the first usage of this extension.
For me, solution below work perfect. I found post on google groups which resolve my problem with doctrine in Twig extensions.
In my AppBundle\Resources\services.yml:
app.twig.get_province.extension:
class: AppBundle\Twig\GetProvinceExtension
tags:
- { name: twig.extension }
arguments: [ '#doctrine.orm.entity_manager' ]
In AppBundle\Twig\GetProvinceExtention.php:
class GetProvinceExtension extends \Twig_Extension {
/**
* #var EntityManager
*/
protected $em;
/**
* GetProvinceExtension constructor.
* #param EntityManager $em
*/
public function __construct(EntityManager $em) {
$this->em = $em;
}
public function getFilters() {
return [
new \Twig_SimpleFilter('province', [$this, 'provinceFilter']),
];
}
public function provinceFilter($code) {
$province = $this->em->getRepository("AppBundle:Province")
->findOneBy([ "code" => $code ]);
return $province->getName();
}
}
Try replacing $this->em-> with $this->$em->.
Try replacing
$this->em->
with $this->$em->
and replacing
$this->em = $em;
with
$this->em = $em['em'];
and
arguments:
em: "#doctrine.orm.entity_manager"
with
arguments: [em: "#doctrine.orm.entity_manager"]
You can use
{% set Products = repository('Entity\\Products').findAll() %}
inside your twig
Then you can do a foreach by using
{% Product in Products %}
<p>{{ Product.name }}</p>
{% endfor %}

Trying to get this Twig (Symfony2) function extension to register

I'm getting an error:
"twig extension FatalErrorException: Error: Class Acme\Bundle\MyBundle\Twig not found in app/cache/dev/appDevDebugProjectContainer.php"
I've cleared the cache but this does nothing.
I'm just trying to test setting it up and then I can put in all my logic.
--
A file named MyTwigExtensions.php
namespace Acme\Bundle\MyBundle\Twig;
class MyTwigExtensions extends \Twig_Extension
{
public function getFunctions() {
return array(
new Twig_SimpleFunction('link', 'generate_link')
);
}
public function generate_link($params) {
return "THE-LINK-HERE";
}
public function getName() {
return "link";
}
}
In services.yml
services:
my_extension.twig.extension:
class: Acme\Bundle\MyBundle\Twig
arguments: []
tags:
- { name: twig.extension }
You have to enter fully qualified name of the extension class.
services:
my_extension.twig.extension:
class: Acme\Bundle\MyBundle\Twig\MyTwigExtensions # <--- here
arguments: []
tags:
- { name: twig.extension }
for service reprensetation format is sth like this. addArrowInCode below is the name of the method used in twig:
twig.extension.addArrowInCode:
class: Acme\DemoBundle\Twig\AddArrowInCodeExtension
tags:
- { name: twig.extension }
and for this extension you should have like...
class AddArrowInCodeExtension extends \Twig_Extension
{
function addArrowInCodeFilter($code, $separator = '⇒')
{
// do sth setting final
return $final;
}
/**
* Returns a list of filters to add to the existing list.
*
* #return array An array of filters
*/
public function getFilters()
{
return array(
'addArrowInCode' => new Twig_Filter_Method($this, 'addArrowInCodeFilter', array('needs_environment' => false)),
);
}
public function getName()
{
return 'addArrowInCode';
}
}
hope it helps
Please check this code.
namespace Acme\Bundle\MyBundle\Twig;
class MyTwigExtensions extends \Twig_Extension
{
public function getFunctions() {
return array(
new Twig_SimpleFunction('link', array($this, 'generate_link')) // <== changed here
);
}
public function generate_link($params) {
return "THE-LINK-HERE";
}
public function getName() {
return "link";
}
}
In services.yml
services:
my_extension.twig.extension:
class: Acme\Bundle\MyBundle\Twig\MyTwigExtensions # <== changed here
arguments: []
tags:
- { name: twig.extension }

How can I create a symfony twig filter?

For instance my bundle namespace is Facebook\Bundle\FacebookBundle\Extension.
Using this how can I create a twig extension ?
It's all here: How to write a custom Twig Extension.
1. Create the Extension:
// src/Facebook/Bundle/Twig/FacebookExtension.php
namespace Facebook\Bundle\Twig;
use Twig_Extension;
use Twig_Filter_Method;
class FacebookExtension extends Twig_Extension
{
public function getFilters()
{
return array(
'myfilter' => new Twig_Filter_Method($this, 'myFilter'),
);
}
public function myFilter($arg1, $arg2='')
{
return sprintf('something %s %s', $arg1, $arg2);
}
public function getName()
{
return 'facebook_extension';
}
}
2. Register an Extension as a Service
# src/Facebook/Bundle/Resources/config/services.yml
services:
facebook.twig.facebook_extension:
class: Facebook\Bundle\Twig\AcmeExtension
tags:
- { name: twig.extension }
3. Use it
{{ 'blah'|myfilter('somearg') }}
You can also create twig functions by using the getFunctions()
class FacebookExtension extends Twig_Extension
{
public function getFunctions()
{
return array(
'myFunction' => new Twig_Filter_Method($this, 'myFunction'),
);
}
public function myFunction($arg1)
{
return $arg1;
}
Use your function like this:
{{ myFunction('my_param') }}
The Twig_Filter_Method class is DEPRECATED since Symfony 2.1
Please use the Twig_SimpleFilter class instead as showed in the following example:
\src\Acme\Bundle\CoreBundle\Twig\DatetimeExtension.php
<?php
namespace Acme\Bundle\CoreBundle\Twig;
use Symfony\Component\DependencyInjection\ContainerInterface;
class DatetimeExtension extends \Twig_Extension
{
/**
* #var \Symfony\Component\DependencyInjection\ContainerInterface
*/
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getFilters()
{
return array(
'dateFormat' => new \Twig_SimpleFilter('dateFormat', array($this, 'dateFormat')),
'datetimeFormat' => new \Twig_SimpleFilter('datetimeFormat', array($this, 'datetimeFormat'))
);
}
/**
* #param mixed $date
* #return string
*/
public function dateFormat($date)
{
$format = $this->container->getParameter('acme_core.date_format');
return $this->format($date, $format);
}
/**
* #param mixed $date
* #return string
*/
public function datetimeFormat($date)
{
$format = $this->container->getParameter('acme_core.datetime_format');
return $this->format($date, $format);
}
/**
* #param mixed $date
* #param string $format
* #throws \Twig_Error
* #return string
*/
private function format($date, $format)
{
if (is_int($date) || (is_string($date) && preg_match('/^[0-9]+$/iu', $date))) {
return date($format, intval($date, 10));
} else if (is_string($date) && !preg_match('/^[0-9]+$/', $date)) {
return date($format, strtotime($date));
} else if ($date instanceof \DateTime) {
return $date->format($format);
} else {
throw new \Twig_Error('Date or datetime parameter not valid');
}
}
public function getName()
{
return 'datetime_extension';
}
}
\src\Acme\Bundle\CoreBundle\Resources\config\services.yml
services:
acme_core.twig.datetime_extension:
class: Acme\Bundle\CoreBundle\Twig\DatetimeExtension
arguments: [#service_container]
tags:
- { name: twig.extension }
Usage example:
{{ value|datetimeFormat }}
Symfony documentation: http://symfony.com/doc/master/cookbook/templating/twig_extension.html
Twig documentation: http://twig.sensiolabs.org/doc/advanced.html#id3
None of the given answers worked for Symfony 3.4 and above.
For Symfony 3.4, 4.x
// src/TwigExtension/customFilters.php
namespace App\TwigExtension;
use Twig\TwigFilter;
class customFilters extends \Twig_Extension {
public function getFilters() {
return array(
new TwigFilter('base64_encode', array($this, 'base64_en'))
);
}
public function base64_en($input) {
return base64_encode($input);
}
}
And then in your twig template you can do
{{ 'hello world' | base64_encode }}
Thats it. For detailed explanation, you could check out the reference.
Reference: DigitalFortress

Resources