Chapter 6. Accéder à une base MySQL avec PHP

Table of Contents

Conseils pour le diagnostic d'erreur
Lecture et affichage HTML des données
Lecture des données
Afficher les données
Une autre façon de faire ...
Programme complet
Insertion de données dans la base
Saisie des données (formulaire)
Récupération des valeurs du formulaire HTML en PHP
Ecriture des données (INSERT)
Modification de données (UPDATE)
Gérer plusieurs tables
L'exemple simple d'un forum
Exemple avec trois tables

Examinons maintenant comment il est possible d'exploiter ces logiciels pour fournir aux visiteurs, à la fois un affichage des produits présents dans notre base de données, et un moyen pour le visiteur d'ajouter un nouveau produit à cette base. Nous commençons avec un programme PHP qui doit nous permettre de générer une page HTML, sur laquelle figure un tableau des produits avec leurs prix correspondants. Nous mettrons ce code dans le fichier lecture.php.

Conseils pour le diagnostic d'erreur

Du fait des langages multiples utilisés, il est parfois difficile d'identifier simplement la source d'un dysfonctionnement lors du développement des programmes.

Il est donc conseillé de manière générale, de ne pas faire l'économie des tests d'erreurs. Par exemple, la tentative de connexion à une base devrait prévoir l'échec de la connexion :

@mysql_connect("$hostname", "$username", "$password")
      or die("Impossible de se connecter a la base");

De même, pour toutes les requêtes MySQL, je conseille de définir une fonction comme celle-ci :

function teste_erreur($query) {
    $error = mysql_errno();
    $error_msg = mysql_error();
    if (!empty($error_msg)) {
        echo "L'erreur $error_msg s'est produite (code=$error)";
        echo "pour la requête $query\n";
    }
}

et de l'appeler systématiquement après une requête mysql_query($q) de façon à ne pas chercher trop longtemps la cause de l'erreur :

   $q = " ..... "
   mysql_query($q);
   teste_erreur($q);

Lecture et affichage HTML des données

Lecture des données

Supposons que nous ayons une base de données appelée base comportant une table appelée produit. Notre première tâche est de demander à PHP de se connecter à l'ordinateur sur lequel se trouve la base MySQL, puis de préciser qu'on veut utiliser cette table :

<?
mysql_connect("localhost","root","") or die("Impossible de se connecter");;
mysql_select_db("base") or die("pas possible de trouver la base");

Ici, nous spécifions que la base est sur l'ordinateur qui exécute le script PHP (localhost), et qu'on se connecte en tant qu'administrateur de la base (root), qui n'a pas de mot de passe.

[Note]Note

Si votre base MySQL à été paramétrée différement, changez bien sûr les paramètres (vérifier entre autres que vous n'avez pas besoin d'un mot de passe).

L'instruction die permet d'arrêter le script avec un message d'erreur en cas d'échec.

Demandons maintenant de lister toutes les informations de notre table produit. Il faut d'abord, à travers PHP, faire une requête SQL qui permette de selectionner tous les champs de la table (première ligne).

$result = mysql_query("SELECT * FROM produit");
$nblignes = mysql_num_rows($result);

Le résultat de cette requête est un tableau de données, comme les deux dernières lignes du tableau suivant :

refnomprix
1canon bj10e120
2epson stylus 8c135

Nous devons traiter les lignes du tableau les unes après les autres. Pour cela, nous avons besoin de connaître le nombre de ligne de notre tableau. C'est ce qui est fait à la deuxième ligne de l'extrait de code précédent, et le résultat est conservé dans nblignes. Nous sommes maintenant prêt à afficher nos données présentes dans result, dans le format HTML.

Afficher les données

Nous voulons présenter nos données sous la forme d'une table HTML. Il faut donc faire écrire à PHP, le code HTML correspondant.

La première ligne de la table est constante : il s'agit de l'entête. Ensuite, il faut extraire, pour chaque ligne de notre tableau, les valeurs à afficher. Pour cela, nous extrayons pour chaque ligne i les valeurs des champs nom et prix de notre table, avec la commande mysql_result(). Celle-ci prend en argument la table, le numéro de ligne, et le nom de colonne pour rendre la valeur correspondante dans la table. Nous mettons les valeurs nom et prix de la table respectivement dans les variables PHP $prod_nom et $prod_prix (le nom de ces variables PHP est tout à fait arbitraire et n'a pas de relation avec le nom des colonnes SQL). Quand les valeurs de la ligne ont été récupérées, nous pouvons écrire la ligne correspondante du tableau HTML. Enfin, nous fermons la table quand toutes les lignes ont été examinées.

 
    echo "<table><tr><td> Désignation </td><td> Prix </td></tr>\n";
    for ($i=0;$i<$nblignes;$i=$i+1) {
	 $prod_nom =  mysql_result($result,$i,"nom");
	 $prod_prix = mysql_result($result,$i,"prix");
	 echo "<tr><td>$prod_nom</td><td>$prod_prix</td></tr>";
    }
    echo "</table>";

Enfin, il nous faut par prudence refermer la base MySQL mais la quasi-totalité du code PHP nécessaire à la consultation de notre table est écrit.

Une autre façon de faire ...

Nous avons utilisé dans la solution précédente une boucle pour passer en revue chaque ligne de la table de résultat obtenue par la requête SELECT. Le nombre de lignes de la table est donné par mysql_num_rows() et chaque élément est extrait avec mysql_result().

Une alternative un peu plus concise consiste à utiliser mysql_fetch_array(). Cette fonction prend en argument la table de résultat obtenue par la requête ($result dans notre exemple), et renvoie un tableau associatif correspondant à une ligne de la table. L'indexation du tableau est faite avec les noms des colonnes de la table SQL. Par exemple, si on récupère un tableau $t ($t = mysql_fetch_array($result)), et qu'un des champs SQL a pour nom id, alors on peut accéder à la valeur de la ligne lue par $t['id']. La fonction gère un poineur interne: lors du premier appel, ce sont les éléments de la première ligne qui sont mis en tableau. Chaque nouvel appel fait progresser le pointeur pour permettre de lire la ligne suivante.

Un équivalent au bout de code précédent est:

 
      echo "<table><tr><td> Désignation </td><td> Prix </td></tr>\n";
      while ($ligne = mysql_fetch_array($result)) {
   		echo "<tr><td>" . $ligne['nom'] . "</td><td>". $ligne['prix']. "</td></tr>";
      }
      echo "</table>";

Programme complet

Récapitulons en listant maintenant le programme complet qui permet de lire le contenu de notre table. Donnez une extension php à votre nom de fichier (lecture.php par exemple).

<html>
<body>
<h1> Catalogue produit</h1>
<?
mysql_connect("localhost","root","") or die("Impossible de se connecter");;
mysql_select_db("base") or die("pas possible de trouver la base");

$result = mysql_query("SELECT * FROM produit");
$nblignes = mysql_numrows($result);
    
echo "<table><tr><td>Désignation</td><td>Prix</td></tr>\n";
for ($i=0;$i<$nblignes;$i=$i+1) {
	 $prod_nom =  mysql_result($result,$i,"nom");
	 $prod_prix = mysql_result($result,$i,"prix");
	 echo "<tr><td>$prod_nom</td><td>$prod_prix</td></tr>";
}
echo "</table>";
mysql_close();
?>
</body>
</html>

Insertion de données dans la base

Pour la mise à jour de la base nous avons besoin de deux fichiers : un premier fichier qui doit contenr un formulaire HTML dans lequel l'internaute peut saisir les données à insérer dans la base, et un deuxième fichier (PHP) qui permette de récupérer les données du formulaire pour les insérer dans la base.

Saisie des données (formulaire)

Ici, le fichier peut être un simple fichier HTML, puisque toutes les éléments de la page sont statiques. Nous l'appelons saisie.html. Notez que la mise en page HTML est des plus sommaires, ceci afin d'améliorer la lisibilité et se concentrer sur les éléments importants du code.

<html>
<body>
<h1>Mise à jour de la base</h1>
<form method="post" action="maj.php">
Référence : <input type="text" name="ref"><br>
Nom       : <input type="text" name="nom"><br>
Prix      : <input type="text" name="prix"><br>
            <input type=submit value="Insérer">
</form>
</body>
</html>

Il est important de remarquer la partie action du formulaire. C'est ici qu'on précise quel programme doit récupérer les données du formulaire et les traiter. Dans notre cas, il s'agit du fichier maj.php, qui sera chargé lorsque le bouton de type submit sera pressé.

Rappelons que toute cette section ne présente que des éléments connus d'un programmeur HTML. Rien de nouveau !

Récupération des valeurs du formulaire HTML en PHP

Nous voulons mainteant récupérer les données du formulaires pour les insérer dans la base. Avant de l'écrire, il est d'abord nécessaire de savoir comment sont transmises les données du formulaire à notre nouveau fichier (celui désigné par l'attribut action dans le formulaire précédent), et comment on peut récupérer ces valeurs en PHP.

Techniquement, le formulaire HTML est posté lorsque l'utilisateur appuie sur le bouton de type submit et que la balise form spécifie method="post". Ceci veut dire qu'une requête HTTP de type POST est émise vers le serveur, et cette requête contient les valeurs du formulaire. La forme sous laquelle sont transmises les valeurs est une suite de couples (variable = valeur).

PHP récupère ces valeurs postées dans un tableau qu'il nomme $_POST. C'est un tableau associatif, dont les index de tableaux sont les noms des variables (voir le paragraphe sur les tableaux associatifs en PHP). Il est donc facile de retrouver la valeur associée à une variable. Par exemple, $_POST['prix'] désigne la valeur associée au champ HTML prix dans le formulaire posté.

[Tip]Tip

Dans les anciennes configurations de PHP (avant PHP 4.2.0), il n'était pas nécessaire de récupérer explicitement les variables postées. PHP créait automatiquemlent des variables PHP de même nom que les champs déclarés dans le forumaire HTML. Ainsi, on récupère la valeur du champ <input type="text" name="prix"> du formulaire dans une variable $prix. Ce mode aujourd'hui désactivé pour des raisons de sécurité, peut être rétabli en écrivant register_globals = On dans le fichier php.ini.

Ecriture des données (INSERT)

Maintenant que nous savons récupérer les valeurs des variables du formulaire, il faut les sauvegarder dans la base de données.

Nous écrivons un deuxième fichier, que nous appelons maj.php. Voyons le listing de notre fichier. On retrouve les instructions de connexion à la base, comme dans the section called “Lecture des données”. Sur le principe, seule la requête SQL change, avec un INSERT au lieu d'un select.

<html>
<body>
<?
// connexion à la base
mysql_connect("localhost","root","") or die ("Impossible de se connecter");
mysql_select_db("base") or die("Impossible de trouver la base");           

// recuperation des valeurs du formulaire
$nom = $_POST['nom'];
$prix= $_POST['prix'];

// insertion des valeurs dans la base
$query = "INSERT INTO produit (nom,prix) VALUES ('$nom','$prix')";
$result=mysql_query($query);
mysql_close();
?>
Mise a jour faite
</body>
</html>

Ici, on suppose toujours que ref est une clé primaire de la table produit, et qu'elle possède en plus la propriété autoincrement. Ainsi, nous n'avons pas à gérer l'unicité de la référence, puisque MySQL inserera automatiquement une référence entière supérieure aux autres références trouvées.

Modification de données (UPDATE)

La modification de données (on dit encore la mise à jour, ou le remplacement de valeur) peut porter sur une ou plusieurs lignes, d'une ou plusieurs tables. La modification en SQL se dit UPDATE. On indique les noms des colonnes concernées et les valeurs à y mettre (SET col1=valeur1, col2=valeur2, ...) et à quelles lignes cela s'applique (WHERE condition).

Exemple: le nom d'un domaine informatique change de 'fly.com' à 'fly.org'. Je veux mettre à jour la colonne 'nondomaine' de ma table 'adresses' en conséquence. La commande suivante modifiera toutes les lignes qui ont 'fly.com'.

	  UPDATE adresses
	  SET nomdomaine='fly.org'
	  WHERE nomdomaine='fly.com'

On peut aussi modifier plusieurs colonnes simultanément. Par exemple, modifier dans la table 'adresses' l'email et le pseudo de l'utilisateur numéro 3 (uid=3) se dit:

        UPDATE adresses
	  SET email='newemail@fly.org', pseudo='newpseudo' 
        WHERE uid=3

MySQL introduit une extension (ne faisant pas partie du standard SQL) permettant de modifier toute une ligne: REPLACE.

   REPLACE INTO  table 
           (champ1,champ2,...) 
    WHERE  (conditions)
    VALUES (val1,val2,...);

qui remplace les valeurs présentes dans champ1 par val1, champ2 par val2, etc.

Cette commande est en fait une extension de la comande INSERT. Dans le cas ou l'un des champ est clé primaire de la table, et que la valeur précisée existe déjà dans la table, alors cette ligne sera remplacée. Si aucun enregistrement avec une telle valeur de clé n'existe, alors la ligne sera insérée. Ainsi, cette commande peut être utilisée à des fins d'insertion ou de remplacement.

Reprenons notre exemple précédent. On veut modifier le prix d'un article dont on connait la référence, stockée dans la variable $ref. La référence est la clé primaire (colonne ref) de notre table 'lecteurs'. Le remplacement va être opéré si la valeur présente dans $ref existe déjà dans la table.

$query = "REPLACE INTO produit (ref,marque,modele,annee,prix) 
          VALUES ('$ref','$marque','$modele','$annee','$nouveau_prix')";
$ok = mysql_query( $query );
if ($ok) 
   echo "Le prix de l'article $ref est modifié ..."; 

Gérer plusieurs tables

Nous n'avons jusqu'ici considéré que la base de données n'était constituée que d'une table. Dans la réalité, la base de données est toujours constituée de plusieurs tables, et il est donc naturel d'aller chercher, pour un élément d'une table, des informations liées présentes dans d'autres tables.

Faire une sélection impliquant plusieurs tables implique un mécanisme plus difficile à comprendre. Une opération sur plusieurs table implique en général de travailler sur le produit cartésien des tables. Une opération fondamentale des bases de données relationnelle est la jointure qui indique comment deux tables doivent être mises en correspondance, et représente donc un sous-ensemeble du produit cartésien.

Une bonne compréhension de ces mécanismes peut être acquise dans des ouvrages traitant spécifiquement des bases de données relationnelles et de SQL. Cependant, l'exemple simple suivant permet de schématiser ce processus.

L'exemple simple d'un forum

Dans un forum de discussions, on pourrait avoir par exemple les deux tables suivantes: une table recensant les messages postés avec pour chaque message, le numéro d'utilisateur l'ayant posté. Dans une table profils, on retrouverait toutes les informations sur cet utilisateur.

posts    (numpost,user,texte)    
profils  (user,pseudo,email)

Un exemple simplifié de données pour de telles tables pourrait être:

numpostusertexte
11Hello, does anybody knows what is SQL?
21Hello again, anyone listening?
32Hello, you'd better buy a book and read it.

et

userpseudoemail
1newbie555newbie@hotmail.com
2jobasherjo.basher@gmail.com

Pour l'affichage des messages, on veut bien sûr pouvoir associer au message le pseudo ou l'email de l'utilisateur ayant posté le message, ce qui demande d'aller trouver, à partir de numpost le pseudo correspondant. Par exemple, le post 3 (numpost=3) à été posté par un utilisateur de pseudo jobasher.

On peut illustrer sur cet exemple simple, la requête SQL qui va nous permettre d'obtenir le résultat. Etant donné un numéro de post (disons 3), on veut le pseudo associé tiré de la table profils:

select pseudo
  from posts,profils
where
   numpost = 3 and 
   posts.user = profils.user

[Tip]Tip

En SQL, un même nom de champ (de colonne) peut être utilisé dans plusieurs tables. Il devient donc ambigu quand ces tables sont utilisées en même temps. Pour lever l'ambiguité, il suffit de préfixer le nom du champ par le nom de sa table. Ici par exemple, on écrit profils.user pour préciser de quel champ user il s'agit.

Pour comprendre cette jointure, on peut la représenter par le produit cartésien des deux tables, sur lequel on va imposer des restrictions. Le produit cartésien des deux tables (développant toutes les combinaisons de colonnes des deux tables) donnerait:

numpostusertexteuserpseudoemail
11Hello, does anybody knows what is SQL?1newbie555newbie@hotmail.com
11Hello, does anybody knows what is SQL?2jobasherjo.basher@gmail.com
21Hello again, anyone listening?1newbie555newbie@hotmail.com
21Hello again, anyone listening?2jobasherjo.basher@gmail.com
32Hello, you'd better buy a book and read it.1newbie555newbie@hotmail.com
32Hello, you'd better buy a book and read it.2jobasherjo.basher@gmail.com

La jointure contraint ce résultat en spécifiant l'égalité des valeurs de deux des colonnes. Ainsi, la seule ligne qui satisfait aux contraintes de notre requête est la dernière du tableau: numpost=3 et post.user ainsi que profils.user sont égaux (2). Le résultat à notre requête est donc la table:

pseudo
jobasher

Exemple avec trois tables

Prenons maintenant un autre exemple. Imaginons que notre base est faite de trois tables : les tables clients, produits et commandes.

clients (clid,nom)                          clé : clid
produits(pid,descr,prix)                    clé : pid
commandes(pid,clid,quantite,montant,date)   clé : pid,clid

Pour lister les clients qui ont passé des commandes de plus de 2000 Euros et afficher ce qu'ils ont commandé, on peut écrire la requête SQL suivante :

select client.nom,produits.descr 
  from produits,commandes,clients
where
   commandes.montant >  2000  and
   commandes.pid=produits.pid and
   commandes.clid=clients.clid

Pour se persuader que la commande select précédente remplit bien notre objectif, paraphrasons la. Nous obtiendrons au final une table composée des colonnes client.nom et produits.descr. Dans la table commandes seules les lignes dont le montant dépassent 2000 sont sélectionnées. A chacune de ces lignes correspond un numéro de client (commandes.clid). Ne seront sélectionnées dans les autres tables que les lignes dont le numéro de client est égal à celui-ci.