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 :

[TUTO][1.3, 1.4] Listes liées ou listes dépendantes


Sujet :

Symfony PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Invité
    Invité(e)
    Par défaut [TUTO][1.3, 1.4] Listes liées ou listes dépendantes
    Bonjour à tous,

    Bon étant donné qu'on a eu ces derniers jours plusieurs fois la question "Comment on fait pour faire des select box dépendantes l'un de l'autre en symfony ?", je fais un rapide tuto. Et vous allez voir que même si on code avec symfony, ça n'a rien de sorcier.

    Avertissement : Je ne prétends absolument pas que mon post présente la meilleure des solutions. C'est un tuto basique, fonctionnel, qui devrait pouvoir fournir à ceux qui ont du mal une base afin d'implémenter cette fonctionnalité. Je n'ai sûrement pas gérer tous les cas possibles, les erreurs possibles, je n'ai pas utilisé de sfForm ce sont juste deux selects basiques mais le principe reste le même avec un sfForm. De plus, ce qui est fait au niveau du modèle est sûrement pas ce qu'il y a de mieux, mais ce n'est pas le sujet. (Le findAll() c'est maaaaaaaaaaaaaal)

    Toutes les remarques et commentaires afin d'améliorer l'exemple sont les bienvenus, mais je répète que celui-ci n'a pas pour but de gérer tous les trucs possibles et imaginables pour ce genre de cas. Les questions sont aussi les bienvenues.


    Contexte : Aller on va prendre un sujet un peu sympathique, vous imaginez qu'on est dans un jeu, qu'on a des personnages, qui possèdent un ensemble d'objets dans leur inventaire. Et on va faire justement l'écran qui va nous permettre d'utiliser ces objets.

    C'est parti !

    Pour commencer, je vous laisse le soin d'installer symfony, d'installer votre projet, de générer une application (que j'appellerai frontend pendant ce tuto). D'avoir votre serveur apache configuré et avoir la page par défaut de symfony. Une base de donnée disponible aussi, avec le database.yml qui va bien.

    Bon déjà, de quoi va-t-on avoir besoin ? On veut deux listes en ajax (non, pas le produit ménager ...), il nous faut de quoi faire de l'ajax. On va donc commencer par récupérer jQuery. Oui, c'est un choix tout à fait arbitraire, mais c'est comme ça. Et non, je ne rédigerai pas une version du tuto avec un autre framework javascript.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ./symfony plugin:install sfJqueryReloadedPlugin
    Bon il me faut aussi un model correct pour travailler. Beaucoup d'entre-vous aurons le leur mais je mets le miens au cas où certaines personnes veuillent faire le tuto pas à pas.
    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
    31
    32
    33
    34
    # /config/doctrine/schema.yml
    MainCharacter:
      columns:
        id: { type: integer(4), unsigned: true, notnull: true, primary: true, autoincrement: true }
        name: { type: string(50), notnull: true }
      relations:
        Items:
          class: Item
          refClass: MainCharacterItem
          local: main_character_id
          foreign: item_id
          foreignAlias: Characters
     
    Item:
      columns:
        id: { type: integer(4), unsigned: true, notnull: true, primary: true, autoincrement: true }
        name: { type: string(50), notnull: true }
     
    MainCharacterItem:
      columns:
        main_character_id: { type: integer(4), unsigned: true, notnull: true, primary: true }
        item_id: { type: integer(4), unsigned: true, notnull: true, primary: true }
        quantity: {type: integer(4), unsigned: true, notnull: true }
      relations:
        MainCharacter:
          local: main_character_id
          foreign: id
          foreignAlias: MainCharacterItems
          onDelete: CASCADE
        Item:
          local: item_id
          foreign: id
          foreignAlias: MainCharacterItems
          onDelete: CASCADE
    On va avoir besoin d'avoir aussi des données pour travailler, voici donc le fixtures.yml pour ceux qui en auraient besoin :
    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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    # /data/fixtures.yml
    MainCharacter:
      lightning:
        name: Lightning
      serah:
        name: Serah
      vanille:
        name: Vanille
     
    Item:
      potion:
        name: Potion
      queue_de_phenix:
        name: Queue de Phénix
      elixir:
        name: Elixir
      antidote:
        name: Antidote
      collyre:
        name: Collyre
     
    MainCharacterItem:
      potion_lightning:
        MainCharacter: lightning
        Item: potion
        quantity: 5
      potion_serah:
        MainCharacter: serah
        Item: potion
        quantity: 2
      elixir_serah:
        MainCharacter: serah
        Item: elixir
        quantity: 3
      queue_de_phenix_vanille:
        MainCharacter: vanille
        Item: queue_de_phenix
        quantity: 2
      antidote_vanille:
        MainCharacter: vanille
        Item: antidote
        quantity: 4
      collyre_vanille:
        MainCharacter: vanille
        Item: collyre
        quantity: 1
    Donc vous êtes censés avoir votre application qui fonctionne, on va avoir besoin d'un module maintenant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ./symfony generate:module frontend inventory
    On fait une petite route de base, obligatoire puisque vous avez supprimé les routes par défaut vu que vous suivez les recommandations :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    # /apps/frontend/config/routing.yml
    inventory_show:
      url: /inventory.:sf_format
      param: { module: inventory, action: index, sf_format: html }
    Alors, quand on va arriver sur l'écran de l'inventaire, on aura déjà une liste prête avec le nom des personnages. Notre petite action est donc très simple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // /apps/frontend/modules/inventory/actions/actions.class.php
    class inventoryActions extends sfActions
    {
      public function executeIndex(sfWebRequest $request)
      {
        $this->characters = Doctrine::getTable('MainCharacter')->findAll();
      }
    }
    Le template qui va avec :
    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
     
    <!-- /apps/frontend/modules/inventory/templates/indexSuccess.php -->
    <?php use_javascript('/sfJqueryReloadedPlugin/js/jquery-1.3.2.min.js'); ?>
    <h1>Inventory</h1>
    <select name="character_id" id="characters">
        <option>Please select a character</option>
    <?php foreach ($characters as $character): ?>
        <option value="<?php echo $character->getId() ?>"><?php echo $character->getName(); ?></option>
    <?php endforeach; ?>
    </select>
    <select name="item_id" id="items"></select>
    <input type="submit" value="Use this item !" />
     
    <script type="text/javascript">
        $(document).ready(function() {
            $("#characters").change(function(event) {
                var characterId = $(event.target).val();
                $.post('<?php echo url_for('@inventory_update_items_list') ?>', { character_id: characterId }, function(result) {
                    $("#items").html(result);
                });
            });
        });
    </script>
    C'est peu commenté je sais, mais ça ne devrait pas être bien dur à comprendre. Ici on affiche notre première liste avec les personnages, et on affiche une deuxième liste vide. Ensuite il y a du javascript. Je vous suggère d'apprendre jQuery si vous y comprenez rien , mais en gros, quand la page est chargée, on surveille l'évènement "onChange" de notre liste de personnage pour faire une requête ajax dès que la valeur sélectionnée change.
    On récupère donc la valeur actuellement selectionnée, puis on la post vers la route @inventory_update_items_list

    Là dans vos têtes ça doit faire "Ding ! On a une nouvelle route" (Attention, si ça ding trop fort c'est que votre tête est vide). Et ben il reste plus qu'à la rajouter dans le routing.yml :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    # /apps/frontend/config/routing.yml
    inventory_update_items_list:
      url: /inventory/update-items-list.:sf_format
      param: { module: inventory, action: updateItemsList, sf_format: html }
      requirements:
        sf_method: post
    Ah, maintenant on a donc une nouvelle action qui va se charger de récupérer les éléments de la deuxième liste. Allons-y alors :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // apps/frontend/modules/inventory/actions/actions.class.php
    public function executeUpdateItemsList(sfWebRequest $request)
      {
        if (!$request->isXmlHttpRequest())
        {
          return sfView::NONE;
        }
     
        $this->character = Doctrine::getTable('MainCharacter')->find($request->getParameter('character_id'));
        if (!$this->character)
        {
          return sfView::NONE;
        }
      }
    Rien de sorcier là encore : Si ce n'est pas une requête ajax, on renvoit une page blanche. Bon à la place vous auriez pu faire une redirection, une 404, ou ce que vous voulez peu importe. Ensuite on récupère le personnage concerné grâce au paramètre transmis par post.
    Dans le cas où on ne le trouve pas, là encore j'ai choisi d'afficher une page blanche.
    Le template qui va avec cette action :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    <!-- /apps/frontend/modules/inventory/templates/updateItemsListSuccess.php -->
    <?php foreach ($character->getMainCharacterItems() as $mainCharacterItem): ?>
    <option value="<?php echo $mainCharacterItem->getItem()->getId() ?>"><?php echo $mainCharacterItem; ?></option>
    <?php endforeach; ?>
    Alors là, j'ai fait le rendu des options de ma 2ème liste en html directement. Vous pouvez soit faire pareil, soit par exemple faire un rendu en json et utiliser le javascript après le retour de votre requête ajax pour construire les différentes options. Bon, le plus simple je trouve c'est de faire le html directement, j'ai choisi ça par soucis de rapidité à faire. Et si vous avez rien compris à ce que je viens de dire, faites comme moi
    Donc là, toujours rien de sorcier. Cette page va afficher une liste de tags <option> qui contiendront l'id de l'objet en value, et qui en texte affichent directement l'objet MainCharacterItem. MainCharacterItem ? Mais il n'y a que 3 colonnes de type entier dedans. Diantre ! Pourquoi lui ? Alors en effet, j'aurais pu afficher directement le nom de l'objet, mais dans MainCharacterItem on a aussi une donnée qui peut être intéressante : La quantité possédée par le personnage ! Donc on va se faire la petite fonction __toString() qui va bien dans le model :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // /lib/model/doctrine/MainCharacterItem.class.php
    class MainCharacterItem extends BaseMainCharacterItem
    {
        public function __toString()
        {
            return $this->getItem()->getName()." (".$this->getQuantity().")";
        }
    }
    (Oui, comme vous vous en doutez, ça n'a rien à voir avec le tuto . Mais c'est moi qui rédige, donc "je fais qu'est ce que je veux" comme dirait mon petit cousin)

    Et voilà, votre requête ajax renvoit donc du html qui est inséré directement à l'intérieur de la seconde liste.

    Voilà, c'est fini. Au final je me rend compte que c'était tellement pas compliqué que peut-être que personne va apprendre quoi que ce soit avec ce truc .
    En tout cas, y'a rien de différent parce qu'on est en symfony. La seule chose qui diffère, c'est où vous mettez votre code qui gère la requête ajax en gros.

  2. #2
    Membre confirmé
    Inscrit en
    Novembre 2009
    Messages
    77
    Détails du profil
    Informations forums :
    Inscription : Novembre 2009
    Messages : 77
    Par défaut
    Merci mon frère
    merci beaucoup

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 19
    Par défaut
    Hello

    merci pour ce tuto ! il est censé faire ce que je cherche à faire désespérément sans y parvenir.

    Quand j'essaye d'installer sfJqueryReloadedPlugin avec la commande qui est donnée, j'ai l'erreur

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    >> plugin    installing plugin "sfJqueryReloadedPlugin"
    PHP Warning:  require_once(PEAR.php): failed to open stream: No such file or directory in /home/vincent/www/Poker05/lib/vendor/symfony/lib/plugin/sfPearEnvironment.class.php on line 15
    PHP Fatal error:  require_once(): Failed opening required 'PEAR.php' (include_path='.:/usr/share/php:/usr/share/pear') in /home/vincent/www/Poker05/lib/vendor/symfony/lib/plugin/sfPearEnvironment.class.php on line 15
    j'ai trouvé des tas de messages sur des forums qui postent ce problème, mais jamais personne n'y répond

    J'ai essayé de suivre un tuto pour installer manuellement un plugin, mais ça n'a pas l'air d'avoir trop fonctionné... comment puis je savoir si le plugin est installé correctement? sinon comment puis je faire pour l'installer automatiquement? j'ai symfony 1.4

    aussi je vois dans le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <?php use_javascript('/sfJqueryReloadedPlugin/js/jquery-1.3.2.min.js'); ?>
    mais quand j'ai téléchargé le plugin, ce fichier est dans

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [root de mon site]/plugins/sfJqueryReloadedPlugin/web/js/jquery-1.3.2.min.js
    est ce normal?

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 19
    Par défaut
    Bon pour mon premier problème à force de chercher, j'ai trouvé qu'il fallait que j'installe php-PEAR sur mon système pour utiliser l'installation automatique de plugins

  5. #5
    Expert confirmé
    Avatar de Michel Rotta
    Homme Profil pro
    DPO
    Inscrit en
    Septembre 2005
    Messages
    4 954
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : DPO
    Secteur : Distribution

    Informations forums :
    Inscription : Septembre 2005
    Messages : 4 954
    Par défaut
    Un beau retour

    Bravo.

    Juste une remarque sur la deuxième liste, on perd la compatibilité ascendante, "un formulaire doit rester viable sans javascript".

Discussions similaires

  1. [AJAX] Liste liées et tuto de siddh
    Par rider74 dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 22/05/2008, 08h19
  2. Valeur par défaut dans la seconde liste de 2 listes liées
    Par orus8 dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 07/06/2007, 14h11
  3. Réponses: 1
    Dernier message: 28/09/2005, 18h10
  4. Problème avec listes liées entre elles et bouton "précé
    Par Oluha dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 02/08/2005, 15h10

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