Les bonnes pratiques de la classe Db sur PrestaShop
Cet article est une archive de sécurité de l'article proposé par Prestashop en 2012
Par Raphaël Malié - Développeur cœur PrestaShop
La grande majorité des modules et des développements à effectuer sur PrestaShop nécessitent d’utiliser ou d’insérer des informations dans la base de données. L’utilisation de la classe cœur DB est donc une étape obligatoire pour tout développeur.
En plus de fournir une abstraction potentielle pour d’autres types de bases de données relationnelles, la classe DB fournit plusieurs outils destinés à simplifier la vie !
Cet article a pour but d’expliquer ses différentes méthodes, dans quel contexte les utiliser et les bonnes pratiques à avoir en développement.
Un article similaire a été écrit sur les pratiques de la 1.4, celui ci se présente de la même façon mais pour la version 1.5.
Vous trouverez également en bas de page, les principales différences entre la 1.4 et la 1.5 concernant ces classes.
Les fondements de la classe
La classe DB est en réalité constituée de deux classes :
- La classe Db dans le fichier ~/classes/db/Db.php qui est abstraite
- Une classe fille étendant la classe Db. Actuellement trois abstractions sont supportées en tant que classes filles : MySQL, MySQLi et PDO. Par défaut PDO est utilisé, cependant si l’extension n’est pas installée chez votre hébergeur MySQLi sera utilisé, et si ni l’une ni l’autres ne le sont MySQL sera pris.
DB est un pseudo singleton, elle peut tout de même être instanciée au besoin manuellement car le constructeur est public, mais au sein de PrestaShop il est demandé d’accéder à l’instance comme ceci : $db = Db::getInstance();
Dans certains cas vous croiserez des appels comme ceci dans le code : $db = Db::getInstance(_PS_USE_SQL_SLAVE_);
Si l’utilisateur de PrestaShop permet l’utilisation de serveurs MySQL esclaves dans son architecture, alors la connexion de cette dernière instance pourra se faire sur les serveurs esclaves. Il ne faut utiliser la constante _PS_USE_SQL_SLAVE_ en argument que dans le cadre de requêtes en lecture (SELECT, SHOW, etc..), et uniquement si celles-ci n’ont pas besoin d’avoir un résultat mis à jour immédiatement. Si vous faites une requête select juste après une insertion sur la même table, il est nécessaire de la faire sur le serveur maître.
Les différentes méthodes
1. La méthode insert($table, $data, $null_values = false, $use_cache = true, $type = Db::INSERT, $add_prefix = true)
Cette méthode permet de générer automatiquement l’insertion de données dans la base à partir d’un tableau de données. Il faut utiliser cette méthode à la place de faire des requêtes INSERT, sauf si ces requêtes sont un peu complexes (utilisation de fonctions SQL, requêtes imbriquées, etc.). L’avantage de tout faire via une seule méthode est de centraliser les appels. Le jour où il y a un traitement en particulier à faire sur certaines tables lors de l’insertion il est ainsi possible de le faire en surchargeant cette méthode via le système d’override de PrestaShop. Exemple fictif : $target = Tools::getValue('id'); $name = Tools::getValue('name'); Db::getInstance()->insert('target_table', array( 'id_target' => (int)$target, 'name' => pSQL($name), ));
L’appel à cette méthode produit la requête SQL suivante : INSERT INTO `prefix_target_table` (`id_target`, `name`) VALUES (10, 'myName')
Voici les arguments de cette méthode :
- $table : nom de la table (le préfixe sera automatiquement inséré devant, vous n’avez donc pas besoin de le préciser)
- $data : le tableau contenant les données à insérer, avec en clefs les noms de colonne et en valeurs les données à insérer
- $null_values : si “true”, alors les valeurs passées sous forme de NULL seront insérées de la même façon dans la base
- $use_cache : si “false”, désactive la gestion du cache SQL de PrestaShop pour cette requête (ne changez pas ce paramètre si vous ne savez pas précisément ce que vous faîtes)
- $type : peut prendre les constantes Db::INSERT, Db::INSERT_IGNORE ou Db::REPLACE si vous souhaitez changer le type d’insertion
- $add_prefix : si “false”, n’ajoutera pas automatiquement le préfixe de vos tables devant le nom de la table
Important : Veillez à toujours protéger vos données avant de les passer à insert(). Dans l’exemple, on s’assure que id_target soit un entier et que name soit protégé contre les injections SQL avec pSQL()
2. La méthode update($table, $data, $where = '', $limit = 0, $null_values = false, $use_cache = true, $add_prefix = true)
Cette méthode s’utilise de la même façon que la méthode insert(), mais pour les mises à jour de données (requêtes UPDATE). Les arguments étant principalement les même, voici juste une description des deux qui diffèrent :
- $where : prend la clause WHERE pour la mise à jour
- $limit : vous pouvez limiter le nombre de résultats que vous mettrez à jour
3. La méthode delete($table, $where = '', $limit = 0, $use_cache = true, $add_prefix = true)
Cette méthode est l’équivalent de insert() et update() en version DELETE. Elle est à utiliser pour les mêmes raisons. L’argument $limit permet de limiter le nombre d’enregistrements que l’on souhaite supprimer. L’autre avantage d’utiliser cette méthode est qu’elle sera utilisée par le système de cache des requêtes SQL de PrestaShop et effacera donc les requêtes concernées en cache sauf si l’argument $use_cache vaut false. Exemple : Db::getInstance()->delete(‘target_table’, ‘myField < 15’, 3);
va générer la requête DELETE FROM `prefix_target_table` WHERE myField < 15 LIMIT 3
4. La méthode execute($sql, $use_cache = 1)
Cette méthode exécute la requête SQL donnée. Elle n’est à utiliser que pour les requêtes en écriture (INSERT, UPDATE, DELETE, TRUNCATE…) car elle supprime de plus le cache de requêtes (sauf si l’argument $use_cache vaut false). Exemple : $sql = ‘DELETE FROM ‘._DB_PREFIX_.’product WHERE active = 0’; if (!Db::getInstance()->execute($sql)) die(‘Erreur etc.)’;
Important : Il est recommandé d’utiliser les méthodes insert(), update() et delete() autant que possible, n’utilisez execute() que si les requêtes sont trop complexes. Veuillez noter aussi que cette méthode retourne un boolean (true ou false), et non pas une ressource de base de donnée à utiliser.
5. La méthode query($sql,)
Toutes les méthodes de la classe exécutant des requêtes SQL utilisent cette méthode de plus bas niveau. Elle fait la même chose que la méthode execute() à deux exceptions prêt :
- Aucune gestion du cache dans cette méthode
- Elle ne retournera non pas un boolean mais une ressource que vous pourrez exploiter avec d’autres méthodes comme nextRow()
6. La méthode executeS($sql, $array = true, $use_cache = 1)
Cette méthode exécute la requête SQL donnée et charge l’ensemble des résultats qu’elle retourne dans un tableau multidimensionnel. Elle n’est à utiliser que pour les requêtes en lecture (SELECT, SHOW, etc.). Les résultats de cette requête seront mis en cache, sauf si l’argument $use_cache vaut false. Le second argument $array est déprécié et ne doit plus être utilisé, laissez-le à true. Exemple : $sql = ‘SELECT * FROM ‘._DB_PREFIX_.’shop’; if ($results = Db::getInstance()->ExecuteS($sql)) foreach ($results as $row) echo $row[‘id_shop’].’ :: ‘.$row[‘name’].’
’;
7. La méthode getRow($sql, $use_cache = 1)
Cette méthode exécute la requête SQL donnée et récupère la première ligne de résultats. Elle n’est à utiliser que pour les requêtes en lecture (SELECT, SHOW, etc.). Les résultats de cette requête seront mis en cache, sauf si l’argument $use_cache vaut false. Attention : cette méthode ajoute automatiquement une clause LIMIT à la requête. Veillez à ne pas en ajouter une vous-même manuellement. Exemple : $sql = ‘SELECT * FROM ‘._DB_PREFIX_.’shop WHERE id_shop = 42’; if ($row = Db::getInstance()->getRow($sql)) echo $row[‘id_shop’].’ :: ‘.$row[‘name’];
8. La méthode getValue($sql, $use_cache = 1)
Cette méthode exécute la requête SQL donnée et récupère uniquement le premier résultat de la première ligne. Elle n’est à utiliser que pour les requêtes en lecture (SELECT, SHOW, etc.). Les résultats de cette requête seront mis en cache, sauf si l’argument $use_cache vaut false. Attention : cette méthode ajoute automatiquement une clause LIMIT à la requête. Veillez à ne pas en ajouter une vous-même manuellement. Exemple : $sql = ‘SELECT COUNT(*) FROM ‘._DB_PREFIX_.’shop’; $totalShop = Db::getInstance()->getValue($sql);
9. La méthode NumRows()
Cette méthode met en cache et retourne le nombre de résultats de la dernière requête SQL. Attention : cette méthode n’est pas dépréciée mais il est fortement déconseillé de l’utiliser pour des raisons de bonnes pratiques. En effet, il vaut mieux récupérer le nombre de résultats via une requête de type SELECT COUNT(*) au préalable.
10. Quelques méthodes annexes
- Insert_ID() : retourne l’identifiant créé de la dernière requête INSERT exécutée
- Affected_Rows() : retourne le nombre de lignes concernées par la dernière requête UPDATE ou DELETE exécutée
- getMsgError() : retourne le dernier message d’erreur si une requête a échouée
- getNumberError() : retourne le dernier numéro d’erreur si une requête a échoué
11. Les changements entre la 1.4 et la 1.5
- Les méthodes autoExecute() et autoExecuteWithNullValues() sont dépréciées au profit de insert() et update()
- Le préfixe des tables n’est plus obligatoire pour la méthode delete()
- La méthode execute() ne retourne plus une ressource mais un boolean, utilisez query() pour obtenir une ressource
- Support de PDO et MySQLi
Discussions