How to execute a doctrine query using Twig on a template in Symfony 2 and 3

Sometimes due to complicated design patterns, or maybe not enough skills with query knowledge to databases you're forced to do queries in the view. This, till Symfony 2 wasn't a problem as you could access the entity manager from the views with php, but with the introduction of Twig in symfony 2, this becomes harder to achieve. Remember that is not a good practice as it doesn't acomplish with the MVC workflow.

However, if you're in a hurry, this will do the trick for you :

  • Create a class that contains the twig extension.
  • Write the extension.
  • Register the extension in your application.
  • Test your method on a template.

We are going to inject doctrine in a TwigExtension, this will allow you to do queries in the php file and return it in Twig, using functions or filters. In this case, we are going only to use functions. If you want to know how to create twig filters, please read the official documentation of twig in the following link.

Create your class

Create a file named TwigExtensions.php file somewhere in your project (you need to know the namespace as we'll need it later). In this example we will create a folder named Extensions in the /src folder of the project. Therefore the namespace in this case will be ourcodeworld\Extensions. Is important to know the namespace of the class that you're creating to register in the services.yml file.

Write the extension

Our TwigExtensions.php file content, should look like (and the name of the class need to match too [TwigExtensions]):

<?php
// Note that the namespace must match with
// your project !
namespace ourcodeworld\Extensions;

use Symfony\Bridge\Doctrine\RegistryInterface;

class TwigExtensions extends \Twig_Extension
{    
    public function getFunctions()
    {
        // Register the function in twig :
        // In your template you can use it as : {{find(123)}}
        return array(
            new \Twig_SimpleFunction('find', array($this, 'find')),
        );
    }
    
    protected $doctrine;
    // Retrieve doctrine from the constructor
    public function __construct(RegistryInterface $doctrine)
    {
        $this->doctrine = $doctrine;
    }

    public function find($id){
        $em = $this->doctrine->getManager();
        $myRepo = $em->getRepository('ourcodeworldarticlesBundle:Articles');
        ///

        return $myRepo->find($id);
    }
    
    public function getName()
    {
        return 'Twig myCustomName Extensions';
    }
}

In the previous class we register a method named find that will be accesible with twig using :

{# Find a register with id 2 #}

{{dump(find(2))}}

Note that you need to change the name of the repository (change how the find function works) and you'll be ready to go. The name of the function needs to be registered as a function in twig, therefore you need to register it in the returned array of the getFunctions method of the class. The constructor of the class expects doctrine as parameter (which will allow you to execute queries in this class), it will be sent in the next step so don't worry if you don't understand.

Register the service

Now that the class exists, you need to register in your services.yml file located in the /app/config path of your project. As seen before, the class expects Doctrine as first parameter, now we'll use yml to send it. Register your service using :

services:
    twig.extension:
        # Register the class (The namespace must match)
        class: ourcodeworld\Extensions\TwigExtensions
        # Inject doctrine as first parameter
        arguments: 
            doctrine : '@doctrine'
        tags:
          -  { name: twig.extension }

Finally, do not forget to clear the cache of the project.

Test on a template

Now you'll be able to use the find method on your templates. Note that the methods are global and can be accessed from all your templates.

<h1>Hello, the title of this article is {{find(123).title}}</h1>

Have fun

Become a more social person