dialogik
dialogik

Reputation: 9552

How can I fetch a value from the DB and display it in all templates?

Assume I have a User and a Message entity in my Symfony2 app, with a oneToMany relation between both entities, so a User can have multiple messages, and I want to display the number of unread messages in my main navigation like this

where (6) is the number of unread messages. In order to fetch this amount of unread messages per user Controller independently, I could think of defining a User entity method:

public function getUnreadMessagesCount() {
    $unreadMessagesCount = 0;
    foreach($this->messages as $message) {
        if($message->isUnread()) {
            $unreadMessagesCount++;
        }
    }
}

and call it in my menu.html.twig

{% set umc = app.user.getUnreadMessagesCount() %}
{% if umc > 0 %}
    ({{ umc }})
{% endif %}

But for performance reasons, I would want to avoid looping through the entire set of messages just to check whether it is unread or not. So I would prefer to fetch it via a DQL call (and place this fetch in the UserRepository class)

$query = $em->createQuery("
            SELECT COUNT(m.id) AS umc
            FROM AcmeBundle:User u
            JOIN AcmeBundle:Message m
            WITH u.id = m.user_id
            WHERE m.unread = true
            AND u.id = :uid");

But afaik it's bad practice to use repository methods or to use the entitiy manager (or Doctrine) in entity classes.

I thought about fetching the value in a service and inject this into my entity. But it also is not recommended to inject services into entites.

So what is a legit way to fetch this value and output it in all templates (may also be without entity methods, e.g. by injecting the value into all Controllers)?

Upvotes: 2

Views: 317

Answers (2)

A.L
A.L

Reputation: 10513

I used another solution to get data from a repository.

First, define the service:

## in bundle/Resources/config/services.yml
services:
    YOUR_BUNDLE.twig.NAME_OF_THE_EXTENSION:
        class: YOUR\BUNDLE\Twig\CLASSNAME
        arguments: 
            - @service_container
        tags:
            - { name: twig.extension }

Then define the Twig extension class:

# YOUR_BUNDLE/Twig/CLASSNAME.php
<?php

namespace YOUR\BUNDLE\Twig;

class CLASSNAME extends \Twig_Extension
{
    protected $container;

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

    public function getGlobals()
    {
        return(array(
            'unreadMessagesCount' => $this->container->get('doctrine')
                ->getManager()
                ->getRepository('YOUR_BUNDLE:UserRepository')
                ->getUnreadMessagesCount()
        ));
    }

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

Upvotes: 2

dialogik
dialogik

Reputation: 9552

I don't really like this approach, but - for completeness - I found this:

You can set the service a twig global variable in config.yml, e.g

#app/config/config.yml
twig:
    globals:
        your_service: "@your_service"

See here.

So simply define your service

class AcmeService {
    public function __construct() {
        // construct
    }

    public function myMethod() {
        // do stuff
    }
}

declare this service in your config.yml, define it as twig globale (see quoted answer above) and you can use your method in your twig file like this

{{ your_service.myMethod() }}

Upvotes: 2

Related Questions