Créez proprement un service Symfony

Logo Symfony

Créer un service dans Symfony2 est très facile. Je vais présenter 2 méthodes pour créer un service rapidement.

L’injection de dépendance

On le voit partout, on le dit très souvent : Il ne faut pas injecter le conteneur de services comme dépendance à un service (ou à tout autre élément). C’est un peu comme écraser une mouche à coup de batte de baseball.

Mais parfois, ça permet de se simplifier la vie. Par exemple dans un contrôleur, par habitude, on accède à n’importe quel service en faisant un petit :

$monService = $this->getContainer()->get('nom.du.service');

ou

$monService = $this->container->get('nom.du.service');

Grâce à ça, on accède directement aux services. Simple, efficace, pratique.

Service de fainéant

Ajoutons la définition de notre service dans la configuration de notre bundle.

services:
    papsou.acme.monService:
        class: PapsOu\AcmeBundle\DependencyInjection\Services\MonService
        calls:
            - [setContainer, ["@service_container"]]

Et notre classe de service :

namespace PapsOu\AcmeBundle\DependencyInjection\Services;

use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class MonService implements ContainerAwareInterface
{
    /** @var ContainerInterface */
    private $container;

    /**
     * @inheritdoc
     */
    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

    public function maFonction($parameters)
    {
        // Votre fonction...
        $unService = $this->container->get('un.service');
    }
}

Pour résumer, on créer un service qui à besoin du conteneur de service de Symfony pour fonctionner (car on utilise le conteneur dans une méthode de la classe du service). Cette classe doit implémenter l’interface ContainerAwareInterface pour injecter le conteneur. On peut très bien aussi injecter le conteneur par le biais d’une injection par constructeur.

Voila, c’est rapide, et on change pas trop nos habitudes. Seulement, c’est bien crade !

Service « Propre »

Cette fois-ci, on va devoir réfléchir un minimum sur ce point : De quels services ai-je besoin ?

Pour notre exemple, on va dire qu’on a besoin de l’EntityManager de Doctrine et d’un logger. Allons-y !

La définition de notre service :

services:
    papsou.acme.monService:
        class: PapsOu\AcmeBundle\DependencyInjection\Services\MonService
        calls:
            - [setEm, ["@doctrine.orm.entity_manager"]]
            - [setLogger, ["@logger"]]

Et notre classe de service :

namespace PapsOu\AcmeBundle\DependencyInjection\Services;

use Doctrine\ORM\EntityManager;
use Symfony\Bridge\Monolog\Logger;

class MonService
{
    /** @var EntityManager */
    private $_em;

    /** @var Logger */
    private $logger;

    /**
     * @param EntityManager $em
     * @return void
     */
    public function setEm(EntityManager $em = null)
    {
        $this->_em = $em;
    }

    /**
     * @param Logger $logger
     * @return void
     */
    public function setLogger(Logger $logger) {
        $this->logger = $logger;
    }

    public function maFonction($parameters)
    {
        // Votre fonction...
        $mesEntites = $this->_em
            ->getRepository('PapsOuAcmeBundle:Entite')
            ->findAll();
        // On fait des traitement sur nos entités
        // On va logger ces manipulations
        $this->logger->info('mon message');
    }
}

Voila. C’est un peu plus long à écrire, il faut savoir de quels services on va devoir utiliser pour notre service. Mais c’est bien plus propre !

Si comme moi, vous avez fait pas mal de services en injectant directement le conteneur (par flemme probablement), je vous conseille de trouver quelques minutes pour optimiser tout ça et remplacer l’injection du conteneur en injectant uniquement les services que vous utilisez dans vos propres services. Juste histoire d’être plus carré, ou juste histoire de se dire « mes services sont propres ».

Un commentaire