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 %}
Related
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 am creating a custom block for dashboard and where I want to display information persisted into the DB. How do I get an instance of the container or doctrine entity manager in the block service?
Tried googling alot but nothing substantial has come out so far
When you create a new block in sonata, you have to declare it like a service, so you can inject doctrine.orm.entity_manager.
I can show you an example of a block where I injected the entity manager:
//My\Bundle\Block\MyBlockService
use Symfony\Component\HttpFoundation\Response;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Validator\ErrorElement;
use Sonata\BlockBundle\Model\BlockInterface;
use Sonata\BlockBundle\Block\BaseBlockService;
use Sonata\BlockBundle\Block\BlockContextInterface;
class MyBlockService extends BaseBlockService
{
protected $em;
public function __construct($type, $templating, $em)
{
$this->type = $type;
$this->templating = $templating;
$this->em = $em;
}
public function getName()
{
return 'MyBlock';
}
public function getDefaultSettings()
{
return array();
}
public function validateBlock(ErrorElement $errorElement, BlockInterface $block)
{
}
public function buildEditForm(FormMapper $formMapper, BlockInterface $block)
{
}
public function execute(BlockContextInterface $blockContext, Response $response = null)
{
$settings = array_merge($this->getDefaultSettings(), $blockContext->getBlock()->getSettings());
$data = count($this->em->getRepository("MyBundle:Entity")->findAll());
return $this->renderResponse('MyBundle::myblock.html.twig', array(
'block' => $blockContext->getBlock(),
'settings' => $settings,
'data' => $data,
), $response);
}
}
Declare you block in services.yml and inject whatever you need:
//services.yml
sonata.block.service.myblock:
class: My\Bundle\Block\MyBlockService
arguments: [ "sonata.block.service.myblock", #templating, #doctrine.orm.entity_manager ]
tags:
- { name: sonata.block }
Declare you block in config.yml:
//config.yml
sonata_block:
default_contexts: [cms]
blocks:
# Enable the SonataAdminBundle block
sonata.admin.block.admin_list:
contexts: [admin]
sonata.block.service.myblock: ~
And then, of course, you have to create the template for block:
{# myblock.html.twig #}
{% extends 'SonataBlockBundle:Block:block_base.html.twig' %}
{% block block %}
<p>{{ data }}</p>
{% endblock %}
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 %}
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' %}
I have one class which extends \Twig_Extension like below :
class MYTwigExtension extends \Twig_Extension
{
protected $doctrine;
protected $router;
public function __construct(RegistryInterface $doctrine , $router)
{
$this->doctrine = $doctrine;
$this->router = $router;
}
public function auth_links($user , $request)
{
// Some other codes here ...
// HOW TO GENERATE $iconlink which is like '/path/to/an/image'
$html .= "<img src=\"$iconlink\" alt=\"\" /> ";
echo $html;
}
}
My question is How to generate Asset links in a Twig Extension ? I would like a replacement for ASSET helper in my class. Bassically I have no idea what I have to inject or use here ! Thanks in advance.
<img src="{{ asset('img/icons/modules/timesheet.png') }}" alt="" />
You can use the templating.helper.assets service directly.
use Symfony\Component\DependencyInjection\ContainerInterface;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
and use it like so:
$this->container->get('templating.helper.assets')->getUrl($iconlink);
Injecting just the templating.helper.assets directly does not work in this case because the twig extension cannot be in the request scope. See the documentation here: https://symfony.com/doc/2.3/cookbook/service_container/scopes.html#using-a-service-from-a-narrower-scope
I didn't want to deal with the Dependency Injection Container. This is what I did:
use Twig_Environment as Environment;
class MyTwigExtension extends \Twig_Extension
{
protected $twig;
protected $assetFunction;
public function initRuntime(Environment $twig)
{
$this->twig = $twig;
}
protected function asset($asset)
{
if (empty($this->assetFunction)) {
$this->assetFunction = $this->twig->getFunction('asset')->getCallable();
}
return call_user_func($this->assetFunction, $asset);
}
I've looked at Twig_Extension class code, and found this initRuntime method there, to be overriden in our custom Extension class. It receives the Twig_Environment as an argument! This object has a getFunction method, which returns a Twig_Function instance. We only need to pass the function name (asset, in our case).
The Twig_Function object has a getCallable method, so we finally can have a callable asset function.
I've gone a bit further creating an asset method for my own extension class. Anywhere else on it, I can simply call $this->asset() and obtain the same result as {{ asset() }} in the templates.
EDIT: The getFunction call at initRuntime throws a scope exception when clearing the cache. So I moved it to the custom asset method. It works fine.
Here's a simple and clean way for Symfony 2.8:
services.yml:
app.twig_extension:
class: Path\To\AcmeExtension
arguments:
assets: "#templating.helper.assets"
In the TWIG extension:
use Symfony\Bundle\FrameworkBundle\Templating\Helper\AssetsHelper;
class AcmeExtension
{
protected $assets;
public function __construct(AssetsHelper $assets)
{
$this->assets = $assets;
}
}
Then you can use it in any function of the extension like this:
$this->assets->getUrl('myurl');
In Symfony 5.3 that worked for me:
(just do what the assets extension does and inject Packages)
use Symfony\Component\Asset\Packages;
use Twig\Extension\AbstractExtension;
use Twig\Extension\ExtensionInterface;
class AppExtension extends AbstractExtension implements ExtensionInterface
{
public function __construct(Packages $packages)
{
$this->packages = $packages;
}
// ... your other methods
private function asset($path, $packageName = null)
{
return $this->packages->getUrl($path, $packageName);
}
}
In Symfony 2.8 that works for me:
# services.yml
services:
app.twig_extension:
class: AppBundle\Twig\AppTwigExtension
public: false
arguments:
- #templating.helper.assets
tags:
- { name: twig.extension }
AppTwigExtension class:
namespace AppBundle\Twig;
use Symfony\Bundle\FrameworkBundle\Templating\Helper\AssetsHelper;
/**
* Class AppTwigExtension
* #package AppBundle\Twig
*/
class AppTwigExtension extends \Twig_Extension
{
const IMG_PATH = 'bundles/app/images/';
private $assetsHelper;
public function __construct(AssetsHelper $assetsHelper)
{
$this->assetsHelper = $assetsHelper;
}
public function getFilters()
{
return array(
new \Twig_SimpleFilter('img', array($this, 'imagePathFilter'))
);
}
/**
* Get image path relatively to host
* Usage in Twig template: {{ 'my_image.png'|img }} - equal to
* {{ asset('bundles/app/images/my_image.png') }} in Twig template:
*
* #param string $imageName (e.g. my_image.png)
* #return string
*/
public function imagePathFilter($imageName)
{
return $this->assetsHelper->getUrl(self::IMG_PATH . $imageName);
}
public function getName()
{
return 'app_twig_extension';
}
}