In Symfony 3.4, base.html.twig I have a navbar showing number of the current user's messages. I use a repository entity function to do this. This function must be call every time when template base.html.twig is rendering but I don't want to put this function in all controllers how to do this by event listener before rendering base.html.twig? Override base controller ?
base.html.twig :
....
{{ include top_bar_nav.html.twig }}
....
A custom Twig extension is the correct way:
example in twig:
{{ number_of_current_users() }}
create twig extension like this:
<?php
namespace AppBundle\Twig;
use Doctrine\ORM\EntityRepository;
class UserExtension extends \Twig_Extension
{
/**
* #var EntityRepository
*/
private $userRepository;
/**
* #param EntityRepository $repository
*/
public function __construct(EntityRepository $repository)
{
$this->userRepository = $repository;
}
/**
* {#inheritdoc}
*/
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('number_of_current_users', array($this, 'numberOfCurrentUsers')),
);
}
/**
* #param $sku
*
* #return string
*/
public function numberOfCurrentUsers()
{
return $this->userRepository->getNumberOfCurrentUsers();
}
/**
* {#inheritdoc}
*/
public function getName()
{
return 'user';
}
}
and register it like this:
app.twig.users:
class: AppBundle\Twig\UserExtension
arguments: ['INJECT YOUR USER REPOSITORY HERE']
public: false
tags:
- { name: twig.extension }
I'm trying to call some data in my root twig template and used Twig global variables to achieve that.
When I call the service i get this error:
Type error: Argument 1 passed to AwarenessBundle\Service\AwarenessService::getLatestNews() must implement interface Doctrine\ORM\EntityManagerInterface, none given, called in /var/www/html/myisms/vendor/twig/twig/lib/Twig/Template.php on line 675
Twig code:
{{ news_service.getlatestNews() }}
services.yml:
news.latest:
class: AwarenessBundle\Service\AwarenessService
arguments: [ '#doctrine.orm.entity_manager' ]
AwarenessService
class AwarenessService
{
public function getLatestNews(EntityManagerInterface $em)
{
return $em->getRepository("AwarenessBundle:Feed")
->findBy(array(), array('id' => 'DESC', 10));
}
}
I'm not sure where my problem is but I have to make my service global and I don't know how to do that. Any help would be appreciated.
Pass EntityManagerInterface to your service constructor(instead of getLatestNews method), like this:
class AwarenessService
{
/**
* #var EntityManagerInterface
*/
protected $em;
public function __construct(EntityManagerInterface $em){
$this->em = $em;
}
public function getLatestNews() {
return $this->em->getRepository("AwarenessBundle:Feed")
->findBy(array(), array('id' => 'DESC', 10));
}
}
Service.yml:
news.latest:
class: AwarenessBundle\Service\AwarenessService
//arguments for constructor!
arguments: [ '#doctrine.orm.entity_manager' ]
I'm trying to show a menu of my Bundles, but I need show only the Bundles that are active, how can I get the active Bundles in Twig?
Thanks and Regards!
The list of bundle is stored in the kernel.
You have to create a twig extension BundleExtension and pass the kernel as dependency:
<?php
namespace MyBundle\Twig\Extension;
use Symfony\Component\HttpKernel\KernelInterface;
class BundleExtension extends \Twig_Extension
{
protected $kernel;
public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
}
/**
* {#inheritdoc}
* #see Twig_Extension::getFunctions()
*/
public function getFunctions()
{
return array(
'getBundles' => new \Twig_SimpleFunction('getBundles', array($this, 'getBundles')),
);
}
public function getBundles()
{
return $this->kernel->getBundles();
}
/**
* {#inheritdoc}
* #see Twig_ExtensionInterface::getName()
*/
public function getName()
{
return 'get_bundles';
}
}
Register it as a service:
services:
bundle_extension:
class: MyBundle\Twig\Extension\BundleExtension
arguments: ['#kernel']
tags:
- { name: twig.extension }
And now in your twig template:
{% set bundles = getBundles() %}
{% for bundle in bundles %}
{{ bundle.getName()}}<br/>
{% endfor %}
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 }
I am using sonata media bundle.
and I was wondering how can I access the media url in twig.
I just want the url, I do not need to show the media.
Any suggestions?
You have to use the path media helper:
{% path media, 'small' %}
In the above code, media is an instance of the media entity, and small is the chosen format.
http://sonata-project.org/bundles/media/master/doc/reference/helpers.html#twig-usage
But if you do not want to render the media right there and just store the url in a variable, you need to ask the media provider for the public url.
This was my case, that I needed to pass the url to another template.
I did it creating a custom function in my Twig Extension (see here: http://symfony.com/doc/current/cookbook/templating/twig_extension.html).
Provided that you have the container available in your extension service with $this->container, you can do like this:
public function getMediaPublicUrl($media, $format)
{
$provider = $this->container->get($media->getProviderName());
return $provider->generatePublicUrl($media, $format);
}
Register the function in the extension:
public function getFunctions() {
....
'media_public_url' => new \Twig_Function_Method($this, 'getMediaPublicUrl'),
....
);
}
And call your new helper form your template:
{% set img_url = media_public_url(media, 'small') %}
for instance
regards
#javigzz's is perfect in case of default context. I used custom context, so had to handle $format first taking into account context name:
$provider = $this->container->get($media->getProviderName());
$format = $provider->getFormatName($media, $format);
$url = $provider->generatePublicUrl($media, $format);
Additional Note
Since injecting container is not the best practice, it is better to get provider from the provider pool:
class Foo {
public function __construct(Sonata\MediaBundle\Provider\Pool $pool) {
$this->pool = $pool;
}
public function getUrl($media, $format) {
$provider = $this->pool->getProvider($media->getProviderName());
$format = $provider->getFormatName($media, $format);
$url = $provider->generatePublicUrl($media, $format);
return $url;
}
}
Since #javigzz's answer did not work for me, here is a twig extension that works with the latest version of sonata_media:
namespace Socialbit\SonataMediaTwigExtensionBundle\Twig;
use Sonata\CoreBundle\Model\ManagerInterface;
use Symfony\Component\DependencyInjection\Container;
Class:
/**
* Description of MediaPathExtension
*
* #author thomas.kekeisen
*/
class MediaPathExtension extends \Twig_Extension
{
/**
*
* #var type Container
*/
protected $container;
/**
*
* #var type ManagerInterface
*/
protected $mediaManager;
public function __construct(Container $container, $mediaManager)
{
$this->container = $container;
$this->mediaManager = $mediaManager;
}
public function getFunctions()
{
return array
(
'media_public_url' => new \Twig_Function_Method($this, 'getMediaPublicUrl')
);
}
/**
* #param mixed $media
*
* #return null|\Sonata\MediaBundle\Model\MediaInterface
*/
private function getMedia($media)
{
$media = $this->mediaManager->findOneBy(array(
'id' => $media
));
return $media;
}
public function getMediaPublicUrl($media, $format)
{
$media = $this->getMedia($media);
$provider = $this->container->get($media->getProviderName());
return $provider->generatePublicUrl($media, $format);
}
public function getName()
{
return 'SocialbitSonataMediaTwigExtensionBundleMediaPathExtension';
}
}
services.yml:
services:
socialbit.sonatamediatwigextensionbundle.mediapathextension:
class: Socialbit\SonataMediaTwigExtensionBundle\Twig\MediaPathExtension
public: false
arguments:
- #service_container
- #sonata.media.manager.media
tags:
- { name: twig.extension }
The usage will be the same:
{% set img_url = media_public_url(media, 'reference') %}
{{ dump(img_url) }}
You can use: {% path media, 'reference' %}
#Blauesocke - tried your solution and had exactly the same result for file provider with using both
{% set img_url = media_public_url(media, 'reference') %}
{{ dump(img_url) }}
and
{% path sonata_admin.value, 'reference' %}