IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Symfony PHP Discussion :

Récupérer la session en dehors des controleurs


Sujet :

Symfony PHP

  1. #1
    Membre à l'essai
    Inscrit en
    Février 2013
    Messages
    25
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 25
    Points : 22
    Points
    22
    Par défaut Récupérer la session en dehors des controleurs
    Bonjour,

    Je travaille actuellement sur un projet Symfony 2.

    Ce dernier est composé bien sûr de Controller et de couches métiers appelées par les Controllers.
    Ces couches métiers sont des classes des plus classique permettant essentiellement l'accès au données de la bdd.

    Le code ressemble en fait à cela :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class DefaultController extends Controller
    {
          public function indexAction()
        {
             ....
             ProjectBLL::insertProject($projectBO);
             TaskBLL::insertTask($taskBO);
             ....
         }
    }
    Dans mes couches métiers lors de l'insertion en base d'un projet (ou d'une tâche) je désire aussi insérer une ligne d'historique dans une table dédiée.
    Dans cet historique je désire stocker l'identifiant de l'utilisateur courant à l'origine de l'action, en l’occurrence un identifiant stocker dans la session.

    Dans mes controlleurs pas de problème pour récupérer cet identifiant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $this->get("request")->getSession()->get("idUser");
    Comment puis-je récupérer la valeur de cette session dans mes couches métiers sans avoir à le passer en paramètre à mes méthodes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ProjectBLL::insertProject($projectBO, $idUser);
    En d'autres termes comment avoir accès à ma session en dehors du controlleur ?

    Par avance je vous remercie pour votre aide.

  2. #2
    Membre à l'essai
    Homme Profil pro
    Apprenti chef de projet / Développeur
    Inscrit en
    Août 2011
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Apprenti chef de projet / Développeur

    Informations forums :
    Inscription : Août 2011
    Messages : 11
    Points : 16
    Points
    16
    Par défaut
    Tu peux définir ton controller en temps que service et lui injecter le "securityContext" comme ça tu aura accès à l'utilisateur courant dans celui-ci.

    En espérant t'avoir aidé ..

  3. #3
    Membre à l'essai
    Inscrit en
    Février 2013
    Messages
    25
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 25
    Points : 22
    Points
    22
    Par défaut
    skududufru merci pour cette première réponse.

    Pour des raisons sous-jacentes je n'utilise pas la couche de Symfony pour gérer ma session utilisateur.
    Je possède une authentification maison qui stocke mon objet 'User' (dont son identifiant) dans une session particulière.

    Mes contrôleurs (MVC) ont sans problème accès à cette session en passant directement par l'objet "request".
    Dans mes couches métier (MVC) qui ne sont pas des contrôleurs mais des méthodes me permettant de faire des INSERT, UPDATE, DELETE dans ma base de données.

    Dans ma base de données je stocke souvent l'identifiant de mon utilisateur courant.
    Et au lieu de devoir le passer en paramètre systématiquement de mon contrôleur vers mon modèle j'aurai aimé pouvoir le récupérer directement de mon modèle.

  4. #4
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    En admettant que la méthode que tu utilises pour le stockage d'un user en session soit justifiée, tu peux récupérer la session par injection de dépendance.

    Selon ce dont tu as besoin tu peux injecter le service @session ou si tu veux la récupérer comme dans ton exemple depuis la requête : tu peux y accéder en injectant le service @request_stack (>=2.4).

    ++

  5. #5
    Membre à l'essai
    Inscrit en
    Février 2013
    Messages
    25
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 25
    Points : 22
    Points
    22
    Par défaut
    Bonjour Nico_F,

    Oui effectivement mes investigations m'ont mené à cette notion d'"injection de dépendance" mais je n'arrive pas à saisir comment cela fonctionne même après avoir parcouru les tutoriaux de Symfony.

    Le service "@request_stack" peut-il être utilisé dans mes couches métier "ProjectBLL", "TaskBLL" même si ces dernières ne sont pas des contrôleurs ?
    Si oui je n'ai pas trouvé comment instancier dans mes couches métier ce service afin d'avoir accès à l'objet "request" ?

    Je sens que j'approche du but... mais un doute persiste sur la faisabilité...
    Je te remercie donc par avance pour les informations complémentaires que tu pourrais m'apporter

  6. #6
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    De nombreuses classes peuvent être déclarées en tant que service et c'est une bonne chose de le faire si tant est que tu gardes une cohérence sur l'ensemble de ton modèle.
    Tu peux déclarer un controlleur en tant que service, mais aussi un manager, un repository, un formHandler, un subscriber ... n'importe quelle classe custom de ton choix.

    Donc je ne sais pas ce que sont tes classes PLL mais s'il ne s'agit pas d'entités Doctrine, il n'y a à priori pas de raison pour que tu ne puisses pas les déclarer en tant que service.

    En reprenant le même fonctionnement que la déclaration des services des contrôleurs, tu peux déclarer la classe de ton choix en tant que service, et lui passer en argument d'autres services.

    Première lecture => http://symfony.com/fr/doc/current/co...roduction.html

    ++

  7. #7
    Membre à l'essai
    Inscrit en
    Février 2013
    Messages
    25
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 25
    Points : 22
    Points
    22
    Par défaut
    C'est là que je perds le file... J'ai peur de ne pas comprendre ce qu'est un service Symfony...

    Actuellement j'ai essayé :

    1. Déclaration de ma classe en tant que service :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    session_service:
            class: ItmBundle\Services\SessionService
            arguments: ["@request_stack"]
    Ici je pense déclarer ma classe SessionService en tant que service et que cette dernière prend en argument l'objet request_stack

    2. Implémentation de ma classe :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    namespace ItmBundle\Services;
     
    class SessionService
    {
    	private $_requestStack;
    	public function __construct(RequestStack $requestStack) {
    		$this->_requestStack = $requestStack;
    	}
     
    	public function getUserId(){
        	    $currentRequest = $this->_requestStack->getCurrentRequest();
    	    $session = $currentRequest->getSession();
        	    if ($session->has("User")) {
    	    	$ret = $session->get("User")->getId();
    	    }
        	return $ret;
        }
    }
    Je pense ici que ma classe étant un service le constructeur va automatiquement utiliser le paramètre @request_stack pour créer mon instance...
    Ce n'est pas le cas...

    En effet dans ma classe ProjectBLL, classe effectivement non Doctrine mais classe standard, pour récupérer l'identifiant de mon utilisateur j'ai une erreur lorsque je fais...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    public class ProjectBLL {
        ...
        ...
        $sessionService = new SessionService();
        $idCurrentUser = $sessionService->getUserID();
        ...
        ...
    }
    ... me retourne bien entendu l'erreur que le constructeur attend un argument (à priori le @request_stack que je ne précise pas car je ne connais pas ici)...

    Peux-tu me dire ou je me plante dans la mécanique car je sens bien que quelquechose m'a échappé...

    En te remerciant par avance

  8. #8
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Le service et la déclaration du service m'ont l'air plutôt ok, par contre pourquoi n'utilises-tu pas le service session déjà fourni par Symfony ?
    Peu importe, le problème vient du fait que tu instancies toi même la classe en faisant un new au lieu de l'injecter ou bien de le récupérer depuis le container. En faisant un new de cette manière tu ne possèdes pas le paramètre dont le constructeur a besoin. Sans injection de dépendance, tu aurais fait un new SessionService($requestStack);La si tu veux récupérer ton service dans ProjectBLL, soit tu fais implémenter ContainerAware à ta classe pour qu'elle ait accès à tous les services et tu accèdes à la session en faisant $this->container->get('session_service'); (peu recommandé). Soit tu déclares également ProjectBLL en tant que service et tu lui injectes SessionService comme tu as injecté le service request_stack à SessionService.

    Théoriquement en utilisant l'injection de dépendance, tu ne devrais plus avoir beaucoup de "new quelque chose". Tu déclares tes classes en tant que service jusqu'au controller que tu utiliseras tel quel avec le container, ou que tu déclareras également en tant que service pour être le point de départ et y injecter les premiers services dont lui aura besoin.

    C'est un comportement un peu particulier à saisir au début, mais qui est diablement efficace !

  9. #9
    Membre à l'essai
    Inscrit en
    Février 2013
    Messages
    25
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 25
    Points : 22
    Points
    22
    Par défaut
    Merci Nico_F pour ces informations !
    Mais il me manque encore le petit truc pour que je comprenne.
    Pourrais-tu sur les bases du code déjà implémenté comment toi tu coderais cela ?
    L'objectif restant de pouvoir accéder à la session en dehors de mes contrôleurs.

    Je craque...
    Par avance merci

  10. #10
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Eh bien, sur la base de ce que tu as fait toi je vois ça de cette manière :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    session_service:
        class: ItmBundle\Services\SessionService
        arguments: 
            - @request_stack
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    project_bll:
        class: Namespace\Path\To\ProjectBLL
        arguments:
            - @session_service
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
    use Symfony\Component\HttpFoundation\RequestStack;
     
    class SessionService
    {
        private $_requestStack;
     
        public function __construct(RequestStack $requestStack)
        {
            $this->_requestStack = $requestStack;
        }
     
        public function getUserId()
        {
            $currentRequest = $this->_requestStack->getCurrentRequest();
    	$session = $currentRequest->getSession();
        	if (!$session->has("User")) {
    	    throw new \Exception('Whatever ...');
    	}
     
            return $session->get("User")->getId();
        }
     
        //...
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    use ItmBundle\Services\SessionService;
     
    class ProjectBLL
    {
        private $_session;
     
        public function __construct(SessionService $session)
        {
            $this->_session = $session;
        }
     
        public function getUserId()
        {
            return $this->_session->getUserId();
        }
    }
    Ensuite si ta classe ProjectBLL n'est pas directement utilisée par le contrôleur il faudra que tu l'injectes de la même manière à la classe qui l'utilise.
    Sinon, si elle est utilisée dans un contrôleur, soit tu déclares ton contrôleur en tant que service et tu l'injectes de la manière suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    my_controller:
        class: Namespace\Path\To\MyController
        arguments:
            - @project_bll
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    use Namespace\Path\To\ProjectBLL;
     
    class MyController
    {
        private $_projectBll;
     
        public function __construct(ProjectBLL $projectBll)
        {
            $this->_projectBll = $projectBll;
        }
     
        public randomAction()
        {
            $userId = $this->_projectBll->getUserId();
        }
    }
    Ou bien tu la récupères depuis le container de service en faisant un $projectBll = $this->container->get('project_bll');.

    Maintenant personnellement, sans tenir compte de la base que tu m'as donné, j'utiliserais le service session par défaut récupérable directement depuis le contrôleur soit en l'injectant (cf. ci dessus) soit via le container (cf ci-dessus aussi). Du coup je n'aurais pas besoin de passer par plusieurs classes custom, mais je ne connais pas toutes tes contraintes so ...

    Mais bon, ta méthode a moins l'avantage de te faire bosser sur le principe le plus important de l'utilisation de Symfony : l'injection de dépendance ^^

    Have fun !
    ++

  11. #11
    Membre à l'essai
    Inscrit en
    Février 2013
    Messages
    25
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 25
    Points : 22
    Points
    22
    Par défaut
    Merci à toi pour ce code et pour le temps réservé.

    En fait je n'essaie pas de récupérer la session de mon ProjectBLL vers mon contrôleur via un service,
    mais plutôt récupérer la session dans ma classe ProjectBLL qui n'est pas un contrôleur mais une classe métier de base.
    Je pensais qu'en appelant un service qui connait la session j'aurai pu l'utiliser dans ma classe ProjectBLL pour récupérer la session.

  12. #12
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Le contrôleur est le point d'entrée de toutes tes requêtes.
    À moins que tu utilises ce service via une commande, tu passeras forcément par le contrôleur avant d'atteindre ta classe ProjectBLL. Et c'est le rôle du contrôleur d'utiliser des services avant de retourner une réponse.

    Donc si ce n'est pas le contrôleur qui utilise directement le service ProjectBLL ce sera peut-être une (ou plusieurs) autre classe intermédiaire qui le fera et qui devra également être déclarée en tant que service.

    Une fois que tu as injecté ton service session dans ton service ProjectBLL, tu n'as plus besoin de t'occuper de quoique ce soit d'autre : chaque fois qu'il sera appelé (ou injecté), il possèdera la session.

    Globalement voilà comment on peut le résumer vulgairement :

    Le container de service est généré lors des passes de compilation, il recense tous les services, avec leur dépendances. Ce qui veut dire qu'à n'importe quel moment ou tu appelleras ProjectBLL (en tant que service, pas en faisant un new), il te mettra ses dépendances avec : donc SessionService. Et comme SessionService a une dépendance vers RequestStack, il va setter l'attribut $requestStack de la classe session avec le service RequestStack.

    Les services sont accessibles de deux manières
    - Directement depuis le container $this->container->get('my_service');, pour ça il faut que la classe implémente l'interface ContainerAware. En général, ce sont les contrôleurs par défaut qui implémentent cette interface, les commandes peuvent également être ContainerAware.

    ou alors

    - Par injection de dépendance : en déclarant toutes tes classes (sauf les entités) en tant que service, tu peux les injecter les unes dans les autres comme bon te semble (en faisant attention à ne pas créer un couplage de la mort ou des boucles de dépendance => A dépend de B qui dépend de C qui dépend de A.

    Ta classe métier a besoin de la session ? Tu lui as injecté : maintenant la question est : comment accèdes-tu au service ProjectBLL ?
    Soit depuis le contrôleur, auquel cas si tu as le contrôleur par défaut il est ContainerAware et accessible directement, soit il est dans une autre classe et donc cette autre classe dépend de ProjectBLL, donc il faut que lui injectes.

  13. #13
    Membre à l'essai
    Inscrit en
    Février 2013
    Messages
    25
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 25
    Points : 22
    Points
    22
    Par défaut
    Aille... Bon merci beaucoup à toi pour le temps consacré !

    Oui je passe par un contrôleur pour appeler ma couche métier ProjectBLL.

    Par contre je n'ai jamais considéré ProjectBLL comme un service... J'espérai plutôt qu'il puisse utiliser un service pour prendre connaissance de la session.

    Non je n'ai pas encore compris comment injecter la session à ma couche métier

    Comme indiqué en tout début de discussion cela ressemble à cela :

    Mon contrôleur "DefaulController connait la request et donc la session, pas de problème ici...
    Ce contrôleur appelle des méthodes (statiques) de mes couches métier, comme ProjectBLL, qui se charge d'insérer un projet dans la bdd.
    J'ai beaucoup d'autres méthodes insert/update/delete dans diverses couches métier équivalente à ProjectBLL.
    A chaque insert/update/delete j'ai besoin de connaitre l'identifiant de mon utilisateur courant qui est stocké en session.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class DefaultController extends Controller
    {
          public function indexAction()
        {
             ....
             ProjectBLL::insertProject($projectBO);
             TaskBLL::insertTask($taskBO);
             ....
         }
    }
    Suis-je obligé de passer cet identifiant à chaque appel du contrôleur vers mes couches métier ? Du type ProjectBLL::insertProject($projectBO, $this->get("request")->getSession()->get("User")->getId());.
    Ou puis-je imaginer pouvoir récupérer cette session au sein de ma couche métier ProjectBLL ?

    En te remerciant par avance.
    Je pense quand même finir par le passer en paramètre ce satané identifiant !!!!

  14. #14
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Ah oui ... alors celle là je l'avais pas vu venir. Les méthodes statiques ... :/ On aurait gagné du temps en commençant par là.

    Ben oui mais non ! Faire de l'objet pour n'utiliser que des méthodes statiques ça ne sert pas à grand chose mmh'voyez.

    Si tu utilises Symfony mais pas d'injection de dépendance, pas d'ORM et pas d'objet (parce que soyons honnêtes : utiliser des méthodes statiques d'une classe ce n'est pas faire de l'objet), tu vas avoir des problèmes ... tu vas même avoir des GROS problèmes.

    Bon et puis quand bien même tu utiliserais les classes et les services et les ORM et tout ce qui va bien, si c'est pour faire du CRUD en base de données, ton service n'a pas à connaitre la session. Ce n'est pas logique. Son boulot c'est d'insérer les valeurs qu'on lui donne. Si tu veux insérer une donnée avec un user qui ne vient pas de la session tu vas être obligé de faire une autre méthode qui prend en paramètre le user. Autant faire une seule méthode et lui passer le user en paramètre c'est bien plus logique. Récupérer le user de la session dans ton cas, c'est le boulot du contrôleur.

    Si tu avais un peu plus détaillé la manière dont tu utilises tes classes je ne t'aurais pas balancé tout ce charabia sur l'injection de dépendance.

    Donc mon conseil maintenant : fais des entités et utilise Doctrine. Et oublie les méthodes statiques : on est plus en 1999

Discussions similaires

  1. les sessions en dehors des applications web
    Par paonus dans le forum Windows Forms
    Réponses: 1
    Dernier message: 11/07/2008, 09h19
  2. [EJB Stateful] récupérer les sessions des utilisateurs
    Par robert.tari dans le forum Java EE
    Réponses: 4
    Dernier message: 07/12/2007, 10h50
  3. Session en dehors des servlets
    Par batataw dans le forum Servlets/JSP
    Réponses: 6
    Dernier message: 19/10/2007, 13h00
  4. [Sessions] Comment creer des sessions pour chaque visiteur
    Par developower dans le forum Langage
    Réponses: 2
    Dernier message: 06/10/2005, 12h55
  5. [servlet]récupérer la session
    Par deldin dans le forum Servlets/JSP
    Réponses: 7
    Dernier message: 09/08/2004, 19h04

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo