Archives par étiquette : servicelocator

Zend Framework 2 : supprimer l’appel à getServiceLocator dans les contrôleurs

Vous avez peut-être récemment joué avec la Skeleton Application de Zend Framework (ou pire, remarqué ça lors d’une mise à jour) et en lançant votre application, vous avez vu s’afficher le message suivant, du à l’appel de la méthode getServiceLocator dans votre contrôleur (AlbumController, pour ceux qui ont tenté de suivre le tutoriel) :

Deprecated: You are retrieving the service locator from within the class Album\Controller\AlbumController. Please be aware that ServiceLocatorAwareInterface is deprecated and will be removed in version 3.0, along with the ServiceLocatorAwareInitializer. You will need to update your class to accept all dependencies at creation, either via constructor arguments or setters, and use a factory to perform the injections.

Pour faire simple, on vous avertit que dans la version future de ZF, cette façon de faire sera considérée comme obsolète (j’aime entendre les gens dire « déprécatède ») et qu’il vaudrait mieux injecter la dépendance à la création de votre objet, en l’occurrence ici celui qui résulte de l’instanciation de la classe AlbumController (voir cette page).

Ah...heu...bon...OK, soit !

Ah…heu…bon…OK, soit !


Pour mettre en place quelque chose d’un peu élégant, je vous invite à faire appel à une factory dans votre fichier module/Album/config/module.config.php :

     'factories' => array(
                      'Album\Controller\Album' => 'Album\Factory\AlbumControllerFactory'
                    ),
     ...

Voici le code de votre factory, placée si ce n’est pas déjà fait, dans un répertoire éponyme:

namespace Album\Factory;

use Album\Controller\AlbumController;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

use Album\Service\ControllerService;

class AlbumControllerFactory implements FactoryInterface
{
    /**
    * Create service
    *
    * @param ServiceLocatorInterface $sl
    *
    * @return Album\Controller\AlbumController
    */
    public function createService(ServiceLocatorInterface $sl)
    {
        $cs = new ControllerService($sl->getServiceLocator()->get('doctrine.entitymanager.orm_default'));
        return new AlbumController($cs);
    }
}

Dans ma factory, qui est la seule à savoir comment se fabrique un contrôleur de type Album, je récupère le service que j’injecte dans le constructeur d’AlbumController (relisez le message d’erreur ci-dessus, on vous dit bien qu’il vaudrait mieux injecter la dépendance à la création de votre objet, ce que vous faites). Ici j’utilise comme service l’Entity Manager de Doctrine, mais ça pourrait être n’importe quel autre service, évidemment !

Regardons maintenant le constructeur que j’ai rajouté dans mon contrôleur:

    protected $service;

    public function __construct(\Album\Service\ControllerServiceInterface $service) {
        $this->service = $service->getService();
    }

J’injecte dans mon constructeur les dépendances de celui-ci, sous la forme d’une interface (le po-ly-mor-phisme, que diable !). Cette interface c’est moi qui l’ai écrite, dans mon répertoire Service, elle est simplissime :

 namespace Album\Service;

 interface ControllerServiceInterface
 {
    public function getService();
 }

Elle dit que tout objet qui l’implémente se doit d’avoir une méthode qui permette d’accéder au service qu’il contient.

Dans le même répertoire, j’ai écrit un service dédié à mon contrôleur :

 namespace Album\Service;

 class ControllerService implements ControllerServiceInterface
 {
    protected $service;

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

    public function getService() {
        return $this->service;
    }
 }

On me reprochait d’avoir fait ça dans mon contrôleur:

$this->em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');

Désormais c’est ma factory qui se charge de faire ça, on a donc déporté les responsabilités dans la fabrique, qui est la seule à savoir comment se constitue l’objet qu’elle fabrique. J’ai choisi d’injecter les dépendances (le service) à la création de l’objet et pas via un setter, c’est un choix strictement personnel. Rien ne vous empêche de procéder autrement.