11

I'd like to display new notifications on every page of my symfony 2 webapplication. I was advised to use a Twig Extension for this. I've created a function getFriendRequests in that extension, but I don't know if it's good practice to get data through my custom repository in the twig extension: Right now it's giving me the error, that it can't find the getDoctrine method.

<?php

namespace Tennisconnect\DashboardBundle\Extension;

class NotificationTwigExtension extends \Twig_Extension
{
    public function getFriendRequests($user)
    {
        $users = $this->getDoctrine()
            ->getRepository('TennisconnectUserBundle:User')
            ->getFriendRequests();
        return count($users);
    }

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

    public function getFunctions()
    {
        return array(
            'getFriendRequests' => new \Twig_Function_Method($this, 'getFriendRequests'));
    }
}
mattyh88
  • 1,585
  • 5
  • 26
  • 48
  • Have you considered using a bundle specifically designed for notifications, such as [merkNotificationBundle](https://github.com/merk/merkNotificationBundle)? – Problematic Dec 09 '11 at 19:48

2 Answers2

29

I don't think it is so bad to fetch your data directly from your twig extension. After all, if you don't do it here, you will need to fetch those records before and then pass them to the extension for display anyway.

The important point is to do the DQL/SQL stuff in the repository like you are already doing. This is important to separate database statements from other part of your project.

The problem you having is that the method getDoctrine does not exist in this class. From what I understand, you took this code from a controller which extends the FrameworkBundle base controller. The base controller of the FrameworkBundle defines this method.

To overcome this problem, you will have to inject the correct service into your extension. This is based on the dependency injection container. You certainly defined a service for your twig extension, something like this definition:

services:
  acme.twig.extension.notification:
    class: Acme\WebsiteBundle\Twig\Extension\NotificationExtension
    tags:
      - { name: twig.extension }

The trick is now to inject the dependencies you need like this:

services:
  acme.twig.extension.notification:
    class: Acme\WebsiteBundle\Twig\Extension\NotificationExtension
    arguments:
      doctrine: "@doctrine"
    tags:
      - { name: twig.extension }

And then, in you extension, you define a constructor that receives the doctrine dependency:

    use Symfony\Bridge\Doctrine\RegistryInterface;

    class NotificationTwigExtension extends \Twig_Extension
    {
        protected $doctrine;

        public function __construct(RegistryInterface $doctrine)
        {
            $this->doctrine = $doctrine;
        }

        // Now you can do $this->doctrine->getRepository('TennisconnectUserBundle:User')

        // Rest of twig extension
    }

This is the concept of dependency injection. You can see another question I answered sometime ago about accessing services outside controller: here

Hope this helps.

Regards,
Matt

Community
  • 1
  • 1
Matt
  • 10,633
  • 3
  • 46
  • 49
  • Ha, beat me out on the answer by a few seconds. Well played. – Problematic Dec 09 '11 at 19:47
  • Hehe what about a timing :) It happens sometime for me also to get beaten just before posting an answer. – Matt Dec 09 '11 at 19:50
  • Thx for the answer but I'm getting this error: The "notification" extension is not enabled in "::base.html.twig" at line 18. – mattyh88 Dec 10 '11 at 14:18
  • Solved it .. I copied it a little bit to much. Had to edit constructor() to construct() and there were a few to much spaces in front of tags. I've gotten the advice to typehint the interface and not the actual class. What should I put in the arguments in the config.yml, because now the constructor is working with an instance of Registry. – mattyh88 Dec 10 '11 at 14:52
  • Solved it :) I had to put a use statement for the Registry interface in my twig extension – mattyh88 Dec 10 '11 at 15:19
  • Glad you solved it :D I fixed my post accordingly. The advice to typehint is indeed the best thing to do. This way, it is easier to change a concrete class by another one. – Matt Dec 11 '11 at 14:12
  • Why Am i getting this error : `Catchable Fatal Error: Argument 1 passed to PMI\HomePagesBundle\Extension\PMITwigExtension::__construct() must implement interface Symfony\Bridge\Doctrine\RegistryInterface, none given, called in C:\wamp\www\PMI_sf2\app\cache\dev\appDevDebugProjectContainer.php on line 1432 and defined in C:\wamp\www\PMI_sf2\src\PMI\HomePagesBundle\Extension\PMITwigExtension.php line 14` – pmoubed Jun 01 '12 at 19:20
  • Can somebody answer this question too : http://stackoverflow.com/questions/10792579/what-does-exactly-dependency-injection-in-symfony2-do – pmoubed Jun 01 '12 at 19:22
  • The answer you linked is already answered and accepted, I guess by you. What do you want to know? Do you want a better explanation as a new answer to the linked question? – Matt Jun 01 '12 at 19:59
2

The same but with mongo:

in config.yml

services:
    user.twig.extension:
        class: MiProject\CoreBundle\Twig\Extension\MiFileExtension
        arguments:
          doctrine: "@doctrine.odm.mongodb.document_manager"
        tags:
          -  { name: twig.extension }

and in your Twig\Extensions\MiFile.php

<?php

namespace MiProject\CoreBundle\Twig\Extension;

use Symfony\Component\HttpKernel\KernelInterface;

class MiFileExtension extends \Twig_Extension
{
    protected $doctrine;

    public function __construct( $doctrine){
        $this->doctrine = $doctrine;
    }
    public function getTransactionsAmount($user_id){
    return $results = $this->doctrine
    ->createQueryBuilder('MiProjectCoreBundle:Transaction')             
    ->hydrate(false)
    ->getQuery()            
    ->count();
    }

    Rest of mi code ... 

}
Rubén Fanjul Estrada
  • 1,296
  • 19
  • 31