Formulaires : Listes déroulantes dynamiques liées

3 messages   •   Page 1 sur 1
Mammouth du PHP | 19664 Messages

17 juin 2005, 09:01

Listes déroulantes liées

Un problème très souvent posé :
J'ai deux listes déroulantes dont le contenu est extrait d'une base de données.
Mais je voudrais que quand je sélectionne une valeur dans la première, le contenu de la seconde s'ajuste parce que selon le choix dans la première liste, je n'aurai pas les mêmes éléments dans la seconde.
Je vais illustrer la solution en faisant afficher les département d'une région sélectionnée. J'aurai donc une base de données avec deux tables, une table pour les régions, une seconde pour les départements. En sélectionnant une région dans la première liste, je vais faire afficher la seconde liste correspondand aux départements de la région choisie.

Nous verrons successivement trois méthodes. Celle-ci pour commencer qui n'utilise que le PHP, une seconde faisant appel à JavaScript et enfin une alternative avec AJAX.

Voici d'abord la base de données regions et sa structure:

Code : Tout sélectionner

-- -- Base de données: `regions` -- -- -------------------------------------------------------- -- -- Structure de la table `region` -- CREATE TABLE `region` ( `id_region` tinyint(4) NOT NULL auto_increment, `region` varchar(50) NOT NULL default '', PRIMARY KEY (`id_region`) ); -- -------------------------------------------------------- -- -- Structure de la table `departement` -- CREATE TABLE `departement` ( `id_departement` char(2) NOT NULL default '', `departement` varchar(50) NOT NULL default '', `id_region` tinyint(4) NOT NULL default '0', PRIMARY KEY (`id_departement`), KEY `id_region` (`id_region`) ); -- -------------------------------------------------------- -- -- Contenu de la table `region` -- INSERT INTO `region` VALUES (1, 'Alsace'); INSERT INTO `region` VALUES (2, 'Aquitaine'); INSERT INTO `region` VALUES (3, 'Auvergne'); INSERT INTO `region` VALUES (4, 'Basse-Normandie'); INSERT INTO `region` VALUES (5, 'Bourgogne'); INSERT INTO `region` VALUES (6, 'Bretagne'); INSERT INTO `region` VALUES (7, 'Centre'); INSERT INTO `region` VALUES (8, 'Champagne'); INSERT INTO `region` VALUES (9, 'Corse'); INSERT INTO `region` VALUES (10, 'Franche-Comté'); INSERT INTO `region` VALUES (11, 'Haute-Normandie'); INSERT INTO `region` VALUES (12, 'Île-de-France'); INSERT INTO `region` VALUES (13, 'Languedoc-Roussillon'); INSERT INTO `region` VALUES (14, 'Limousin'); INSERT INTO `region` VALUES (15, 'Lorraine'); INSERT INTO `region` VALUES (16, 'Midi-Pyrénées'); INSERT INTO `region` VALUES (17, 'Nord-pas-de-Calais'); INSERT INTO `region` VALUES (18, 'Pays de la Loire'); INSERT INTO `region` VALUES (19, 'Picardie'); INSERT INTO `region` VALUES (20, 'Poitou-Charentes'); INSERT INTO `region` VALUES (21, 'Provence-Alpes-Côte-d\'Azur'); INSERT INTO `region` VALUES (22, 'Rhône-Alpes'); -- -- Contenu de la table `departement` -- INSERT INTO `departement` VALUES ('01', 'Ain', 22); INSERT INTO `departement` VALUES ('02', 'Aisne', 19); INSERT INTO `departement` VALUES ('03', 'Allier', 3); INSERT INTO `departement` VALUES ('04', 'Alpes-de-Haute-Provence', 21); INSERT INTO `departement` VALUES ('05', 'Hautes-Alpes', 21); INSERT INTO `departement` VALUES ('06', 'Alpes-Maritimes', 21); INSERT INTO `departement` VALUES ('07', 'Ardèche', 22); INSERT INTO `departement` VALUES ('08', 'Ardennes', 8); INSERT INTO `departement` VALUES ('09', 'Ariège', 16); INSERT INTO `departement` VALUES ('10', 'Aube', 8); INSERT INTO `departement` VALUES ('11', 'Aude', 13); INSERT INTO `departement` VALUES ('12', 'Aveyron', 16); INSERT INTO `departement` VALUES ('13', 'Bouches-du-Rhône', 21); INSERT INTO `departement` VALUES ('14', 'Calvados', 4); INSERT INTO `departement` VALUES ('15', 'Cantal', 3); INSERT INTO `departement` VALUES ('16', 'Charente', 20); INSERT INTO `departement` VALUES ('17', 'Charente-Maritime', 20); INSERT INTO `departement` VALUES ('18', 'Cher', 7); INSERT INTO `departement` VALUES ('19', 'Corrèze', 14); INSERT INTO `departement` VALUES ('2A', 'Corse-du-Sud', 9); INSERT INTO `departement` VALUES ('2B', 'Haute-Corse', 9); INSERT INTO `departement` VALUES ('21', 'Côte-d\'Or', 5); INSERT INTO `departement` VALUES ('22', 'Côtes-d\'Armor', 6); INSERT INTO `departement` VALUES ('23', 'Creuse', 14); INSERT INTO `departement` VALUES ('24', 'Dordogne', 2); INSERT INTO `departement` VALUES ('25', 'Doubs', 10); INSERT INTO `departement` VALUES ('26', 'Drôme', 22); INSERT INTO `departement` VALUES ('27', 'Eure', 11); INSERT INTO `departement` VALUES ('28', 'Eure-et-Loir', 7); INSERT INTO `departement` VALUES ('29', 'Finistère', 6); INSERT INTO `departement` VALUES ('30', 'Gard', 13); INSERT INTO `departement` VALUES ('31', 'Haute-Garonne', 16); INSERT INTO `departement` VALUES ('32', 'Gers', 16); INSERT INTO `departement` VALUES ('33', 'Gironde', 2); INSERT INTO `departement` VALUES ('34', 'Hérault', 13); INSERT INTO `departement` VALUES ('35', 'Ille-et-Vilaine', 6); INSERT INTO `departement` VALUES ('36', 'Indre', 7); INSERT INTO `departement` VALUES ('37', 'Indre-et-Loire', 7); INSERT INTO `departement` VALUES ('38', 'Isère', 22); INSERT INTO `departement` VALUES ('39', 'Jura', 10); INSERT INTO `departement` VALUES ('40', 'Landes', 2); INSERT INTO `departement` VALUES ('41', 'Loir-et-Cher', 7); INSERT INTO `departement` VALUES ('42', 'Loire', 22); INSERT INTO `departement` VALUES ('43', 'Haute-Loire', 3); INSERT INTO `departement` VALUES ('44', 'Loire-Atlantique', 18); INSERT INTO `departement` VALUES ('45', 'Loiret', 7); INSERT INTO `departement` VALUES ('46', 'Lot', 16); INSERT INTO `departement` VALUES ('47', 'Lot-et-Garonne', 2); INSERT INTO `departement` VALUES ('48', 'Lozère', 13); INSERT INTO `departement` VALUES ('49', 'Maine-et-Loire', 18); INSERT INTO `departement` VALUES ('50', 'Manche', 4); INSERT INTO `departement` VALUES ('51', 'Marne', 8); INSERT INTO `departement` VALUES ('52', 'Haute-Marne', 8); INSERT INTO `departement` VALUES ('53', 'Mayenne', 18); INSERT INTO `departement` VALUES ('54', 'Meurthe-et-Moselle', 15); INSERT INTO `departement` VALUES ('55', 'Meuse', 15); INSERT INTO `departement` VALUES ('56', 'Morbihan', 6); INSERT INTO `departement` VALUES ('57', 'Moselle', 15); INSERT INTO `departement` VALUES ('58', 'Nièvre', 5); INSERT INTO `departement` VALUES ('59', 'Nord', 17); INSERT INTO `departement` VALUES ('60', 'Oise', 19); INSERT INTO `departement` VALUES ('61', 'Orne', 4); INSERT INTO `departement` VALUES ('62', 'Pas-de-Calais', 17); INSERT INTO `departement` VALUES ('63', 'Puy-de-Dôme', 3); INSERT INTO `departement` VALUES ('64', 'Pyrénées-Atlantiques', 2); INSERT INTO `departement` VALUES ('65', 'Hautes-Pyrénées', 16); INSERT INTO `departement` VALUES ('66', 'Pyrénées-Orientales', 13); INSERT INTO `departement` VALUES ('67', 'Bas-Rhin', 1); INSERT INTO `departement` VALUES ('68', 'Haut-Rhin', 1); INSERT INTO `departement` VALUES ('69', 'Rhône', 22); INSERT INTO `departement` VALUES ('70', 'Haute-Saône', 10); INSERT INTO `departement` VALUES ('71', 'Saône-et-Loire', 5); INSERT INTO `departement` VALUES ('72', 'Sarthe', 18); INSERT INTO `departement` VALUES ('73', 'Savoie', 22); INSERT INTO `departement` VALUES ('74', 'Haute-Savoie', 22); INSERT INTO `departement` VALUES ('75', 'Paris', 12); INSERT INTO `departement` VALUES ('76', 'Seine-Maritime', 11); INSERT INTO `departement` VALUES ('77', 'Seine-et-Marne', 12); INSERT INTO `departement` VALUES ('78', 'Yvelines', 12); INSERT INTO `departement` VALUES ('79', 'Deux-Sèvres', 20); INSERT INTO `departement` VALUES ('80', 'Somme', 19); INSERT INTO `departement` VALUES ('81', 'Tarn', 16); INSERT INTO `departement` VALUES ('82', 'Tarn-et-Garonne', 16); INSERT INTO `departement` VALUES ('83', 'Var', 21); INSERT INTO `departement` VALUES ('84', 'Vaucluse', 21); INSERT INTO `departement` VALUES ('85', 'Vendée', 18); INSERT INTO `departement` VALUES ('86', 'Vienne', 20); INSERT INTO `departement` VALUES ('87', 'Haute-Vienne', 14); INSERT INTO `departement` VALUES ('88', 'Vosges', 15); INSERT INTO `departement` VALUES ('89', 'Yonne', 5); INSERT INTO `departement` VALUES ('90', 'Territoire de Belfort', 10); INSERT INTO `departement` VALUES ('91', 'Essonne', 12); INSERT INTO `departement` VALUES ('92', 'Hauts-de-Seine', 12); INSERT INTO `departement` VALUES ('93', 'Seine-Saint-Denis', 12); INSERT INTO `departement` VALUES ('94', 'Val-de-Marne', 12); INSERT INTO `departement` VALUES ('95', 'Val-d\'Oise', 12);
À présent le code pour récupérer les informations que je veux: Il y a deux possibilités pour avoir un changement des valeurs contenues dans la seconde liste :
- Soit on fait recharger la page en soumettant le formulaire et on effectue une requête pour obtenir le contenu nécessaire à la seconde liste;
- Soit on utilise JavaScript pour stocker les valeurs « départements » dépendantes de la région sélectionnée.

Avantages et inconvients ?

- La première méthode fonctionne même si JavaScript est désactivé sur le navigateur de l'internaute. En revanche, comme il y a échanges d'informations entre client et serveur, c'est un peu plus lent.
- Le seconde, vous l'aurez deviné, a l'inconvénient d'être en panne si JavaScript est désactivé, mais est beaucoup plus rapide parce qu'elle s'exécute directement sur la machine du client sur laquelle on stocke les informations complètes.

Voici le code pour la méthode 100% PHP:
<?php
echo("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
/* Variables de connexion : ajustez ces paramètres selon votre propre environnement */
$serveur = "localhost";
$admin   = "root";
$mdp     = "";
$base    = "regions";
/* On récupère si elle existe la valeur de la région envoyée par le formulaire */
$idr = isset($_POST['region'])?$_POST['region']:null;
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" xml:lang="fr" />
<title>Sélectionner un département selon la région choisie</title>
<meta name="description" content="Listes déroulantes dynamiques inter-dépendantes" />
<meta name="keywords" content="" />
<meta name="author" content="Cyrano" />
<meta name="generator" content="Zend Studio Environnement et WebExpert 5" />
<meta http-equiv="imagetoolbar" content="no" />
<meta http-equiv="Pragma" content="no-cache" />
</head>
<body style="font-family: verdana, helvetica, sans-serif; font-size: 85%">
<?php
if(isset($_POST['ok']) && isset($_POST['departement']) && $_POST['departement'] != "")
{
    $region_selectionnee = $_POST['region'];
    $dept_selectionne = $_POST['departement'];
?>
<p>Vous avez sélectionné le département <?php echo($dept_selectionne); ?> dans la région <?php echo($region_selectionnee); ?></p>
<?php
}
?>
<h3>Trouver un département</h3>
<?php
/* On établit la connexion à MySQL avec mysql_pconnect() plutôt qu'avec mysql_connect()
*  car on aura besoin de la connexion un peu plus loin dans le script */
$connexion = mysql_pconnect($serveur, $admin, $mdp);
if($connexion != false)
{
    $choixbase = mysql_select_db($base, $connexion);
    $sql1 = "SELECT `id_region`, `region`".
    " FROM `region`".
    " ORDER BY `id_region`";
    $rech_regions = mysql_query($sql1);
    $code_region = array();
    $region = array();
    /* On active un compteur pour les régions */
    $nb_regions = 0;
    if($rech_regions != false)
    {
        while($ligne = mysql_fetch_assoc($rech_regions))
        {
            array_push($code_region, $ligne['id_region']);
            array_push($region, $ligne['region']);

            /* On incrémente de compteur */
            $nb_regions++;
        }
    }
    ?>
<form action="<?php echo($_SERVER['PHP_SELF']); ?>" method="post" id="chgdept">
<fieldset style="border: 3px double #333399">
<legend>Sélectionnez une région</legend>
<select name="region" id="region" onchange="document.forms['chgdept'].submit();">
  <option value="-1">- - - Choisissez une région - - -</option>
    <?php
    for($i = 0; $i < $nb_regions; $i++)
    {
?>
  <option value="<?php echo($code_region[$i]); ?>"<?php echo((isset($idr) && $idr == $code_region[$i])?" selected=\"selected\"":null); ?>><?php echo($region[$i]); ?></option>
<?php
    }
    ?>
</select>
    <?php
    mysql_free_result($rech_regions);
    /* On commence par vérifier si on a envoyé un numéro de région et le cas échéant s'il est différent de -1 */

    if(isset($idr) && $idr != -1)
    {
        /* Cération de la requête pour avoir les départements de cette région */
        $sql2 = "SELECT `id_departement`, `departement`".
        " FROM `departement`".
        " WHERE `id_region` = ". $idr ."".
        " ORDER BY `id_departement`;";
        if($connexion != false)
        {
            $rech_dept = mysql_query($sql2, $connexion);
            /* Un petit compteur pour les départements */
            $nd = 0;
            /* On crée deux tableaux pour les numéros et les noms des départements */
            $code_dept = array();
            $nom_dept = array();
            /* On va mettre les numéros et noms des départements dans les deux tableaux */
            while($ligne_dept = mysql_fetch_assoc($rech_dept))
            {
                array_push($code_dept, $ligne_dept['id_departement']);
                array_push($nom_dept, $ligne_dept['departement']);
                $nd++;
            }
            /* Maintenant on peut construire la liste déroulante */
            ?>
<select name="departement" id="departement">
            <?php  
            for($d = 0; $d<$nd; $d++)
            {
                ?>
  <option value="<?php echo($code_dept[$d]); ?>"<?php echo((isset($dept_selectionne) && $dept_selectionne == $code_dept[$d])?" selected=\"selected\"":null); ?>><?php echo($nom_dept[$d]." (". $code_dept[$d] .")"); ?></option>
                <?php
            }
?>
</select>
<?php
        }
        /* Un petit coup de balai */
        mysql_free_result($rech_dept);
    }
?>
<br /><input type="submit" name="ok" id="ok" value="Envoyer" />
</fieldset>
</form>
<?php
    /* Terminé, on ferme la connexion */
    mysql_close($connexion);
}
else
{
    /* Si on arrive là, c'est pas bon signe, il faut vérifier les 
    * paramètres de connexion, mot de passe, serveur pas démarré etc... */
?>
<p>Un incident s'est produit lors de la connexion à la base de données, veuiillez essayer à nouveau ultérieurement.</p>
<?php
}
?>
</body>
</html>
Voir la Démonstration: ICI

Téléchargez les fichiers (index.php + region.sql) zippés ICI

.. A suivre, la méthode avec JavaScript.
Modifié en dernier par Cyrano le 17 déc. 2006, 23:17, modifié 9 fois.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Mammouth du PHP | 19664 Messages

17 juin 2005, 18:34

La suite : la version avec JavaScript

Pour faire la même chose avec JavaScript, nous utiliserons outre la même base de données, deux fichiers externes JavaScript.

Le premier est directement tiré du site AideJavaScript et va servir à construire un tableau d'éléments à partir d'un tableau PHP des éléments issus de la requête SQL.

Le second est une fonction qui génère la seconde liste déroulante en fonction du choix effectué sur la première liste. Cette fonction est maison.

Et enfin la page elle même.

Premier fichier que j'ai nommé arrayPHP2JS.js
/* Date de création: 24/03/2005 */
/**
* Fonctions de sérialisation de tableaux PHP vers des tableaux JavaScript
* On récupère des valeurs de PHP pour les retourner en JavaScript.
*/
function PhpArray2Js(tabphp_serialise)
{
    this.php = corrigerChainePHP(tabphp_serialise);
    var dim = this.extraireDimTab();
    this.tabjs = this.transformer(dim);
}

PhpArray2Js.prototype.retour = function()
{
    // retourne le tableau JS
    return this.tabjs;
}

PhpArray2Js.prototype.transformer = function(dim)
{
    // méthode principale qui transforme la chaîne sérialisée en un tableau Javascript
    // dim est la dimension du tableau PHP
    var tab = new Array();
    // extrait un groupe de dim données (indice - valeur)
    for (var i=0;i<dim;i++)
    {
        // extrait un indice : numérique ou littéral
        var indice = this.extraireIndice();
        if (indice == -1)
        {
            return;
        }
        // extrait une valeur : tableau, null, booléen, numérique ou littéral
        var valeur = this.extraireValeur();
        if (valeur == -1)
        {
            tab[indice] = undefined;
        }
        else
        {
            switch (valeur[0])
            {
                case "N" : tab[indice] = null;                          break;
                case "b" : tab[indice] = valeur[1] ? true : false;      break;
                case "i" : tab[indice] = parseInt(valeur[1]);           break;
                case "d" : tab[indice] = parseFloat(valeur[1]);         break;
                case "s" : tab[indice] = valeur[1];                     break;
                case "a" : tab[indice] = this.transformer(valeur[1]);   break;
                default  : tab[indice] = undefined;
            }
        }
    }
    // en fin de groupe de données, supprime le "}" final
    this.php = this.php.substring(1);
    return tab;
}

PhpArray2Js.prototype.extraireDimTab = function()
{
    // extrait la dimension N du tableau de "a:N:{"
    var reg = this.php.match(/^a:(\d+):\{/);
    if (reg != -1)
    {
        // on coupe le texte de l'entité détectée
        this.php = this.php.substring(reg[0].length);
        return reg[1];
    }
    else
    {
        return -1;
    }
}

PhpArray2Js.prototype.extraireIndice = function()
{
    // extrait l'indice d'un tableau
    // cet indice peut être de la forme "i:\d+" ou "s:\d+:\"\w+\""
    var retour;
    var reg = this.php.match(/^((i):(\d+);|(s):\d+:"([^"]+)";)/);
    if (reg != -1)
    {
        // on coupe le texte de la chaîne détectée
        this.php = this.php.substring(reg[0].length);
        if (reg[2] == "i") retour = reg[3];
        else if (reg[4] == "s") retour = reg[5];
        else retour = -1;
    }
    else retour = -1;
    return retour;
}

PhpArray2Js.prototype.extraireValeur = function()
{
    // extrait une valeur au début de this.php
    // cette valeur est de type "a:\d+:{" ou "N" ou "b:[01]" ou "i:\d+" ou "i:[\d\.]+" ou "s:\d+:\"\w+\""
    // on tente de détecter une valeur en tête de texte
    var retour;
    var reg = this.php.match(/^((N);|(b):([01]);|(i):(\d+);|(d):([\d\.]+);|(s):\d+:"([^"]*)";|(a):(\d+):\{)/);
    if (reg != -1)
    {
        // on coupe le texte de la valeur détectée
        this.php = this.php.substring(reg[0].length);
        // retour est un tableau contenant le type et la valeur de la donnée détectée dans la chaîne
        if (reg[2] == "N") retour = new Array("N", null); // valeur nulle
        else if (reg[3] == "b") retour = new Array("b", reg[4]); // booléen (true/false)
        else if (reg[5] == "i")  retour = new Array("i", reg[6]); // entier
        else if (reg[7] == "d")  retour = new Array("d", reg[8]); // entier double ou flottant
        else if (reg[9] == "s") retour = new Array("s", remplacerQuotes(reg[10])); // chaîne de caractères
        else if (reg[11] == "a") retour = new Array("a", reg[12]); // sous-tableau
        else retour = -1;
    }
    else retour = -1;
    return retour;
}

function corrigerChainePHP(chaine)
{
    // remplace les " en " uniquement autour des chaînes de caractères
    chaine = chaine.replace(/:"/g, ':"');
    chaine = chaine.replace(/";/g, '";');
    return chaine;
}

function remplacerQuotes(chaine)
{
    // remplace les " à l'intérieur des chaînes de caractères
    return chaine.replace(/"/g, '\"');
}
PhpArray2Js.prototype.var_dump = function() {
    // affiche le tableau
    return var_dump(this.tabjs);
}

function var_dump(tab)
{
    // fonction analogue à var_dump en PHP, mais plus simple
    var indent = (arguments.length == 2) ? arguments[1] + "\t" : "\t";
    var i = 0;
    var elements = "";
    for (var elt in tab)
    {
        elements += (i ? ",\n " : " ") + indent + "[" + elt + "]:";
        switch (typeof tab[elt])
        {
            case "string" :
            elements += "\"" + tab[elt] + "\""; break;
            case "number" :
            elements += tab[elt]; break;
            case "object" :
            if (tab[elt] == null) elements += "*null*";
            else if (tab[elt]) elements += var_dump(tab[elt], indent); break;
            case "undefined" :
            elements += "*undefined*"; break;
            default : elements += tab[elt];
        }
        i++;
    }
    return "tableau(" + i + "){\n" + elements + "\n" + (arguments[1] ? arguments[1] : "") + "}";
}
Je n'ai rien modifié si ce n'est la présentation du code pour une lecture plus facile à mon goût. Je n'ai rajouté ni enlevé aucun des commentaires. Je vous invite pour plus de détails à aller sur le site cité plus haut.

Second fichier beaucoup plus succinct nommé changeDept.js, la fonction qui crée la seconde liste déroulante:
/* On crée la fonction qui va construire la seconde liste déroulante */
function changeDept(tab,idr)
{
    if(idr != "vide")
    {
    /* On compte les départements de cette région */
    var nbd = tab[idr][1].length;
    var form_d  = '<select name="departement" id="departement">';
    for(var j = 0;  j < nbd; j++)
    {
        form_d += '  <option value="'+ tab[idr][1][j] +'">'+ tab[idr][2][j] +" ("+ tab[idr][1][j] +')<\/option>';
    }
    form_d += '<\/select>';
    }
    else
    {
        form_d = "";
    }
    document.getElementById("blocDepartements").innerHTML = form_d;
}
Comme vous voyez, rien de bien extraordinaire. Notez toutefois qu'on utilise pas la fonction JavaScript document.write() mais innerHTML qui va insérer le contenu construit dans la fonction entre deux balises identifiées par un attribut "id".

Enfin, la page elle même:
<?php
echo("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
/* Variables de connexion : ajustez ces paramètres selon votre propre environnement */
$serveur = "localhost";
$admin   = "root";
$mdp     = "";
$base    = "regions";
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" xml:lang="fr" />
<title>Liste déroulantes dynamiques liées</title>
<meta name="description" content="Listes dynamiques liées: la seconde liste est modifiée instantanément lors d'une sélection sur la première." />
<meta name="keywords" content="menu,déroulant,select,liées,JavaScript" />
<meta name="author" content="Cyrano" />
<meta name="generator" content="Zend Studio Environnement et WebExpert 5" />
<meta http-equiv="imagetoolbar" content="no" />
<meta http-equiv="Pragma" content="no-cache" />
<script type="text/javascript" src="./arrayPHP2JS.js" charset="iso_8859-1"></script>
<script type="text/javascript" src="./changeDept.js" charset="iso_8859-1"></script>
<?php
/* Requête SQL de récupération des données */
$sql = "SELECT id_departement AS idd, departement AS dept, region.id_region AS idr, region ".
"FROM departement, region ".
"WHERE departement.id_region = region.id_region ".
"ORDER BY region.id_region;";
/* Connexion et exécution de la requête */
$connexion = mysql_pconnect($serveur, $admin, $mdp);
if($connexion != false)
{
    $choixbase = mysql_select_db($base, $connexion);
    $recherche = mysql_query($sql, $connexion);
    /* Pour ne pas écraser mes tableaux, je crée un témoin */
    $temoin_r = 0;
    /* Création du tableau PHP des valeurs récupérées */
    $regions = array();
    /* Index du département par tableau régional */
    $id = 0;
    while($ligne = mysql_fetch_assoc($recherche))
    {
        $r = $ligne['idr'];
        $d = $ligne['idd'];
        /* Je vérifie si je suis toujours dans la même région, sinon je crée les tableaux nécessaires */
        if($temoin_r != $r)
        {
            $regions[$r] = array();
            /* J'ajoute laa région */
            $regions[$r][0] = $ligne['region'];
            $regions[$r][1] = array();
            $regions[$r][2] = array();
            $temoin_r = $r;
            $id = 0;
        }
        /* J'ajoute les départements */
        $regions[$r][1][$id] = $d;
        $regions[$r][2][$id] = $ligne['dept'];
        $id++;
    }
    /* On sérialise le tableau obtenu pour traitement par JavaScript */
    $chaine = htmlspecialchars(serialize($regions), ENT_QUOTES);
?>
<script type="text/javascript">
/* <![CDATA[ */
<!--
/*
* Ici, on transmets la chaîne sérialisée à JavaScript 
* pour la transformer en tableau indexé JavaScript 
*/
var tableau = new PhpArray2Js('<?php echo $chaine; ?>');
var tab = tableau.retour();
// -->
/* ]]> */
</script>
</head>
<body style="font-family: verdana, helvetica, sans-serif; font-size: 85%">
<h3>Version Utilisant JavaScript</h3>
<p>Vous constaterez que le délai de latence entre la sélection et la mise à jour est quasiment inexistant.</p>
<?php
if(isset($_POST['ok']) && isset($_POST['departement']) && $_POST['departement'] != "")
{
    $region_selectionnee = $_POST['region'];
    $dept_selectionne = $_POST['departement'];
?>
<p>Vous avez sélectionné le département <?php echo($dept_selectionne); ?> dans la région <?php echo($region_selectionnee); ?></p>
<?php
}
?>
<form action="<?php echo($_SERVER['PHP_SELF']); ?>" method="post" id="chgdept">
  <fieldset style="border: 3px double #333399">
  <legend>Sélectionnez une région</legend>
    <select name="region" id="region" onchange="changeDept(tab,this.value);">
      <option value="vide">- - - Choisissez une région - - -</option>
    <?php
    /* Construction de la première liste : on se sert du tableau PHP */
    $nbr = count($regions);
    foreach($regions as $nr => $nom)
    {
        ?>
    <option value="<?php echo($nr); ?>"><?php echo($nom[0]); ?></option>
<?php
    }
    ?>
    </select>
    <!-- ICI, le secret : on met un bloc avec un id ou va s'insérer le code de
         la seconde liste déroulande -->
  <span id="blocDepartements"></span><br />
  <input type="submit" name="ok" id="ok" value="Envoyer" />
  </fieldset>
</form>
<?php
    
}
else
{
    /*  Si vous arrivez ici, vous avez un gros problème avec la connexion au serveur de base de données */
?>
</head>
<body>
<p>La connexion au serveur de base de données a échoué. Aucun élément ne peut être affiché.</p>
<?php
}
?>
<p><a href="./index.php" title="Aller vers la version 100% PHP">Aller vers la version 100% PHP</a></p>
</body>
</html>
Démonstration ICI

Téléchargez les fichiers (index2.php + fichiers JavaScript + region.sql) zippés ICI

Et voilà, décortiquez, apprenez, modifiez selon vos propres besoins.
Amusez vous :)
Modifié en dernier par Cyrano le 24 août 2005, 21:26, modifié 5 fois.
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

Mammouth du PHP | 19664 Messages

17 déc. 2006, 23:09

Solution alternative : AJAX

La première ou la seconde solution ont toutes les deux des avantages, mais elles ont aussi un inconvénient majeur :
Avec la version PHP, on est handicapés par le délai de rechargement de la page complète.
Avec la version JavaScript, on est limité à cause de la taille des listes, c'est visible lorsqu'on génère le tableau sérialisé : ce tableau a une taille importante. Que faire si on doit utiliser des listes encore plus grandes et si éventuellement on veut ajouter une troisième voire une quatrième liste ?
La solution passe alors par un objet XHR qui va mettre à jour uniquement la partie du formulaire nécessaire.
Nous aurons besoin de deux scripts : un en JavaScript et un en PHP.

Commençons par le fichier PHP. Il s'agit de recevoir en paramètre la région sélectionnée, d'effectuer une requête et de retourner la liste des départements correspondant. Voici ce code de departements.php :
<?php
/**
 * Code qui sera aeeplé par un objet XHR et qui
 * retournera la liste déroulante des départements
 * correspondant à la région sélectionnée.
 */
/* Paramètres de connexion */
$serveur = "localhost";
$admin   = "root";
$mdp     = "";
$base    = "regions";

/* On récupère l'identifiant de la région choisie. */
$idr = isset($_GET['idr']) ? $_GET['idr'] : false;
/* Si on a une région, on procède à la requête */
if(false !== $idr)
{
    /* Cération de la requête pour avoir les départements de cette région */
    $sql2 = "SELECT `id_departement`, `departement`".
            " FROM `departement`".
            " WHERE `id_region` = ". $idr ."".
            " ORDER BY `id_departement`;";
    $connexion = mysql_connect($serveur, $admin, $mdp);
    mysql_select_db($base, $connexion);
    $rech_dept = mysql_query($sql2, $connexion);
    /* Un petit compteur pour les départements */
    $nd = 0;
    /* On crée deux tableaux pour les numéros et les noms des départements */
    $code_dept = array();
    $nom_dept = array();
    /* On va mettre les numéros et noms des départements dans les deux tableaux */
    while(false != ($ligne_dept = mysql_fetch_assoc($rech_dept)))
    {
        $code_dept[] = $ligne_dept['id_departement'];
        $nom_dept[]  = $ligne_dept['departement'];
        $nd++;
    }
    /* Maintenant on peut construire la liste déroulante */
    $liste = "";
    $liste .= '<select name="departement" id="departement">'."\n";
    for($d = 0; $d < $nd; $d++)
    {
        $liste .= '  <option value="'. $code_dept[$d] .'">'. htmlentities($nom_dept[$d]) .' ('. $code_dept[$d] .')</option>'."\n";
    }
    $liste .= '</select>'."\n";
    /* Un petit coup de balai */
    mysql_free_result($rech_dept);
    /* Affichage de la liste déroulante */
    echo($liste);
}
/* Sinon on retourne un message d'erreur */
else
{
    echo("<p>Une erreur s'est produite. La région sélectionnée comporte une donnée invalide.</p>\n");
}
?>
On peut voir ce que retourne ce script : la liste complète ou un message d'erreur. Notre manipulation est prête coté serveur, passons coté client.
Le script JavaScript que nous nommerons dept_xhr.js. Il sert à créer un objet XHR qui va communiquer avec le script PHP vu plus haut:
/**
 * Lister les départements d'une région avec un objet
 * XMLHTTPRequest.
 */
/* Création de la variable globale qui contiendra l'objet XHR */
var requete = null;
/**
 * Fonction privée qui va créer un objet XHR.
 * Cette fonction initialisera la valeur dans la variable globale définie
 * ci-dessus.
 */
function creerRequete()
{
    try
    {
        /* On tente de créer un objet XmlHTTPRequest */
        requete = new XMLHttpRequest();
    }
    catch (microsoft)
    {
        /* Microsoft utilisant une autre technique, on essays de créer un objet ActiveX */
        try
        {
            requete = new ActiveXObject('Msxml2.XMLHTTP');
        }
        catch(autremicrosoft)
        {
            /* La première méthode a échoué, on en teste une seconde */
            try
            {
                requete = new ActiveXObject('Microsoft.XMLHTTP');
            }
            catch(echec)
            {
                /* À ce stade, aucune méthode ne fonctionne... mettez donc votre navigateur à jour ;) */
                requete = null;
            }
        }
    }
    if(requete == null)
    {
        alert('Impossible de créer l\'objet requête,\nVotre navigateur ne semble pas supporter les object XMLHttpRequest.');
    }
}
/**
 * Fonction privée qui va mettre à jour l'affichage de la page.
 */
function actualiserDepartements()
{
    var listeDept = requete.responseText;
    var blocListe = document.getElementById('blocDepartements');
    blocListe.innerHTML = listeDept;
}

/**
 * Fonction publique appelée par la page affichée.
 * Cette fonction va initialiser la création de l'objet XHR puis appeler
 * le code serveur afin de récupérer les données à modifier dans la page.
 */
function getDepartements(idr)
{
    /* Si il n'y a pas d'identifiant de région, on fait disparaître la seconde liste au cas où elle serait affichée */
    if(idr == 'vide')
    {
        document.getElementById('blocDepartements').innerHTML = '';
    }
    else
    {
        /* À cet endroit précis, on peut faire apparaître un message d'attente */
        var blocListe = document.getElementById('blocDepartements');
        blocListe.innerHTML = "Traitement en cours, veuillez patienter...";
        /* On crée l'objet XHR */
        creerRequete();
        /* Définition du fichier de traitement */
        var url = 'departements.php?idr='+ idr;
        /* Envoi de la requête à la page de traitement */
        requete.open('GET', url, true);
        /* On surveille le changement d'état de la requête qui va passer successivement de 1 à 4 */
        requete.onreadystatechange = function()
        {
            /* Lorsque l'état est à 4 */
            if(requete.readyState == 4)
            {
                /* Si on a un statut à 200 */
                if(requete.status == 200)
                {
                    /* Mise à jour de l'affichage, on appelle la fonction apropriée */
                    actualiserDepartements();
                }
            }
        };
        requete.send(null);
    }
}
La page maintenant, peu de changement par rapport aux versions précédentes. Ce qui change, c'est l'appel de la fonction JavaScript getDepartements() dans un évènement onchange de la première liste :
<?php
echo("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
/* Variables de connexion : ajustez ces paramètres selon votre propre environnement */
$serveur = "localhost";
$admin   = "root";
$mdp     = "";
$base    = "regions";
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" xml:lang="fr" />
<title>Liste déroulantes dynamiques liées</title>
<meta name="description" content="Listes dynamiques liées: la seconde liste est modifiée via un objet XHR lors d'une sélection sur la première." />
<meta name="keywords" content="menu,déroulant,select,liées,JavaScript" />
<meta name="author" content="Cyrano" />
<meta name="generator" content="Zend Studio Environnement et WebExpert 5" />
<meta http-equiv="imagetoolbar" content="no" />
<meta http-equiv="Pragma" content="no-cache" />
<script type="text/javascript" src="./dept_xhr.js" charset="iso_8859-1"></script>
<?php
/* Requête SQL de récupération des données de la première liste */
$sql = "SELECT `id_region` AS idr, `region` ".
       "FROM `region` ".
       "ORDER BY `id_region`;";
/* Connexion et exécution de la requête */
$connexion = mysql_connect($serveur, $admin, $mdp);
if($connexion != false)
{
    $choixbase = mysql_select_db($base, $connexion);
    $recherche = mysql_query($sql, $connexion);
    /* Création du tableau PHP des valeurs récupérées */
    $regions = array();
    /* Index du département par tableau régional */
    $id = 0;
    while($ligne = mysql_fetch_assoc($recherche))
    {
        $regions[$ligne['idr']] = $ligne['region'];
    }
?>
</head>
<body style="font-family: verdana, helvetica, sans-serif; font-size: 85%">
<h3>Version Utilisant AJAX</h3>
<p>Vous constaterez que le délai de latence entre la sélection et la mise à jour est quasiment intermédiaire entre les versions 100% PHP et JavaScript.</p>
<h3>Trouver un département</h3>
<form action="<?php echo($_SERVER['PHP_SELF']); ?>" method="post" id="chgdept">
  <fieldset style="border: 3px double #333399">
  <legend>Sélectionnez une région</legend>
    <select name="region" id="region" onchange="getDepartements(this.value);">
      <option value="vide">- - - Choisissez une région - - -</option>
    <?php
    /* Construction de la première liste : on se sert du tableau PHP */
    foreach($regions as $nr => $nom)
    {
        ?>
    <option value="<?php echo($nr); ?>"><?php echo($nom); ?></option>
<?php
    }
    ?>
    </select>
    <!-- ICI, le secret : on met un bloc avec un id ou va s'insérer le code de
         la seconde liste déroulande -->
  <span id="blocDepartements"></span><br />
  <input type="submit" name="ok" id="ok" value="Envoyer" />
  </fieldset>
</form>
<p><a href="./index.php" title="Aller vers la version 100% PHP">Aller vers la version 100% PHP</a></p>
<p><a href="./index2.php" title="Aller vers la version JavaScript">Aller vers la version JavaScript</a></p>
<?php
}
else
{
    /*  Si vous arrivez ici, vous avez un gros problème avec la connexion au serveur de base de données */
?>
</head>
<body>
<p>La connexion au serveur de base de données a échoué. Aucun élément ne peut être affiché.</p>
<?php
}
?>
</body>
</html>

Démonstration ICI

Téléchargez les fichiers (index3.php + fichiers JavaScript + region.sql) zippés ICI
Codez en pensant que celui qui maintiendra votre code est un psychopathe qui connait votre adresse :axe:

3 messages   •   Page 1 sur 1