Aller au menu - Aller au contenu

[Plan du site] Vous êtes ici --- > Le Site du Zéro > Les tutoriels > Non-Officiels > Site Web > PHP > Base de données > SQL en POO sans prise de tête > Lecture du tutoriel

SQL en POO sans prise de tête

Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Avatar
Auteur : SpaceFox
Note : 16 / 20 (10 votes)
Visualisations : 8 424

Plus d'informations Plus d'informations
Vous avez sans doute déjà aperçu un certain nombre de tutos & sites qui vous disaient en gros caractères alléchants : « Simplifiez SQL en utilisant la POO ! ».

Dans 95 % des cas, ces tutos & sites vous proposaient de créer une classe gérant les principales fonctions dont vous avez besoin avec SQL.

Mais saviez-vous que, depuis PHP 4.1.3, tous ces tutos sont obsolètes ? Car il existe une extension de PHP qui, une fois activée, fait déjà tout ça - et même des trucs auxquels vous n'avez même pas pensé !
C'est cette extension et ses avantages (et inconvénients :-° ) que nous allons voir dans ce tuto.
Ici, je pars du principe que vous désirez utiliser MySQL, et que vous voulez utiliser la Programmation Orientée Objet (POO).
D'aucuns dirons qu'il y a d'autres méthodes, qu'elle n'est pas optimale, etc. Ce n'est pas le problème ici. Ce tutoriel présente une solution, et n'a pas la prétention de présenter la meilleure solution (qui, de toutes façons, n'existe pas dans l'absolu).
Sommaire du chapitre :
Icône du chapitre

Ce dont vous avez besoin

Bonne question, ça :
De quoi ai-je besoin pour pouvoir utiliser cette extension qui, d'après toi, va me faire tout mon boulot tout seul ?

Heu... D'abord, j'ai pas dit que ça allait faire le boulot tout seul, hein, j'ai dit que ça allait le simplifier. Nuance.

De quoi parle-t-on ?



D'abord, il faut comprendre le titre, ce qui implique deux choses.

1- Savoir ce qu'est SQL et comment on s'en sert d'habitude



Je ne peux que vous renvoyer à l'excellent tuto de M@teo21.

2- Savoir ce qu'est la Programmation Orientée Objet (POO)



En fait, on ne va pas avoir besoin de toute la puissance de la programmation orientée objet ici. Pour comprendre ce tuto, il faut que vous sachiez juste :

Je rappellerai comment on fait tout ça en PHP ; en cas de doute, vous pouvez consulter la partie sur la POO du cours de C++. La théorie est exactement la même !

Comment utiliser MySQLi ?



Maintenant qu'on sait de quoi on parle, il faut pouvoir utiliser cette fameuse extension. La doc nous dit qu'il faut l'installer, mais, pas de panique ! C'est déjà fait sur tout serveur sérieux !

Pour vous rassurer, voici les hébergeurs et programmes qui l'ont activée :

Mon hébergeur ou programme n'est pas dans la liste, ça veut dire que je ne peux pas l'utiliser ?
Absolument pas ! Simplement, je n'ai pas cette information.
D'ailleurs, voici sa cousine, la liste des hébergeurs / programmes qui ne permettent pas l'utilisation de MySQLi :

Si vous connaissez un serveur qui n'est pas dans l'une de ces listes, communiquez-le moi (forum, MP) et je l'ajouterai.
Comment faire pour savoir si je peux utiliser MySQLi ?

Le plus simple est de créer un fichier, appelé (par exemple) version.php, et qui contient juste ça :
Code : PHP
1
2
3
<?php
     phpinfo();
?>

Mettez-le sur le serveur et allez voir avec votre navigateur internet préféré. Vous devriez obtenir un gros tas de tableaux ; cherchez un peu (Ctrl + F ? ;) ) ; si vous trouvez un gros titre disant « MySQLi » suivi d'un tableau, c'est bon. Sinon, vous ne pouvez pas utiliser MySQLi en l'état.
Que faire si je ne peux pas utiliser MySQLi ?

Il faut demander au responsable du serveur de l'activer :
Si c'est vous (donc que vous développez sur votre PC), mettez vos logiciels à jour (EasyPHP, Wamp, PHP seul éventuellement, etc.).
Si vous ne pouvez toujours pas l'utiliser, c'est sans doute qu'elle n'est pas activée, mais là on sort du cadre du tuto.

Si vous êtes sur un serveur hébergé à Pétaouschnock et que vous n'en avez pas les droits d'administration, il faut demander à l'administrateur. Mais bon, là j'y crois pas trop, m'enfin, vous pouvez toujours essayer...

Si vous ne pouvez toujours pas utiliser MySQLi, vous pouvez essayer l'une des nombreuses classes qui émulent son fonctionnement, mais vous perdez l'intérêt de la classe interne à PHP.
Celle-ci n'a pas l'air mal, mais je ne l'ai pas testée.
Notez que si ces classes fonctionnent de la même manière que MySQLi, elles ne sont pas immédiatement compatibles (i.e. les classes, méthodes et attributs ont des noms différents).

La programmation orientée objet en PHP



Comme je ne vais pas refaire ce qui a déjà été fait, je vous renvoie au tuto de Keeper.

Bien, maintenant qu'on sait ce qu'on va faire et avec quoi, on va pouvoir attaquer les choses sérieuses !

MySQLi : les fonctions de base

J'utilise des guillemets simples (') pour délimiter mes chaînes de caractères, mais rien ne vous empêche d'utiliser les guillemets doubles (") si vous préférez. Dans mes exemples ça ne change rien, mais faites gaffe dans vos scripts. Voyez ici pour la différence.


Les objets utilisés



En utilisant MySQLi, on utilise en fait deux types d'objets :
1- l'objet de type MySQLi, qui représente la connexion à la base de données. C'est grâce à lui qu'on va pouvoir faire nos requêtes et récupérer plein d'informations utiles ;
2- l'objet de type result, qui représente le résultat de notre action sur la connexion. Typiquement, on s'en sert pour deux choses :
Bien sûr, il y a d'autres objets, mais ils ne nous intéressent pas à notre niveau.

L'utilisation de base



Créer une connexion



Ben oui, ça n'a pas changé : si on ne se connecte pas à la base de données, on ne peut strictement rien faire.
Il s'agit tout simplement de créer un nouvel objet MySQLi qui représente ? Oui, une connexion !

Ça se fait très simplement, de la manière suivante :
Code : PHP
1
2
3
<?php
$connexion = new mysqli('adresse_du_serveur', 'login', 'mot_de_passe', 'nom_de_la_base_de_donnees');
?>

On peut évidemment sélectionner la base de données à un autre moment :
Code : PHP
1
2
3
4
<?php
$connexion = new mysqli('adresse_du_serveur', 'login', 'mot_de_passe');
$connexion->select_db('nom_de_la_base_de_donnees');
?>


Faire une requête



Bon, maintenant qu'on a une connexion, on aimerait bien l'utiliser !
Rien de plus simple !
Code : PHP
1
2
3
<?php
$resultat = $connexion->query('MA REQUETE SQL');
?>
Ici la variable $resultat contient ce qu'a retourné la requête SQL : un objet result, ou encore false si la requête n'a pas marché, ou tout un tas de trucs en fonction de ladite requête.

Fermer la connexion



Citation : Lao-Tseu
Une connexion inutile, jamais ne doit être gardée.

En conséquence de quoi, les programmeurs ont prévu une fonction permettant de fermer une connexion :
Code : PHP
1
2
3
<?php
$connexion->close(); // close() est une méthode, donc n'oubliez pas les parenthèses !
?>


Traiter un résultat



Bon, maintenant on a (sans doute) récupéré le résultat de notre requête, mais il faut bien en faire quelque chose...

Défiler une liste de résultats



C'est ce qu'on fait le plus souvent, et si vous savez faire ça, vous pouvez récupérer un seul résultat.
Code : PHP
1
2
3
4
5
6
7
<?php
while ($ligne = $resultat->fetch_assoc()) {
   /* Traitement du résultat
   On accède aux différentes colonnes par : */
   $ligne['nomDeLaColonne'];
}
?>


Ou, en renvoyant un objet :
Code : PHP
1
2
3
4
5
6
7
<?php
while ($ligne = $resultat->fetch_object()) {
   /* Traitement du résultat
   On accède aux différentes colonnes par : */
   $ligne->nomDeLaColonne;
}
?>
Vous devez avoir des noms de colonnes uniques pour ces fonctions. Si vous avez une requête qui renvoie deux noms de colonnes identiques, seule la première sera prise en compte. Dans ce cas, utilisez fetch_row().

PHP nous informe que la méthode fetch_row() n'est pas significativement plus rapide que fetch_array() ou fetch_object(). Utilisez ces deux dernières, elles sont plus claires et permettent au code d'être relu plus facilement...

Compter le nombre de lignes envoyées



C'est très simple, l'objet $resultat a une propriété qui donne directement cette valeur :
Code : PHP
1
2
3
<?php
$resultat->num_rows
?>


Fermer un résultat



Comme la connexion, il est inutile de garder en mémoire un résultat qui ne sert plus à rien, et ça peut prendre beaucoup de place dans la mémoire !
Vous pouvez utiliser n'importe laquelle des méthodes suivantes, seul le nom change :
Code : PHP
1
2
3
4
5
<?php
$resultat->close();
$resultat->free();
$resultat->free_result();
?>
Comme pour la connexion, n'oubliez pas les parenthèses () : c'est une méthode.


Il existe plein d'autres fonctions de traitement de résultat, mais elles sont assez spécifiques ou ne nous intéressent pas. N'hésitez pas à aller voir la doc pour plus d'infos.
Et ? C'est tout sur le traitement des résultats ?

Oui. Le reste se fait exactement comme vous le faisiez avec la bibliothèque SQL « standard ».

Maintenant que vous avez les bases, je vais vous présenter ce qu'on peut facilement faire grâce à MySQLi.

MySQLi : utilisation "avancée"

Gestion d'erreurs



Par défaut, MySQLi reste muet sur les erreurs qui peuvent survenir dans vos requêtes. Nous allons le faire parler :diable: (mais sans violence, je vous rassure).
Le code de cette partie fonctionne avec PHP5, je ne l'ai pas testé avec PHP4, et je crois qu'il ne fonctionne pas (la gestion objet est très différente).


Une nouvelle classe



Donc, nous voulons une classe qui fait la même chose que MySQLi et en plus qui nous affiche les erreurs, pour nous aider à débugger.
Rien de plus simple, il suffit de faire une classe qui hérite de MySQLi (c'est une classe, donc on peut l'hériter) :
Code : PHP
1
2
3
4
5
<?php
class Sql extends mysqli {
    // Le code de la classe, qu'on va remplir au fur et à mesure.
}
?>

Voilà, on peut utiliser notre classe Sql en lieu et place de notre classe MySQLi, et ce en changeant une seule ligne de code : la création !
Code : PHP
1
2
3
<?php
$connexion = new mysqli('adresse_du_serveur', 'login', 'mot_de_passe', 'nom_de_la_base_de_donnees');
?>

devient
Code : PHP
1
2
3
<?php
$connexion = new Sql('adresse_du_serveur', 'login', 'mot_de_passe', 'nom_de_la_base_de_donnees');
?>

Notez que contrairement à PHP, je respecte ici la convention qui dit qu'un nom de classe commence par une majuscule.


Erreurs à la connexion



Les premières erreurs peuvent survenir quand on crée la connexion à la base de données (erreur de login / mot de passe, etc.).
Avec MySQLi, on se connecte à la création de l'objet : il faut donc gérer cette erreur dans le constructeur.
Je vais considérer qu'on sélectionne la base de données directement à l'instanciation.
Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php
    public function __construct($hote, $login, $pass, $base) {    // On surcharge le constructeur
 
        /* On appelle le constructeur de la classe parent.
           Ici, c'est comme si on faisait un « $retour = new mysqli($hote, $login, $pass, $base) », sauf que comme on est dans une classe héritée de MySQLi, on doit utiliser cette syntaxe pour accéder aux méthodes de la classe parent.
        */
        $retour = parent::__construct($hote, $login, $pass, $base);
 
        // On regarde si MySQLi a renvoyé un numéro d'erreur à la connexion :
        if (mysqli_connect_errno()) {
            echo '<p class = "erreur">Échec de la connexion : '.mysqli_connect_error().'</p>';    // Si oui, on affiche le message d'erreur généré
            exit();    // Puis on termine le script, rien ne sert de continuer sans la connexion SQL, tout va bugger.
        }
 
        return $retour;  // On renvoie ce qu'a renvoyé le constructeur de MySQLi
    }
?>


Erreurs de requêtes



Même concept, cette fois on surcharge la méthode query(), qui permet d'exécuter les requêtes.
Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php
    public function query($requete) {
 
        // On exécute la requête via la méthode de la classe parent
        $retour = parent::query($requete);
 
        // Si la requête a renvoyé "false", c'est qu'elle a échouée
        if (!$retour) {
            // On affiche alors le numéro et la description de l'erreur
            echo '<p class = "erreur">Erreur SQL n°',$this->errno,' : ',$this->error,'</p>';
            // Ainsi que la requête qui a raté    
            echo '<p class = "erreur">Requête concernée : « ',$query,' »</p>';
            $retour = false;
        }
        return $retour;
        }
?>

Quel est l'intérêt de ce genre de choses par rapport à un habituel « or die() » placé après les exécutions de requêtes ?

C'est simple : quand vous voulez passer votre site « en production », il est très conseillé de ne pas afficher les erreurs SQL, surtout parce que les requêtes peuvent donner des indications importantes pour hacker votre site.
Ici, notre classe est entièrement compatible avec MySQLi : si vous vous contentez de créer une connexion avec MySQLi et non la classe SQL, votre code fonctionnera exactement de la même manière - il n'affichera aucune erreur, c'est tout.

Tracer les requêtes



Quand on débugge un site, c'est souvent pratique de savoir si on a des requêtes lentes, et si oui lesquelles.
On va ajouter quelques lignes de code à notre classe pour savoir ça automatiquement !
Ne jamais laisser les traces complètes sur un site accessible au public, à moins que vous ne teniez absolument à vous faire hacker !

On voudrait savoir facilement :
On va donc ajouter trois attributs à notre classe :
Code : PHP
1
2
3
4
5
6
7
<?php
class Sql extends mysqli {
    public $tempsExecution = 0;    // Temps d'exécution total
    public $nbRequetes = 0;        // Nombre de requêtes exécutées dans le script
    public $traceRequetes = null;  // Tableau associant chaque requête à son temps d'exécution, en millisecondes
}
?>

Puis, on va modifier la surcharge de la méthode query() :
Code : PHP
 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
<?php
public function query($requete) {
 
    // On enregistre la requête dans le tableau, à l'emplacement n° $nbRequete.
    $this->traceRequetes[$this->nbRequetes]['requete'] = $requete;
 
    // On enregistre le temps serveur avant l'exécution de la requête
    $tmpTemps = microtime(true); 
 
    // On exécute la requête
    $retour = parent::query($requete);
 
    // La différence entre le temps serveur avant et après la requête nous donne le temps d'exécution
    $tmpTempsRequete = microtime(true) - $tmpTemps;
 
    // On ajoute ce temps au temps total
    $this->tempsExecution += $tmpTempsRequete;
 
    // On enregistre le temps d'exécution de la requête, à l'emplacement n° $nbRequete requête (en millisecondes, avec 2 chiffres après la virgule).
    $this->traceRequetes[$this->nbRequetes]['temps'] = round($tmpTempsRequete * 1000, 2);
 
    // On incrémente le nombre de requêtes effectuées
    $this->nbRequetes++;
 
    // On renvoie le résultat
    return $retour;
}
?>


Puis, à la fin du script, il suffit d'afficher l'attribut traceRequetes pour avoir une liste complète des requêtes exécutées, dans l'ordre, avec leur temps d'exécution.
Par exemple :
Code : PHP
1
2
3
<?php
echo '<pre>',htmlentities(print_r($connexion->traceRequetes, true)),'</pre>';
?>

« <pre> ... </pre> » affiche le texte tel qu'il est formaté dans la source ;
« htmlentities( ..., true) » est nécessaire pour ne pas faire bugger le navigateur s'il y a des « < », « > » ou « & » dans la requête ;
et « print_r ($variable) » formate et affiche le contenu d'une variable.
Ce bout de code affiche quelque chose comme :
Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Array
(
    [0] => Array
        (
            [requete] => SELECT 'Ma requête SQL' FROM machin
            [temps] => 33,29
        )
 
    [1] => Array
        (
            [requete] => SELECT 'Une autre requête SQL' FROM truc
            [temps] => 1,62
        )
)


Si vous voulez tracer les requêtes sur un site en production, vous pouvez tout simplement enregistrer l'attribut $connexion->traceRequetes dans un fichier, à la fin du script !


La classe complète



Sans les commentaires, pour ne pas surcharger. Tout le code est décortiqué juste ci-dessus, remontez un peu si vous ne comprenez pas quelque chose.
Code : PHP
 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
<?php
class Sql extends mysqli {
    public $tempsExecution = 0;
    public $nbRequetes = 0;
    public $traceRequetes = null;
    
    public function __construct($hote, $login, $pass, $base) {
        $retour = parent::__construct($hote, $login, $pass, $base);
        if (mysqli_connect_errno()) {
            echo '<p class = "erreur">Échec de la connexion : '.mysqli_connect_error().'</p>';
            exit();
        }
                
        return $retour;
    }
    
    public function query($requete) {
 
        $this->traceRequetes[$this->nbRequetes]['requete'] = $requete;
    
        $tmpTemps = microtime(true);
        $retour = parent::query($requete);
        $tmpTempsRequete = microtime(true) - $tmpTemps;
        $this->tempsExecution += $tmpTempsRequete;
        
        $this->traceRequetes[$this->nbRequetes]['temps'] = round($tmpTempsRequete * 1000, 2);
        $this->nbRequetes++;
        
        if (!$retour) {
            echo '<p class = "erreur">Erreur SQL n°',$this->errno,' : ',$this->error,'</p>';
            echo '<p class = "erreur">Requête concernée : "',$query,'"</p>';
            $retour = false;
        }
        return $retour;
    }
    
}
?>


Voilà, comme vous avez pu le voir, le grand intérêt de MySQLi est de pouvoir étendre très facilement ses possibilités, et ce en modifiant très peu le code.
Si vous avez un code qui fonctionne déjà avec MySQLi, vous n'aurez qu'à changer la ligne de création de l'objet pour profiter de la classe présentée, par exemple.

Après, à vous de voir ce dont vous avez besoin, laissez courir votre imagination !

Migrer un script de l'extension MySQL vers MySQLi

Cette partie va être très courte, puisque très simple.

Principe de base



Toutes les fonctions de l'extension MySQL de PHP sont de la forme : mysql_nom_fonction(), et s'utilisent ainsi :
Code : PHP
1
2
3
<?php
$retour = mysql_nom_fonction($connexion);
?>

$connexion est le nom de la variable de connexion, créé au début du script.

Pour migrer votre script, il suffit :
1) de modifier « $connexion », qui devient un objet de type MySQLi (comme montré ci-dessus) ;
2) de modifier vos appels de fonctions MySQL, qui deviennent tous du type :
Code : PHP
1
2
3
<?php
$retour = $connexion->nom_fonction();
?>

Où « $connexion » ne change pas, et « nom_fonction() » est le même qu'avant !

Donc, même pas besoin de réfléchir ! Il suffit de lancer une recherche sur « mysql_ » et de remplacer !

Pièges à éviter



... Parce que n'est pas si simple que ça !

Le type de l'objet



Dans certains cas, il ne faut pas utiliser :
Code : PHP
1
2
3
<?php
$retour = $connexion->nom_fonction();
?>

mais quelque chose du style :
Code : PHP
1
2
3
<?php
$retour = $resultat->nom_fonction();
?>

Où « $resultat » est l'objet renvoyé par la méthode query().
Mais comment je sais si je dois utiliser $connexion ou $resultat ?

Il suffit de réfléchir 30 secondes : si vous voulez utiliser la connexion, c'est $connexion ; si vous voulez utiliser un résultat, c'est $resultat ! En général, c'était déjà le cas avec l'ancien système.

Les attributs



Dans certains cas, ce qui était une fonction devient un attribut, et non pas une méthode. Donc, si vous laissez les parenthèses à la fin du nom, ça buggera, et le message d'erreur n'est pas très clair.
Le plus utilisé, c'est
Code : PHP
1
2
3
<?php
$retour = mysql_num_rows($resultat);
?>

qui devient
Code : PHP
1
2
3
<?php
$retour = $resultat->num_rows;    // Sans les parenthèses !
?>

Voilà, vous savez (presque) tout de MySQLi. En fait, non. Vous savez faire ce que vous saviez faire avant, plus quelques trucs.

MySQLi permet de faire la même chose que l'extension MySQL (ça, vous savez le faire, maintenant), plus plein d'autres trucs. Les plus intéressants sont la gestion des transactions et surtout la possibilité de « grouper » les requêtes en un seul appel. Mais là, c'est l'objet d'un autre tuto !

N'hésitez pas à aller consulter et reconsulter la doc, vous y trouverez toujours quelque chose ; d'autant plus qu'elle est claire et en français !
Retour en haut Retour en haut


Créé : le 18/11/2007 à 23:51:09
Modifié : le 22/08/2008 à 16:09:15
Avancement : 0%
Licence : Copie non autorisée

Changer de design | En savoir plus | Plan du site | Politique d'accessibilité | Règles | Fil RSS | XHTML 1.0 | CSS 2.0
Édité par Simple IT SARL : Nous contacter | Revue de presse | Publicité

Y'a plus rien à lire, faut remonter maintenant !

Hébergement web - Correction de tutoriels - Créer un site
Vous souhaitez apparaître ici ? Contactez-nous.

Nombre de connectés 663 Zéros connectés | Requêtes SQL 9 requêtes | Temps de génération de la page : Total (SQL) 0.0371s (0.0257s)