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 :

Plusieurs Bundles dans une même page [2.x]


Sujet :

Symfony PHP

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 21
    Points : 11
    Points
    11
    Par défaut Plusieurs Bundles dans une même page
    Bonjour à tous !

    Je crois que le titre parle de lui-même

    Je développe en php et je débute sous Symfony. Actuellement, je bloque sur cette question : comment faire pour utiliser 2 bundles différents sur une seule et même page ? Par exemple un bundle chat et un bundle météo ?

    Mon idée est de créer un Bundle Conteneur qui appellerait les bons bundles mais je ne sais pas bien comment m'y prendre.

    J'ai pas mal cherché sur le net et je n'ai pas trouvé de choses qui me semblait répondre à la question. Du coup je me demande si c'est que ma question est vraiment trop basique pour que quelqu'un l'ai posée avant moi ou si c'est que ma logique de développement n'est pas la bonne.

    En tous cas, je serais content d'avoir des avis ou conseils..

    Merci par avance !

  2. #2
    Membre expert
    Avatar de dukoid
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2012
    Messages
    2 100
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Novembre 2012
    Messages : 2 100
    Points : 3 004
    Points
    3 004
    Par défaut
    yoooooooooooooo mannnn rastafriiiiiiiiiiiiiiiiiiiiiiiiiiiii


    * tout simplement avec des includes pour du simple html :

    grossièrement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    include bundleChat/index.html.twig
    include bundleMeteo/index.html.twig
    * ou faire appel à un controleur dans la vue :

    grossièrement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    {{ render(controller('AcmeArticleBundle:Article:chat', { 'max': 3 })) }}
    {{ render(controller('AcmeArticleBundle:Article:meteo', { 'max': 3 })) }}

  3. #3
    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
    ... euh oui : c'est une solution. Pas la plus élégante mais c'est une solution qui fonctionne.

    Disons que la solution de dukoid a l'avantage de pouvoir utiliser tes blocs météo et chat sous forme de widget grâce au render controller. Tu peux placer un bloc météo sur n'importe quelle page simplement juste en faisant ce render. Par contre ça a le gros inconvénient de te faire autant de cycles Request/Response qu'il y aura de render. Ce qui, sur des applications un peu plus lourdes, aura tendance à ralentir ton site.

    L'autre solution plus propre mais plus complexe :

    En réfléchissant sous forme de fonctionnalités, tu as un service de chat, et un service de météo. Ces services sont des classes managers, services, appelle les comme tu veux. Elles sont dans des bundles différents mais peuvent être injectés n'importe ou dans ton projet.

    Si ton bundle conteneur comporte un controller, auquel tu injectes le service de météo et le service de chat, la page sur laquelle tu veux les afficher possède déjà elle même les deux éléments différents dont elle a besoin. Tu n'as plus qu'à afficher le tout dans ta template sans appeler d'autres controllers.

    Le rendu de ton chat ou de ton bloc météo peut même être géré sous forme de macro s'il doit être réutilisé et présent à d'autres endroits du site.

    ++

  4. #4
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 21
    Points : 11
    Points
    11
    Par défaut
    Merci pour vos réponses.

    Je vais essayer de m'orienter vers la solution plus propre mais plus complexe (tant qu'à faire les choses bien).

    Si j'ai bien compris, l'idée est de spécifier un controller en tant que service comme décrit ici http://symfony.com/doc/current/cookb...r/service.html et de l'appeler depuis n'importe quel autre contrôleur comme une simple classe ?

    Suis-je sur la bonne voie ? Je tiens vraiment à prendre de bonnes habitudes dès le départ.

  5. #5
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Septembre 2009
    Messages
    875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Septembre 2009
    Messages : 875
    Points : 1 313
    Points
    1 313
    Par défaut
    Tu as deux problème distinct: afficher le bout météo, qui sous entend quelque chose de peu complexe. Dans ce cas, pour garder des performances élevées, tu devrais regarder du coté des extensions twig et créer une fonction twig te générant ton bout de météo tu l'utiliserai par exemple comme ca: {{ meteo(today) }}
    Pour le bout de code de tchat, ca peut vide etre du code complexe. Il me semble inévitable d'utiliser les render controller(débutant) ou d'utiliser la solution de Nico, qui te demande de créer un controlleur de base dont étendent tous tes autres controlleurs, et ca c'est plus avancé deja).

    Extension twig > Maccro (injection de dépendance possible pour les extensions, moins chiant a utiliser aussi)
    embed > include (embed permet de redefinir seulement des blocs twigs du bloc inclus)


    Pour le contrôleur en tant que service, c'est sympa mais pas du tout obligatoire et ça t'apportera rien en tant que débutant.(c'est surtout interessant pour l'injection de dépendance)
    Par contre, si tu veux prendre un bon départ, tu devrais déléguer tout ton code métier dans un service, appelé par ton contrôleur.

  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
    Quitte à prendre de bonnes habitudes, habitue toi à quasiment tout déclarer en tant que service : les contrôleurs, les forms, les managers, les subscribers tu peux quasiment tout déclarer en service.

    Concernant ton dernier message, ce n'est pas tout à fait ça : tu peux effectivement déclarer le contrôleur qui va afficher ces deux éléments en tant que service.
    Par contre pour le reste : ce ne sont plus des contrôleurs que tu vas appeler mais d'autres services qui vont te retourner ce qu'il te faut pour faire ton chat et ta météo.

    Imaginons que tu aies aujourd'hui un contrôleur qui envoie un ou plusieurs éléments pour ta météo à la template qui l'affiche. Ces données ne doivent pas être "calculées" dans un contrôleur : ce sont d'autres services (appelons les des managers pour faire simple) qui vont s'en occuper. Le contrôleur lui ne doit pas faire de traitement métier.

    Donc admettons qu'aujourd'hui ton code ressemble globalement à ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class MeteoController {
     
        public function showAction()
        {
            $meteoManager = $this->container('service_meteo');
     
            $weather = $meteoManager->getWeather();
            $temperature = $meteoManager->getTemp();
     
            return array('temperature' => $temperature, 'weather' => $weather)
        }
     
    }
    C'est une supposition approximative.
    Le manager est un service qui va te permettre de récupérer les infos dont tu auras besoin pour faire le rendu dans la template (il a donc besoin d'être déclaré en tant que service). Ces données ne sont pas supposées être traitées et/ou modifiées dans le contrôleur.
    Tu es dans ton MeteoBundle, et ton contrôler fait ça. La template fait le rendu avec les variables température et weather.
    On va supposer que dans ton ChatBundle c'est globalement la même chose. (si ce n'est pas le cas et que tu n'as pas de manager, arrange toi pour que ça donne plus ou moins ça).

    Dans ton nouveau bundle, et ton nouveau contrôler : tu peux te contenter de ça et appeler via le container les deux services qui vont te retourner les éléments dont tu as besoin.
    Ou alors tu pousses le vice, et tu te débarrasses du container de service : ce qui signifie que tous les services que tu appelles via le container devront cette fois être injectés (ce qui signifie aussi que du coup ton contrôleur doit être également déclaré en tant que service).

    En résumé ça donnerait ça :

    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
    26
    27
    28
    29
    30
    use xxx\MeteoBundle\Manager\MeteoManager;
    use xxx\ChatBundle\Manager\ChatManager;
     
    class MyController {
     
        protected $chatManager;
     
        protected $meteoManager;
     
        public function __construct(ChatManager $chatManager, MeteoManager $meteoManager)
        {
            $this->chatManager = $chatManager;
            $this->meteoManager = $meteoManager;
        }
     
        public function meteoAndChatAction()
        {
            $weather = $this->meteoManager->getWeather();
            $temperature = $this->meteoManager->getTemp();
     
            $chatWindow = $this->chatManager->generateChatWindow();
     
            return array(
                'chatWindow' => $chatWindow,
                'temperature' => $temperature,
                'weather' => $weather
            )
        }
     
    }
    Maintenant je m'explique : ce contrôleur n'a plus besoin d'étendre une classe container aware puisqu'on lui injecte tous les services dont il a besoin (les dépendance).
    Ces services te retournent les données qui te permettent de générer dans ta vue la partie météo et la partie chat.

    Tu as donc un seul contrôleur, une seul template et dans les bundles respectifs, les managers qui s'occupent du traitement.

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 21
    Points : 11
    Points
    11
    Par défaut
    Merci Nico,

    Cela fait un moment que j'examine ta réponse et que je fais des tests et certaines choses me paraissent encore obscures (c'est parfois un peu frustrant de débuter).

    Je vais prendre les choses dans l'ordre :

    Dans ton exemple de la classe showAction, j'ai une erreur si je n'étend pas la classe comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    class MeteoController extends Controller
    , tu ne l'as pas étendue parce que c'était juste un exemple ou il y a une subtilité que je n'ai pas comprise ?

    Par ailleurs, on est d'accord que dans cette class MeteoController, tu ne retournes pas de response ? J'ai aussi du mal à comprendre, je croyais qu'un controller devait toujours renvoyer une réponse ?

    Désolé, je me rends bien compte que ce sont les bases

  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
    No problème c'est normal.

    Si tu n'étends pas la classe de base de Symfony, tu n'as pas accès au container de service. Du coup le $this->get() il te dit : "Eh bonhomme t'es bien gentil mais moi je connais pas cette méthode".
    En réalité le $this->get() que tu fais dans ton contrôler c'est un raccourci de $this->container->get().

    Et comme tu n'as pas le container si tu n'étends pas une classe qui implémente l'interface ContainerAware, tu l'as dans l'cubitus.

    Donc dans un premier temps tu peux faire étendre ton contrôler de la classe de base de Symfony pour garder à disposition le container. Tu la vireras après : une chose après l'autre

    Ensuite, ce que j'ai fais : j'ai retourné uniquement un array(). Là c'est de ma faute, j'avoue avoir été un peu vite dans les étapes : c'est un raccourci de symfony qui veut dire : "Je te retourne une réponse, dont la template correspond en arborescence à "Ressources/views/#nom_du_controller#/#nom_de_l_action#.html.twig"
    Et pour que ça fonctionne il faut lui mettre l'annotation @Template et rajouter :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
    Donc en gros ça donnerait ça en étant plus correct :

    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
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
     
    class MeteoController extends Controller {
        /**
         * @Template
         */
        public function showAction()
        {
            $meteoManager = $this->container('service_meteo');
     
            $weather = $meteoManager->getWeather();
            $temperature = $meteoManager->getTemp();
     
            return array('temperature' => $temperature, 'weather' => $weather);
     
            // équivaut à
            // return $this->render( 'MyMeteoBundle:Meteo:show.html.twig', array('temperature' => $temperature, 'weather' => $weather));
        }
     
    }
    Il ne faut pas oublier ensuite la template qui sera donc : Resources/views/Meteo/show.html.twig

    Voilà pour la première étape.

  9. #9
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 21
    Points : 11
    Points
    11
    Par défaut
    OK, j'ai compris, merci, c'est tout clair pour cette partie !

    Alors ensuite, bien sûr, je continu à ramer un peu.

    Prenons l'exemple de la météo (puisqu'après le principe est le même pour le reste).

    Si j'ai bien compris, le principe est, depuis le controller de mon bundle principal, d'aller récupérer la classe "service_meteo" que j'avais utilisée dans le MeteoController de mon MeteoBundle ? L'idée étant de ne pas charger les contrôleurs de mes bundles externes mais directement les services sur lesquels ils s'appuient ?

    Bon, si j'ai compris c'est déjà pas mal (j'attends confirmation avant de crier victoire).

    Par contre, dans la pratique, ça ne marche pas chez moi et je soupçonne que je me plante quelque part dans mon "use".

    use xxx\MeteoBundle\Manager\MeteoManager : ici, "Manager" est-il bien un dossier dans lequel a été placé la class "MeteoManager" (dans un fichier php du même nom) ?

    Je ne comprends pas car normalement j'ai fait attention à ce que le namespace suive bien le même chemin mais je me retoruve avec une catchable fatal error..

  10. #10
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 21
    Points : 11
    Points
    11
    Par défaut
    Non, en fait mon appel est ok. J'ai testé en instanciant la classe distante et ça marche bien.

    Je crois que mon problème doit plutôt venir de ne pas vraiment savoir comment déclarer le container en tant que service. J'ai un peu de mal à comprendre ce que cela signifie.

    (désolé pour le double post)

  11. #11
    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
    Ce n'est pas le container que tu déclares en tant que service : c'est le manager. Selon les projets et le choix du développeur, il y a un fichier xml, yml, ou php qui gère la déclaration des services pour chaque bundle afin qu'ils soient récupérables par le container.

    En gros le container : c'est le distributeur de services. Et les services tu peux les trouver en faisant dans un terminal "php app/console container:debug"
    Tu auras alors la liste de toutes les clés de service de Symfony ainsi que la classe à laquelle ils correspondent.

    Quand tu auras récupéré un objet qui soit une instance de ton MeteoManager grâce au $this->get('service_meteo'), tu auras quasiment fait le plus dur.

    Pour déclarer une classe en tant que service, voilà ce qu'il faut faire : http://symfony.com/fr/doc/current/bo...res-de-service

    Une fois que tu auras réussi à récupérer ton service depuis le MeteoController, tu n'auras besoin de rien de plus pour le récupérer depuis un autre controller qui ait accès au container.

  12. #12
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 21
    Points : 11
    Points
    11
    Par défaut
    C'est bon, j'arrive à récupérer le service et tout fonctionne comme je veux, j'ai pu relier mes 2 bundles avec succès

    Merci beaucoup pour les explications !

    Sinon, je suis en train de chercher dans quel cas il est préférable d'appeler les services des bundles (comme ici) et dans quel cas un simple render(controller) dans le template est plus pratique. Il y a de la doc où on parle un peu de ces deux manières de faire avec leurs "avantages et inconvénients" ?

    Merci encore !

  13. #13
    Membre expert
    Avatar de dukoid
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2012
    Messages
    2 100
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Novembre 2012
    Messages : 2 100
    Points : 3 004
    Points
    3 004
    Par défaut
    malheureusement c'est le grand fouteur sur Symfony... des explications il y en a peu

  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
    Le render controller est très rarement la meilleure option dans la mesure ou elle oblige à refaire tout le cycle Request/Response.

    C'est pratique lorsque tu veux embarquer rapidement un bloc, utiliser un bout de code comme un widget, et l'utiliser un peu partout.

    Typiquement pour un dashboard, tout un ensemble de blocs qui tape dans des bundles différents, dans lequel (par exemple) tu ne vas pas afficher les mêmes éléments en fonction des rôles ou d'autre chose, ça peut être pertinent de les utiliser. Parce que c'est potentiellement plus pratique que de faire des templates différents, ou de gérer les différents cas de figure dans le controller.

    Mais encore faut-il que ce soit réutilisable/réutilisé ailleurs.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [MySQL] Plusieurs boutons dans une même page
    Par phpines dans le forum PHP & Base de données
    Réponses: 6
    Dernier message: 14/04/2009, 15h03
  2. Réponses: 0
    Dernier message: 01/07/2008, 16h16
  3. [XHTML] comment déclarer plusieurs doctypes dans une même page
    Par elsapascal dans le forum Balisage (X)HTML et validation W3C
    Réponses: 1
    Dernier message: 21/06/2008, 11h20
  4. Réponses: 11
    Dernier message: 07/08/2007, 17h11

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