Ce cours est visible gratuitement en ligne.

Ce cours est en vidéo.

Ce cours existe en livre papier.

Ce cours existe en eBook.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !
Concevez votre site web avec PHP et MySQL

Concevez votre site web avec PHP et MySQL

Mis à jour le lundi 16 juin 2014
  • 8 semaines
  • Facile

Dans ce chapitre, nous retournons à nos pages PHP. À partir de maintenant, nous allons apprendre à communiquer avec une base de données via PHP. Ce sera l'occasion de découvrir le langage SQL, que nous étudierons tout au long des prochains chapitres.

Ici, nous allons nous entraîner à lire des données dans une table. Il est vivement conseillé d'avoir un peu manipulé phpMyAdmin au préalable : cet outil vous permettra de vérifier si les manipulations que vous faites en PHP ont bien l'impact que vous attendiez dans votre base de données.

Se connecter à la base de données en PHP

Pour pouvoir travailler avec la base de données en PHP, il faut d'abord s'y connecter.

Nous allons apprendre dans ce chapitre à lire des données dans une BDD (base de données). Or, je vous rappelle que PHP doit faire l'intermédiaire entre vous et MySQL. Problème : PHP ne peut pas dire à MySQL dès le début « Récupère-moi ces valeurs ». En effet, MySQL demande d'abord un nom d'utilisateur et un mot de passe. S'il ne le faisait pas, tout le monde pourrait accéder à votre BDD et lire les informations (parfois confidentielles !) qu'elle contient.

Il va donc falloir que PHP s'authentifie : on dit qu'il établit une connexion avec MySQL. Une fois que la connexion sera établie, vous pourrez faire toutes les opérations que vous voudrez sur votre base de données !

Comment se connecte-t-on à la base de données en PHP ?

Bonne question ! En effet, PHP propose plusieurs moyens de se connecter à une base de données MySQL.

  • L'extension mysql_ : ce sont des fonctions qui permettent d'accéder à une base de données MySQL et donc de communiquer avec MySQL. Leur nom commence toujours par mysql_. Toutefois, ces fonctions sont vieilles et on recommande de ne plus les utiliser aujourd'hui.

  • L'extension mysqli_ : ce sont des fonctions améliorées d'accès à MySQL. Elles proposent plus de fonctionnalités et sont plus à jour.

  • L'extension PDO : c'est un outil complet qui permet d'accéder à n'importe quel type de base de données. On peut donc l'utiliser pour se connecter aussi bien à MySQL que PostgreSQL ou Oracle.

Ce sont toutes des extensions car PHP est très modulaire. On peut très facilement ajouter ou supprimer des éléments à PHP, car tout le monde n'a pas forcément besoin de toutes les fonctionnalités.

Quel moyen choisir parmi tous ceux-là ?

Vous l'aurez compris, les fonctions mysql_ ne sont plus à utiliser (on dit qu'elles sont « obsolètes »). Il reste à choisir entre mysqli_ et PDO. Nous allons ici utiliser PDO car c'est cette méthode d'accès aux bases de données qui va devenir la plus utilisée dans les prochaines versions de PHP. D'autre part, le gros avantage de PDO est que vous pouvez l'utiliser de la même manière pour vous connecter à n'importe quel autre type de base de données (PostgreSQL, Oracle…) (figure suivante).

PDO permet de se connecter à n'importe quel type de base de données
PDO permet de se connecter à n'importe quel type de base de données

Vous pourrez donc réutiliser ce que vous allez apprendre si vous choisissez d'utiliser une autre base de données que MySQL.

Activer PDO

Normalement, PDO est activé par défaut. Pour le vérifier (voir la figure suivante), faites un clic gauche sur l'icône de WAMP dans la barre des tâches, puis allez dans le menu PHP / Extensions PHP et vérifiez que php_pdo_mysql est bien coché.

Vérifiez que l'extension PDO est activée
Vérifiez que l'extension PDO est activée

Et si je n'utilise pas WAMP ?

Vous pouvez ouvrir le fichier de configuration de PHP (qui s'appelle généralement php.ini) et rechercher la ligne qui contient php_pdo_mysql (à la ligne 3 dans l'exemple ci-dessous). Enlevez le point-virgule devant s'il y en a un pour activer l'extension :

;extension=php_pdo_firebird.dll
;extension=php_pdo_mssql.dll
extension=php_pdo_mysql.dll
;extension=php_pdo_oci.dll
;extension=php_pdo_odbc.dll

Si vous êtes sous Linux et que vous utilisez XAMPP, recherchez la ligne qui commence par pdo_mysql.default_socket et complétez-la comme ceci :

pdo_mysql.default_socket = /opt/lampp/var/mysql/mysql.sock

Enregistrez le fichier puis redémarrez PHP. Il suffit pour cela de relancer votre logiciel favori (WAMP, MAMP, XAMPP…).

Se connecter à MySQL avec PDO

Maintenant que nous sommes certains que PDO est activé, nous pouvons nous connecter à MySQL. Nous allons avoir besoin de quatre renseignements :

  • le nom de l'hôte : c'est l'adresse de l'ordinateur où MySQL est installé (comme une adresse IP). Le plus souvent, MySQL est installé sur le même ordinateur que PHP : dans ce cas, mettez la valeur localhost (cela signifie « sur le même ordinateur »). Néanmoins, il est possible que votre hébergeur web vous indique une autre valeur à renseigner (qui ressemblerait à ceci : sql.hebergeur.com). Dans ce cas, il faudra modifier cette valeur lorsque vous enverrez votre site sur le Web ;

  • la base : c'est le nom de la base de données à laquelle vous voulez vous connecter. Dans notre cas, la base s'appelle test. Nous l'avons créée avec phpMyAdmin dans le chapitre précédent ;

  • le login : il permet de vous identifier. Renseignez-vous auprès de votre hébergeur pour le connaître. Le plus souvent (chez un hébergeur gratuit), c'est le même login que vous utilisez pour le FTP ;

  • le mot de passe : il y a des chances pour que le mot de passe soit le même que celui que vous utilisez pour accéder au FTP. Renseignez-vous auprès de votre hébergeur.

Pour l'instant, nous faisons des tests sur notre ordinateur à la maison. On dit qu'on travaille « en local ». Par conséquent, le nom de l'hôte sera localhost.
Quant au login et au mot de passe, par défaut le login est root et il n'y a pas de mot de passe.

Voici donc comment on doit faire pour se connecter à MySQL via PDO sur la base test :

<?php
$bdd = new PDO('mysql:host=localhost;dbname=test', 'root', '');
?>

Je ne comprends rien à ce code, c'est normal ?

Oui, il faut reconnaître qu'il contient quelques nouveautés. En effet, PDO est ce qu'on appelle une extension orientée objet. C'est une façon de programmer un peu différente des fonctions classiques que l'on a appris à utiliser jusqu'ici.

La ligne de code qu'on vient de voir crée ce qu'on appelle un objet$bdd. Ce n'est pas vraiment une variable (même si ça y ressemble fortement) : c'est un objet qui représente la connexion à la base de données. On crée la connexion en indiquant dans l'ordre dans les paramètres :

  • le nom d'hôte (localhost) ;

  • la base de données (test) ;

  • le login (root) ;

  • le mot de passe (ici il n'y a pas de mot de passe, j'ai donc mis une chaîne vide).

Lorsque votre site sera en ligne, vous aurez sûrement un nom d'hôte différent ainsi qu'un login et un mot de passe comme ceci :

<?php
$bdd = new PDO('mysql:host=sql.hebergeur.com;dbname=mabase', 'pierre.durand', 's3cr3t');
?>

Il faudra donc penser à changer cette ligne pour l'adapter à votre hébergeur en modifiant les informations en conséquence lorsque vous enverrez votre site sur le web.

Tester la présence d'erreurs

Si vous avez renseigné les bonnes informations (nom de l'hôte, de la base, le login et le mot de passe), rien ne devrait s'afficher à l'écran. Toutefois, s'il y a une erreur (vous vous êtes trompés de mot de passe ou de nom de base de données, par exemple), PHP risque d'afficher toute la ligne qui pose l'erreur, ce qui inclut le mot de passe !

Vous ne voudrez pas que vos visiteurs puissent voir le mot de passe si une erreur survient lorsque votre site est en ligne. Il est préférable de traiter l'erreur. En cas d'erreur, PDO renvoie ce qu'on appelle une exception qui permet de « capturer » l'erreur. Voici comment je vous propose de faire :

<?php
try
{
	$bdd = new PDO('mysql:host=localhost;dbname=test', 'root', '');
}
catch (Exception $e)
{
        die('Erreur : ' . $e->getMessage());
}
?>

Voilà encore un code un peu nouveau pour nous. Là encore, sans trop rentrer dans le détail, il faut savoir que PHP essaie d'exécuter les instructions à l'intérieur du bloc try. S'il y a une erreur, il rentre dans le bloc catch et fait ce qu'on lui demande (ici, on arrête l'exécution de la page en affichant un message décrivant l'erreur).

Si au contraire tout se passe bien, PHP poursuit l'exécution du code et ne lit pas ce qu'il y a dans le bloc catch. Votre page PHP ne devrait donc rien afficher pour le moment.

Ouh là ! Tout ça semble bien compliqué, je n'y comprends pas grand-chose ! C'est grave, docteur ?

Non, pas du tout ! En fait, et j'insiste là-dessus, PDO nous fait utiliser des fonctionnalités de PHP que l'on n'a pas étudiées jusqu'à présent (programmation orientée objet, exceptions…). Contentez-vous pour le moment de réutiliser les codes que je vous propose et n'ayez crainte : nous reviendrons sur ces codes-là plus tard pour les expliquer en détail.

Si vous avez une page blanche, vous pouvez continuer. Si vous avez une erreur, lisez le message et essayez de comprendre ce qu'il signifie. Si vous êtes bloqués, n'hésitez pas à demander de l'aide sur les forums, sinon vous ne pourrez pas aller plus loin.

Récupérer les données

Dans un premier temps, nous allons apprendre à lire des informations dans la base de données, puis nous verrons dans le chapitre suivant comment ajouter et modifier des données.

Pour travailler ici, il nous faut une base de données « toute prête » qui va nous servir de support. Je vous invite à télécharger la table que j'ai créée pour vous :

Télécharger la table

Rien qu'au nom, vous pouvez vous douter que cette table contient quelque chose en rapport avec des jeux vidéo. En effet, vous allez le voir, cette table contient une liste d'une cinquantaine de jeux.

Pour cet exemple, plusieurs amis ont voulu répertorier tous les jeux vidéo qu'ils possèdent. La base de données est pour eux un moyen très pratique de classer et d'organiser tout cela ; vous allez voir pourquoi.

Euh dis, qu'est-ce que je dois en faire de ce fichier jeux_video.sql ?

Inutile d'essayer de l'ouvrir, ça n'a pas d'intérêt. Il faut l'importer via l'onglet « Importer » de phpMyAdmin. Nous avons appris à le faire dans le chapitre précédent. Pensez à sélectionner votre base de données test au préalable.

Et voilà ! Vous devriez voir une nouvelle table apparaître à gauche : jeux_video (figure suivante). Vous pouvez vous amuser à regarder ce qu'elle contient, pour vous faire une idée.

La table jeux_video apparaît maintenant dans phpMyAdmin
La table jeux_video apparaît maintenant dans phpMyAdmin

Voici les cinq premières entrées qu'elle contient (il y en a une cinquantaine en tout !) :

ID

nom

possesseur

console

prix

nbre_joueurs_max

commentaires

1

Super Mario Bros

Florent

NES

4

1

Un jeu d'anthologie !

2

Sonic

Patrick

Megadrive

2

1

Pour moi, le meilleur jeu au monde !

3

Zelda : ocarina of time

Florent

Nintendo 64

15

1

Un jeu grand, beau et complet comme on en voit rarement de nos jours

4

Mario Kart 64

Florent

Nintendo 64

25

4

Un excellent jeu de kart !

5

Super Smash Bros Melee

Michel

GameCube

55

4

Un jeu de baston délirant !

Pour le moment, ne modifiez pas cette table. Notre objectif est de créer une page PHP qui va afficher ce que contient la table jeux_video.

Faire une requête

Maintenant arrive le grand moment que vous attendiez tous : on va parler à MySQL. Nous allons donc commencer à parler en SQL ! Pour cela, on va faire ce qu'on appelle une requête. On va demander poliment à MySQL de nous dire tout ce que contient la table jeux_video.

Pour récupérer des informations de la base de données, nous avons besoin de notre objet représentant la connexion à la base. Vous vous souvenez, il s'agit de $bdd. Nous allons effectuer la requête comme ceci :

$reponse = $bdd->query('Tapez votre requête SQL ici');

On demande ainsi à effectuer une requête sur la base de données.

On récupère ce que la base de données nous a renvoyé dans un autre objet que l'on a appelé ici $reponse.

Votre première requête SQL

Comme je vous l'ai dit, le SQL est un langage. C'est lui qui nous permet de communiquer avec MySQL.

Voici la première requête SQL que nous allons utiliser :

SELECT * FROM jeux_video

Cela peut se traduire par : « Prendre tout ce qu'il y a dans la table jeux_video ».
Analysons chaque terme de cette requête.

  • SELECT : en langage SQL, le premier mot indique quel type d'opération doit effectuer MySQL. Dans ce chapitre, nous ne verrons que SELECT. Ce mot-clé demande à MySQL d'afficher ce que contient une table.

  • * : après le SELECT, on doit indiquer quels champs MySQL doit récupérer dans la table. Si on n'est intéressé que par les champs « nom » et « possesseur », il faudra taper :
    SELECT nom, possesseur FROM jeux_video

    Si vous voulez prendre tous les champs, tapez *. Cette petite étoile peut se traduire par « tout » : « Prendre tout ce qu'il y a… ».

  • FROM : c'est un mot de liaison qui se traduit par « dans ». FROM fait la liaison entre le nom des champs et le nom de la table.

  • jeux_video : c'est le nom de la table dans laquelle il faut aller piocher.

Effectuons la requête avec la méthode que l'on vient de découvrir :

<?php
$reponse = $bdd->query('SELECT * FROM jeux_video');
?>

$reponse contient maintenant la réponse de MySQL.

Afficher le résultat d'une requête

Le problème, c'est que $reponse contient quelque chose d'inexploitable. MySQL nous renvoie beaucoup d'informations qu'il faut organiser.

Vous imaginez toutes les informations qui s'y trouvent ? Si c'est une table à 10 champs, avec 200 entrées, cela représente plus de 2 000 informations ! Pour ne pas tout traiter d'un coup, on extrait cette réponse ligne par ligne, c'est-à-dire entrée par entrée.

Pour récupérer une entrée, on prend la réponse de MySQL et on y exécute fetch(), ce qui nous renvoie la première ligne.

<?php
$donnees = $reponse->fetch();
?>

$donnees est un array qui contient champ par champ les valeurs de la première entrée. Par exemple, si vous vous intéressez au champ console, vous utiliserez l'array $donnees['console'].

Il faut faire une boucle pour parcourir les entrées une à une. Chaque fois que vous appelez $reponse->fetch(), vous passez à l'entrée suivante. La boucle est donc répétée autant de fois qu'il y a d'entrées dans votre table.

Ouf ! Cela fait beaucoup d'informations à la fois. Je vous propose de résumer tout ce qu'on vient d'apprendre, de la connexion via PDO à l'affichage du résultat de la requête :

<?php
try
{
	// On se connecte à MySQL
	$bdd = new PDO('mysql:host=localhost;dbname=test', 'root', '');
}
catch(Exception $e)
{
	// En cas d'erreur, on affiche un message et on arrête tout
        die('Erreur : '.$e->getMessage());
}

// Si tout va bien, on peut continuer

// On récupère tout le contenu de la table jeux_video
$reponse = $bdd->query('SELECT * FROM jeux_video');

// On affiche chaque entrée une à une
while ($donnees = $reponse->fetch())
{
?>
    <p>
    <strong>Jeu</strong> : <?php echo $donnees['nom']; ?><br />
    Le possesseur de ce jeu est : <?php echo $donnees['possesseur']; ?>, et il le vend à <?php echo $donnees['prix']; ?> euros !<br />
    Ce jeu fonctionne sur <?php echo $donnees['console']; ?> et on peut y jouer à <?php echo $donnees['nbre_joueurs_max']; ?> au maximum<br />
    <?php echo $donnees['possesseur']; ?> a laissé ces commentaires sur <?php echo $donnees['nom']; ?> : <em><?php echo $donnees['commentaires']; ?></em>
   </p>
<?php
}

$reponse->closeCursor(); // Termine le traitement de la requête

?>

Essayer !

Alors, vous avez vu ?
Ça en fait un paquet de texte ! Il faut dire que la table que je vous ai donnée contient une cinquantaine d'entrées, donc c'est normal que vous ayez beaucoup de résultats.

Concrètement, que se passe-t-il ? On fait une boucle pour chaque entrée de la table. On commence par l'entrée n°1, puis l'entrée n°2, etc. Chaque fois qu'on fait une nouvelle boucle, on passe en revue une autre entrée.

Quelle est la différence entre $reponse et $donnees ?

$reponse contenait toute la réponse de MySQL en vrac, sous forme d'objet.
$donnees est un array renvoyé par le fetch(). Chaque fois qu'on fait une boucle, fetch va chercher dans $reponse l'entrée suivante et organise les champs dans l'array $donnees.

Je ne comprends pas la ligne while ($donnees = $reponse->fetch())

En effet, c'est un peu curieux et nouveau pour vous. Cette ligne fait deux choses à la fois :

  • elle récupère une nouvelle entrée et place son contenu dans $donnees ;

  • elle vérifie si $donnees vaut vrai ou faux.

Le fetch renvoie faux (false) dans $donnees lorsqu'il est arrivé à la fin des données, c'est-à-dire que toutes les entrées ont été passées en revue. Dans ce cas, la condition du while vaut faux et la boucle s'arrête.

Vous noterez à la fin la présence de la ligne :

<?php $reponse->closeCursor(); ?>

Elle provoque la « fermeture du curseur d'analyse des résultats ». Cela signifie, en d'autres termes plus humains, que vous devez effectuer cet appel à closeCursor() chaque fois que vous avez fini de traiter le retour d'une requête, afin d'éviter d'avoir des problèmes à la requête suivante. Cela veut dire qu'on a terminé le travail sur la requête.

Afficher seulement le contenu de quelques champs

Avec ce que je vous ai appris, vous devriez être capables d'afficher ce que vous voulez.
Personne ne vous oblige à afficher tous les champs ! Par exemple, si j'avais juste voulu lister les noms des jeux, j'aurais utilisé la requête SQL suivante :

SELECT nom FROM jeux_video

Reprenons le code complet précédent et adaptons-le pour afficher un nom de jeu par ligne :

<?php
try
{
	$bdd = new PDO('mysql:host=localhost;dbname=test', 'root', '');
}
catch(Exception $e)
{
        die('Erreur : '.$e->getMessage());
}

$reponse = $bdd->query('SELECT nom FROM jeux_video');

while ($donnees = $reponse->fetch())
{
	echo $donnees['nom'] . '<br />';
}

$reponse->closeCursor();

?>

Essayer !

Ce code est très semblable au précédent, mais c'est l'occasion pour vous de vous familiariser avec MySQL et PDO. Retenez tout particulièrement les choses suivantes :

  • la connexion à la base de données n'a besoin d'être faite qu'une seule fois, au début de la page ;

  • il faut fermer les résultats de recherche avec closeCursor() après avoir traité chaque requête.

Les critères de sélection

Imaginons que je souhaite obtenir uniquement la liste des jeux disponibles de la console « Nintendo 64 » et les trier par prix croissants. Ça paraît compliqué à faire ? Pas en SQL !

Vous allez voir qu'en modifiant nos requêtes SQL, il est possible de filtrer et trier très facilement vos données. Nous allons nous intéresser ici aux mots-clés suivants du langage SQL :

  • WHERE ;

  • ORDER BY ;

  • LIMIT.

WHERE

Grâce au mot-clé WHERE, vous allez pouvoir trier vos données.

Supposons par exemple que je veuille lister uniquement les jeux appartenant à Patrick. La requête au début sera la même qu'avant, mais je rajouterai à la fin WHERE possesseur='Patrick'.
Cela nous donne la requête :

SELECT * FROM jeux_video WHERE possesseur='Patrick'

Traduction : « Sélectionner tous les champs de la table jeux_video lorsque le champ possesseur est égal à Patrick ».

Un petit code pour voir ce que ça donne ?

<?php
try
{
	$bdd = new PDO('mysql:host=localhost;dbname=test', 'root', '');
}
catch(Exception $e)
{
        die('Erreur : '.$e->getMessage());
}

$reponse = $bdd->query('SELECT nom, possesseur FROM jeux_video WHERE possesseur=\'Patrick\'');

while ($donnees = $reponse->fetch())
{
	echo $donnees['nom'] . ' appartient à ' . $donnees['possesseur'] . '<br />';
}

$reponse->closeCursor();

?>

Essayer !

Si vous vous amusez à changer le nom du possesseur (WHERE possesseur='Michel' par exemple ), ça n'affichera que les jeux appartenant à Michel. Essayez, vous verrez !

Il est par ailleurs possible de combiner plusieurs conditions. Par exemple, si je veux lister les jeux de Patrick qu'il vend à moins de 20 euros, je combinerai les critères de sélection à l'aide du mot-clé AND (qui signifie « et ») :

SELECT * FROM jeux_video WHERE possesseur='Patrick' AND prix < 20

Traduction : « Sélectionner tous les champs de jeux_video lorsque le possesseur est Patrick ET lorsque le prix est inférieur à 20 ».

ORDER BY

ORDER BY nous permet d'ordonner nos résultats. Nous pourrions ainsi classer les résultats en fonction de leur prix ! La requête SQL serait :

SELECT * FROM jeux_video ORDER BY prix

Traduction : « Sélectionner tous les champs de jeux_video et ordonner les résultats par prix croissants ».

Application :

<?php
try
{
	$bdd = new PDO('mysql:host=localhost;dbname=test', 'root', '');
}
catch(Exception $e)
{
        die('Erreur : '.$e->getMessage());
}

$reponse = $bdd->query('SELECT nom, prix FROM jeux_video ORDER BY prix');

while ($donnees = $reponse->fetch())
{
	echo $donnees['nom'] . ' coûte ' . $donnees['prix'] . ' EUR<br />';
}

$reponse->closeCursor();

?>

Essayer !

Et si je veux classer par ordre décroissant ?

Facile. Il suffit de rajouter le mot-clé DESC à la fin :

SELECT * FROM jeux_video ORDER BY prix DESC

Traduction : « Sélectionner tous les champs de jeux_video, et ordonner les résultats par prix décroissants ».

LIMIT

LIMIT nous permet de ne sélectionner qu'une partie des résultats (par exemple les 20 premiers). C'est très utile lorsqu'il y a beaucoup de résultats et que vous souhaitez les paginer (c'est-à-dire par exemple afficher les 30 premiers résultats sur la page 1, les 30 suivants sur la page 2, etc).

À la fin de la requête, il faut ajouter le mot-clé LIMIT suivi de deux nombres séparés par une virgule. Par exemple :

SELECT * FROM jeux_video LIMIT 0, 20

Ces deux nombres ont un sens bien précis.

  • On indique tout d'abord à partir de quelle entrée on commence à lire la table. Ici, j'ai mis « 0 », ce qui correspond à la première entrée. Attention, cela n'a rien à voir avec le champ ID ! Imaginez qu'une requête retourne 100 résultats : LIMIT tronquera à partir du premier résultat si vous indiquez 0, à partir du 21ème si vous indiquez 20, etc.

  • Ensuite, le deuxième nombre indique combien d'entrées on doit sélectionner. Ici, j'ai mis « 20 », on prendra donc vingt entrées.

Quelques exemples :

  • LIMIT 0, 20 : affiche les vingt premières entrées ;

  • LIMIT 5, 10 : affiche de la sixième à la quinzième entrée ;

  • LIMIT 10, 2 : affiche la onzième et la douzième entrée.

Un petit schéma se trouve à la figure suivante pour résumer le fonctionnement de LIMIT.

Présentation du fonctionnement de LIMIT
Présentation du fonctionnement de LIMIT

Allez, un petit exemple ! Si on veut afficher les 10 premiers jeux de la table, on utilisera le code suivant :

<?php
try
{
	$bdd = new PDO('mysql:host=localhost;dbname=test', 'root', '');
}
catch(Exception $e)
{
        die('Erreur : '.$e->getMessage());
}

$reponse = $bdd->query('SELECT nom FROM jeux_video LIMIT 0, 10');

echo '<p>Voici les 10 premières entrées de la table jeux_video :</p>';
while ($donnees = $reponse->fetch())
{
	echo $donnees['nom'] . '<br />';
}

$reponse->closeCursor();

?>

Essayer !

Et voilà le travail !

Bonjour, je suis masochiste et avant de terminer cette section, je souhaiterais mélanger toutes les requêtes SQL que je viens d'apprendre en une seule. C'est possible ?

Mais bien entendu, mon petit. ;-)

Voilà de quoi te triturer les méninges :

SELECT nom, possesseur, console, prix FROM jeux_video WHERE console='Xbox' OR console='PS2' ORDER BY prix DESC LIMIT 0,10

Essayez donc de traduire ça en français dans un premier temps pour voir si vous avez compris, puis testez cette requête chez vous pour voir si c'est bien ce à quoi vous vous attendiez.

Construire des requêtes en fonction de variables

Les requêtes que nous avons étudiées jusqu'ici étaient simples et effectuaient toujours la même opération. Or les choses deviennent intéressantes quand on utilise des variables de PHP dans les requêtes.

La mauvaise idée : concaténer une variable dans une requête

Prenons cette requête qui récupère la liste des jeux appartenant à Patrick :

<?php
$reponse = $bdd->query('SELECT nom FROM jeux_video WHERE possesseur=\'Patrick\'');
?>

Au lieu de toujours afficher les jeux de Patrick, on aimerait que cette requête soit capable de s'adapter au nom de la personne défini dans une variable, par exemple $_GET['possesseur']. Ainsi la requête pourrait s'adapter en fonction de la demande de l'utilisateur !

Nous pourrions être tentés de concaténer la variable dans la requête, comme ceci :

<?php
$reponse = $bdd->query('SELECT nom FROM jeux_video WHERE possesseur=\'' . $_GET['possesseur'] . '\'');
?>

Bien que ce code fonctionne, c'est l'illustration parfaite de ce qu'il ne faut pas faire et que pourtant beaucoup de sites font encore. En effet, si la variable $_GET['possesseur'] a été modifiée par un visiteur (et nous savons à quel point il ne faut pas faire confiance à l'utilisateur !), il y a un gros risque de faille de sécurité qu'on appelle injection SQL. Un visiteur pourrait s'amuser à insérer une requête SQL au milieu de la vôtre et potentiellement lire tout le contenu de votre base de données, comme par exemple la liste des mots de passe de vos utilisateurs.

}

Nous allons utiliser un autre moyen plus sûr d'adapter nos requêtes en fonction de variables : les requêtes préparées.

La solution : les requêtes préparées

Le système de requêtes préparées a l'avantage d'être beaucoup plus sûr mais aussi plus rapide pour la base de données si la requête est exécutée plusieurs fois. C'est ce que je préconise d'utiliser si vous voulez adapter une requête en fonction d'une ou plusieurs variables.

Avec des marqueurs « ? »

Dans un premier temps, on va « préparer » la requête sans sa partie variable, que l'on représentera avec un marqueur sous forme de point d'interrogation :

<?php
$req = $bdd->prepare('SELECT nom FROM jeux_video WHERE possesseur = ?');
?>

Au lieu d'exécuter la requête avec query() comme la dernière fois, on appelle ici prepare().

La requête est alors prête, sans sa partie variable. Maintenant, nous allons exécuter la requête en appelant execute et en lui transmettant la liste des paramètres :

<?php
$req = $bdd->prepare('SELECT nom FROM jeux_video WHERE possesseur = ?');
$req->execute(array($_GET['possesseur']));
?>

La requête est alors exécutée à l'aide des paramètres que l'on a indiqués sous forme d'array.

S'il y a plusieurs marqueurs, il faut indiquer les paramètres dans le bon ordre :

<?php
$req = $bdd->prepare('SELECT nom FROM jeux_video WHERE possesseur = ? AND prix <= ?');
$req->execute(array($_GET['possesseur'], $_GET['prix_max']));
?>

Le premier point d'interrogation de la requête sera remplacé par le contenu de la variable $_GET['possesseur'], et le second par le contenu de $_GET['prix_max']. Le contenu de ces variables aura été automatiquement sécurisé pour prévenir les risques d'injection SQL.

Essayons de construire une page capable de lister les jeux appartenant à une personne et dont le prix ne dépasse pas une certaine somme :

<?php
try
{
	$bdd = new PDO('mysql:host=localhost;dbname=test', 'root', '');
}
catch(Exception $e)
{
        die('Erreur : '.$e->getMessage());
}

$req = $bdd->prepare('SELECT nom, prix FROM jeux_video WHERE possesseur = ?  AND prix <= ? ORDER BY prix');
$req->execute(array($_GET['possesseur'], $_GET['prix_max']));

echo '<ul>';
while ($donnees = $req->fetch())
{
	echo '<li>' . $donnees['nom'] . ' (' . $donnees['prix'] . ' EUR)</li>';
}
echo '</ul>';

$req->closeCursor();

?>

Essayez d'appeler cette page (que l'on nommera par exemple selection_jeux.php) en modifiant les valeurs des paramètres. Vous allez voir que la liste des jeux qui ressort change en fonction des paramètres envoyés !

Avec des marqueurs nominatifs

Si la requête contient beaucoup de parties variables, il peut être plus pratique de nommer les marqueurs plutôt que d'utiliser des points d'interrogation.

Voici comment on s'y prendrait :

<?php
$req = $bdd->prepare('SELECT nom, prix FROM jeux_video WHERE possesseur = :possesseur AND prix <= :prixmax');
$req->execute(array('possesseur' => $_GET['possesseur'], 'prixmax' => $_GET['prix_max']));
?>

Les points d'interrogation ont été remplacés par les marqueurs nominatifs :possesseur et :prixmax (ils commencent par le symbole deux-points, comme vous le voyez).

Cette fois-ci, ces marqueurs sont remplacés par les variables à l'aide d'un array associatif. Quand il y a beaucoup de paramètres, cela permet parfois d'avoir plus de clarté. De plus, contrairement aux points d'interrogation, nous ne sommes cette fois plus obligés d'envoyer les variables dans le même ordre que la requête.

Traquer les erreurs

Lorsqu'une requête SQL « plante », bien souvent PHP vous dira qu'il y a eu une erreur à la ligne du fetch :

Fatal error: Call to a member function fetch() on a non-object in C:\wamp\www\tests\index.php on line 13

Ce n'est pas très précis, je pense que vous êtes d'accord avec moi.
Ce n'est pas la ligne du fetch qui est en cause : c'est souvent vous qui avez mal écrit votre requête SQL quelques lignes plus haut.

Pour afficher des détails sur l'erreur, il faut activer les erreurs lors de la connexion à la base de données via PDO.

Vous vous souvenez de cette ligne ?

<?php
$bdd = new PDO('mysql:host=localhost;dbname=test', 'root', '');
?>

Ajoutez-y un paramètre à la fin pour activer les erreurs :

<?php
$bdd = new PDO('mysql:host=localhost;dbname=test', 'root', '', array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
?>

Désormais, toutes vos requêtes SQL qui comportent des erreurs les afficheront avec un message beaucoup plus clair.

Supposons par exemple que j'écrive mal le nom du champ :

<?php
$reponse = $bdd->query('SELECT champinconnu FROM jeux_video');
?>

L'erreur suivante s'affichera alors :

Unknown column 'champinconnu' in 'field list'

C'est de l'anglais, certes, mais c'est déjà beaucoup plus précis que l'erreur que l'on avait tout à l'heure. Si on traduit, cela signifie : « La colonne champinconnu est introuvable dans la liste des champs ». En effet, il n'y a aucun champ qui s'appelle champinconnu.

En résumé

  • Pour dialoguer avec MySQL depuis PHP, on fait appel à l'extension PDO de PHP.

  • Avant de dialoguer avec MySQL, il faut s'y connecter. On a besoin de l'adresse IP de la machine où se trouve MySQL, du nom de la base de données ainsi que d'un login et d'un mot de passe.

  • Les requêtes SQL commençant par SELECT permettent de récupérer des informations dans une base de données.

  • Il faut faire une boucle en PHP pour récupérer ligne par ligne les données renvoyées par MySQL.

  • Le langage SQL propose de nombreux outils pour préciser nos requêtes, à l'aide notamment des mots-clés WHERE (filtre), ORDER BY (tri) et LIMIT (limitation du nombre de résultats).

  • Pour construire une requête en fonction de la valeur d'une variable, on passe par un système de requête préparée qui permet d'éviter les dangereuses failles d'injection SQL.

Exemple de certificat de réussite
Exemple de certificat de réussite