Zend FR

Consultez la FAQ sur le ZF avant de poster une question

Vous n'êtes pas identifié.

#1 15-10-2012 15:42:03

neilime
Membre
Date d'inscription: 28-04-2009
Messages: 42

ZF1 vers ZF2 => Comment se passer du Singleton / Registry

Bonjour,

Je tente de migrer une application basée ZF1 vers ZF2. Je me trouve au devant de difficultés de conceptions / réflexions.

Un exemple concret pour expliquer mes embuches:

Lors de la connexion de l'utilisateur, un traitement est effectué.

- Récupération via service web des informations pour se connecter à la bdd (différente par utilisateur), puis loggin.
- Requêtes SQL pour récupérer des informations diverses sur l’utilisateur
- Stockage en session de certaines informations...
- ...
- Affichage de la home

Ce processus diffère en fonction de l'utilisateur connecté (process plus ou moins complexe)

Concrètement, une fonction "manageConnexion" de la classe métier "App_Engine" stocké dans le dossier library/App/Engine.php était appelée via le controller IndexController/connexionAction

Elle se chargée d’instancier un manager de session, un client soap, questionner la bdd via les Models...
Ces différents services étaient appelés via Singleton ou Zend_registry.

- SessionManager (singleton)
- ConnexionBDD (singleton afin de n'avoir qu'une connexion mysql à la fois)
- Translator (Zend_Registry)
- Models (singleton)
- ...

Ce qui permet à la classe App_Engine d'appeler les services en fonction de ses besoins (processus).

Maintenant que je passe sur ZF2 : exit le singleton et zend_registry. Me voilà perplexe.
Sans me démonter je commence à transformer mes classes en services, pouvant être appelées via "factories" ou "invoques", selon les besoins.

Mais voilà que je commence à refondre la fonction "manageConnexion" de la classe App_Engine : elle ne peux plus appeler à sa guise les services dont elle a besoin.

Je commence à lui passer ses dépendances par le constructeur. Mais le problème de performance me saute aux yeux. Je doit lui passer tous les services dont elle a potentiellement besoin !!

Je ne peux pas non plus le faire via des setters car seule la fonction de "manageConnexion" connait ses dépendances de surcroit dynamiques car en fonction des divers processus engendrés.

Il doit me manquer un aspect du DI, car il est clair que je ne peux pas continuer dans cette voie.

Étant dans le flou le plus total, je suis vraiment preneur de tous les éclaircissements que l'on peut m'apporter.
Merci d'avance pour vos réponses.

Hors ligne

 

#2 15-10-2012 16:43:31

Orkin
Administrateur
Lieu: Paris
Date d'inscription: 09-12-2011
Messages: 1261

Re: ZF1 vers ZF2 => Comment se passer du Singleton / Registry

Salut je ne sais pas si ça corrigera ton problème mais tu peux faire une classe AbstractService qui étend ServiceManagerAwareInterface et dont va étendre ta classe service qui contient la fonction manageConnexion.

De ce fait tu peux tout passer via des getters/setters. Voici comment il faut procéder :

Code:

[lang=php]/**
     * @var ServiceManager
     */
    protected $serviceManager;

    /**
     * @var ConfigService
     */
    protected $configService;

    /**
     * @var UserService
     */
    protected $userService;

/**
     * Retrieve service manager instance
     *
     * @return ServiceManager
     */
    public function getServiceManager() {
        return $this->serviceManager;
    }

    /**
     * Set service manager instance
     *
     * @param ServiceManager $locator
     * @return User
     */
    public function setServiceManager(ServiceManager $serviceManager) {
        $this->serviceManager = $serviceManager;
        return $this;
    }

    /**
     * @return ConfigService
     */
    public function getConfigService() {
        if ($this->configService === null) {
            $this->setConfigService($this->getServiceManager()->get('application.service.config'));
        }
        return $this->configService;
    }

    public function setConfigService(ConfigService $configService) {
        $this->configService = $configService;
    }

    /**
     * @return UserService
     */
    public function getUserService() {
        if (!$this->userService) {
            $this->setUserService($this->getServiceManager()->get('application.service.user'));
        }

        return $this->userService;
    }

    public function setUserService(UserService $userService) {
        $this->userService = $userService;

        return $this;
    }

De ce fait tous tes services ne seront chargés qu'une seule fois et seulement lorsque tu en auras besoin. Du coup pas de problème de performance.

Hors ligne

 

#3 15-10-2012 17:00:54

neilime
Membre
Date d'inscription: 28-04-2009
Messages: 42

Re: ZF1 vers ZF2 => Comment se passer du Singleton / Registry

D'accord ça me semble déjà une bonne solution. Mais cela induit que toutes les classes fonctionnant sous ce même schéma devront donc implémenter cette solution.
Est ce que cela reste dans la "philosophie" ZF2 ?

Hors ligne

 

#4 15-10-2012 17:53:48

Orkin
Administrateur
Lieu: Paris
Date d'inscription: 09-12-2011
Messages: 1261

Re: ZF1 vers ZF2 => Comment se passer du Singleton / Registry

Oui il n'y a pas de contre indication à ce sujet, toutes tes classes peuvent fonctionner sur ce principe et ça n'a pas d'impact au niveau des performances. Et ça a plusieurs avantages :

- Plusieurs appels au même service
=> une seule instance (ça c'est le serviceManager)
=> Pas de réécriture de code
=> Dans le cas où il est déjà instancié et affecté à ton service il réutilise

Tu peux t'inspirer de ce genre de solutions pour des accès base de données que tu n'as besoin de faire qu'une seule fois par requête (tu peux tester si tu l'as déjà fait avant).

Hors ligne

 

#5 15-10-2012 22:19:42

bakura
Administrateur
Date d'inscription: 30-01-2010
Messages: 353

Re: ZF1 vers ZF2 => Comment se passer du Singleton / Registry

Salut,

Je n'ai pas tout lu mais effectivement, les dépendances se gèrent entièrement via le Service Manager et de ce fait, tu devras l'injecter assez régulièrement. Tu as la solution de Orkin qui utilise une logique de "lazy-loading" (en gros, tu récupèers les dépendances quand tu en as besoin).

Il y aura des cas ou les dépendances sont "oblgiatoires" (hard dependency), où créer un objet sans cette dépendance n'a pas de sens. Dans ce cas là, le plus propre est de définir tes dépendances dans le constructeur et d'utiliser une factory afin de construire l'objet en lui passant les bons paramètres dans le constructeur.

Tu as de nombruex avantages à utiliser ce genre de paradigme plutôt que Zend Registry, notamment au niveau des tests unitaires (comme tes dépendances ne sont pas codées en dur, il est extrêmement aisé de créer des mocks au niveau des tests unitaires en utilisant ce moyen de gérer les dépendances).

Après c'est un coup à prendre, mais tu verras qu'au final c'est plutôt intuitif smile. Essaye surtout de bien piger les diférences entre les objets invokables, les factories, les abstract factories...

Hors ligne

 

#6 16-10-2012 09:38:00

neilime
Membre
Date d'inscription: 28-04-2009
Messages: 42

Re: ZF1 vers ZF2 => Comment se passer du Singleton / Registry

Déjà merci pour votre attention et vos conseils.

Voilà ma classe de service manager.

Code:

[lang=php]
class AbstractServiceManager implements ServiceManagerAwareInterface{
    /**
     * @var ServiceManager
     */
    private $serviceManager;

    /**
     * @return ServiceManager
     */
    private function getServiceManager(){
        return $this->serviceManager;
    }

    /**
     * @param ServiceManager $oServiceManager
     * @return AbstractServiceManager
     */
    public function setServiceManager(ServiceManager $oServiceManager){
        $this->serviceManager = $oServiceManager;
        return $this;
    }

    public function __call($sService,$aArguments){
        if(!preg_match('/^get(.*)$/',$sService,$aMatches) || !$this->getServiceManager()->has($this->$aMatches[1]))throw new \Exception('Service incorrect');
        return isset($this->$aMatches[1])?:$this->getServiceManager()->get($aMatches[1]);
    }
}

Le problème qui me vient maintenant, concerne les modèles, car il m'a été simple d'étendre mes classes "perso" :

Code:

[lang=php]
namespace App;
use \App\ServiceManager\AbstractServiceManager,
class Engine extends AbstractServiceManager{
//...

Par contre pour les modèles, impossible d'"extends  AbstractServiceManager", car il étendent déjà AbstractTableGateway.

Je vois 2 solutions qui s'offrent à moi :
- Overrider la classe AbstractTableGateway afin de la faire implementer ServiceManagerAwareInterface / et lui rajouter les getter pour les services.

- Passer le service manager au constructeur de mes modèles.

Est ce qu'une de ses solutions vous semble correcte ?

Hors ligne

 

#7 16-10-2012 09:55:59

Orkin
Administrateur
Lieu: Paris
Date d'inscription: 09-12-2011
Messages: 1261

Re: ZF1 vers ZF2 => Comment se passer du Singleton / Registry

Alors pour tes modèles je te conseil vivement d'utiliser Doctrine 2 il est vraiment bien conçu et le module s'intègre très facilement à ton application. Ca devrait nécessiter peut de modifications enfin il faudra surement repasser sur ta couche service mais rien de bien méchant.

Ensuite je comprend pas l'utilité d'avoir accès au service manager dans tes modèles car si c'est le cas tu as clairement un problème d'architecture. En effet si on suit le modèle MVC et la séparation des couches on ça :
Vue > Contrôleur > Service > DAO (ou modèle car Doctrine 2 fait office de DAO).

Chacune des couches n'a connaissance que de la couche qui est à sa droite c'est à dire que la vue est dépendante du contrôleur mais pas de la couche service et le contrôleur ne connait pas la vue. Le contrôleur c'est pareil il connait le service mais le service ne connait pas le contrôleur etc ... De cette façon tu peux changer facilement n'importe quelle couche. On peut imaginer que ta couche DAO se connecte à une base MySQL et pour X ou Y raisons tu décides de passer par du stockage sous forme de fichiers XML tu n'auras donc qu'à réécrire la couche DAO car la couche service ne se contente que de récupérer des informations :

getAllUsers() par exemple, cette fonction retourne un array avec Doctrine 2 à toi de faire en sorte en réécrivant la couche DAO pour supporter le XML de renvoyer aussi un array de ce fait il n'y a aucun changements à faire au dessus tout est transparent.

Je pense que ça va répondre en partie à tes questions.

Ensuite je te déconseille l'utilisation de la méthode __call certes elle est très pratique mais moins performante ça fait écrire plus de code de mettre tous les getter/setter mais on s'y retrouve beaucoup mieux et c'est bien plus performant (que du traitement basique) les regex c'est assez gourmand en ressource.

Hors ligne

 

#8 16-10-2012 10:38:48

neilime
Membre
Date d'inscription: 28-04-2009
Messages: 42

Re: ZF1 vers ZF2 => Comment se passer du Singleton / Registry

Concernant doctrine, cela fait parti en effet du processus de refonte (mais pas encore validé par la direction), donc je suis contraint de rester sur Zend\Db\TableGateway pour le moment.

Ensuite pour la conception "Vue > Contrôleur > Service > DAO", je me retrouve coincé... Car la couche service n'existe pas dans l'application : le traitement est géré par les modèles.

A ma décharge, je ne suis pas le concepteur de cette application vielle d'une petite dizaine d'année.

Je suis me sens tout de même confus, car j'avais pour certitude qu'il était correct de déléguer les traitements au modèle pour respecter cette convention MVC, mais je n'ai jamais eu l'occasion de me retrouver confronté à cette couche supplémentaire qu'est le Service.

#Tout s'écroule : Merci Orkin de m'ouvrir les yeux hmm#

Ok pour la conception, mais concrètement, une refonte pour respecter cette conception "Vue > Contrôleur > Service > DAO" entraine une refonte de tout les modèles existants (je regrette presque ma question)...

Hors ligne

 

#9 16-10-2012 11:02:45

Orkin
Administrateur
Lieu: Paris
Date d'inscription: 09-12-2011
Messages: 1261

Re: ZF1 vers ZF2 => Comment se passer du Singleton / Registry

Tu n'es pas obligé de suivre cette solution. Tu peux continuer comme tu le fais déjà mais ça implique les contraintes que tu as actuellement il y a des choses que les classes, suivant la couche où elles se trouvent, ne doivent pas connaitre.

Je suis loin d'être un pro de l'architecture mais j'avoue que lorsque l'on m'a parlé de ce genre d'architecture j'ai trouvé ça tellement logique !
Pour ta défense tu peux regrouper les couches "modèle", "service" et "DAO" dans le Modèle du MVC (bien que service peut aller dans le Contrôleur et le Modèle).

La seule chose où là j'ai un doute et où avec Bakura on a du mal a trouver le bon du mauvais c'est de se service des classes modèles comment classe métier. En effet dans le documentation Doctrine 2 il est indiqué que c'est prévu pour être utilisé comme ça mais on m'a souvent dit qu'on avait les classes modèle d'un côté et les classes métier de l'autre où une classe métier représente ce que tu as besoin de manipuler pour la vue alors que la classe modèle représente une table.

Tu n'as pas forcément tant de choses à refaire mais c'est certain qu'un modèle est à la base fait pour représenter une table c'est tout. (Doctrine pousse à les utiliser aussi comme classes métier mais c'est pas le mieux, après Doctrine a quand même suffisamment d'expertise pour bien conseiller donc j'aurai tendance à faire comme ils disent tout en sachant que c'est pas forcément la solution la plus propre). Il faudrait que tu arrives à découper les traitements fait partie modèles dans la couche service et te servir des modèles uniquement pour imager ta base.

Hors ligne

 

#10 16-10-2012 16:00:53

bakura
Administrateur
Date d'inscription: 30-01-2010
Messages: 353

Re: ZF1 vers ZF2 => Comment se passer du Singleton / Registry

Si tu as besoin d'avoir le SM dans tes entités (même si, comme l'a dit Orkin, c'est pas vraiment recommandé), il faut que tu implémentes l'interface ServiceManagerAwareInterface, et que tu récupères tes modèles via le SM avec que le SM soit automatiquement injecté), mais surtout pas les faire étendre d'une classe AbstractServiceManager. Ca n'a pas de sens sémantiquement de dire que tes modèles SONT des service managers (or c'est ce qui est induit par ton héritage). Du coup tu pourras hériter de TableGateway.

Mais dans l'absolue, oui, il y a quelque chose de mauvais dans ton design qui explique pourquoi tu galères smile.

Hors ligne

 

#11 16-10-2012 16:05:18

neilime
Membre
Date d'inscription: 28-04-2009
Messages: 42

Re: ZF1 vers ZF2 => Comment se passer du Singleton / Registry

Les modèles étendent donc une classe TableGateway perso qui implémente ServiceManagerAwareInterface.
Je me chargerai donc de séparer mes modèles en Services / DAO lorsque j'entamerai la migration vers Doctrine.

Merci pour vos conseils

Hors ligne

 

Pied de page des forums

Propulsé par PunBB
© Copyright 2002–2005 Rickard Andersson
Traduction par punbb.fr

Graphisme réalisé par l'agence Rodolphe Eveilleau
Développement par Kitpages