Les tables personnalisées dans WordPress
Plus de 6 minutes 🙂
Introduction
Même si WordPress propose une structure de tables assez flexible et des API pour en profiter intéressantes, il est parfois nécessaire pour le développeur de créer ses propres tables. Parfois pour les performances, parfois par méconnaissance des API, nous allons voir comment créer une table simple dans WordPress et surtout de manière propre et rapide.
La classe $wpdb
WordPres propose une classe globale qui permet de faire des requêtes SQL très facilement et surtout de façon sécurisée si on s’en donne la peine. Elle permet surtout d’êtrte le plus flexible avec une installation WordPress qui aurait, par exemple, modifié le prefixe de ses tables. Si le développeur n’utilise pas la classe wpdb, toutes ses requêtes ne fonctionneront pas !
Dans classe $wpdb on retrouve tous les noms des tables qui sont natives à WordPress comme la table des métas ou celle des posts. Elle stocke le nom des tables déjà préfixées, par exemple :
[pastacode provider= »manual » lang= »php »]
global $wpdb;
echo $wpdb->postmeta;
[/pastacode]
Si vous utilisez ce bout de code, faites le uniquement en développement 😉.
Donc ce bout de code affiche le nom de la table postmeta de votre base et avec les préfixes que l’utilisateur aurait pu utiliser.
Cela permet de ne pas se soucier des préfixes de votre utilisateur et surtout que si un jour WordPress décide de changer le nom de la table en base que votre code continue à fonctionner sans problèmes et est maintenu entre les versions.
Et la sécurité ?
Faire des requêtes dans la base est assez problématique si vous le faites à partir d’informations entrées par votre utilisateur puisque certains sont mal intentionnés, et tentent de pirater votre site par cet intermédiaire. Si il tente de passer par le SQL cela s’appelle une attaque par injection SQL, il casse votre requête de base et en fait ce qu’il veut comme, par exemple, supprimer tous les utilisateurs ou toutes votre base etc…
Si votre plugin ou développement utilise des requêtes SQL personnalisées et que vous n’utilisez pas du tout la classe $wpdb, je vous encourage à le faire.
Prenons cet (horrible) exemple:
[pastacode provider= »manual » lang= »php »]global $wpdb;
$wpdb->query( « SELECT * FROM wp_posts WHERE ID=’$_GET[‘post_id’]' » );
[/pastacode]
Nous sommes dans le cas où il est possible de se faire pirater son site, votre variable qui est dans $_GET peut être tout et n’importe quoi, ça peut être tout autant un ID de post mais aussi des caractères qui voudraient casser la requête actuelle pour aller faire autre chose dans votre base. Deuxième problème on marque directement le nom de la table dans la requête : ‘wp_posts’ ce qui peut ne pas fonctionner.
Voici la manière de le faire le plus proprement possible :
[pastacode provider= »manual » lang= »php »]
global $wpdb;
$wpdb->get_results( $wpdb->prepare( « SELECT * FROM $wpdb->posts WHERE ID = ‘%d' », $_GET[‘post_id’] ) );
[/pastacode]
Dans cette requête j’utilise l’une des méthodes les plus importantes de la classe : prepare(). Elle permet de spécifier le type de données attendues dans les variables de notre requête. Par exemple ici j’ai mis ‘%d’ cela veut dire que j’attend un chiffre de la variable $_GET[‘post_id’]. Si vous avez plusieurs variables à faire passer dans votre requête vous avez deux choix :
- Mettre l’un après l’autre les variables que vous souhaitez tester
- Donner un tableau à prepare là ou se trouve $_GET [‘post_id’], et e mettant les données dans l’ordre de leur apparition.
Et les autres fonctions de wpdb ?
La classe wpdb permet de faire d’autres choses que get_results, par exemple si vous voulez récupérer une ligne entière, utilisez get_row. Si vous voulez récupérer une valeur unique utilisez get_var.
Dans tous les cas je vous invite à consulter la documentation de la classe wpdb du codex, bien d’autres choses vous y attendent.
Créer sa table
Etant donné que nous voulons créer une petite table personnalisée, il faut le faire le plus proprement possible et surtout en s’insérant le plus proprement dans l’installation de notre client. Comme dit plus tôt si votre client a utilisé une prefixe différent de celui que l’on trouve dans WordPress, il faut que vous puissiez utiliser cette donnée aussi !
Pour que tout se passe à merveille, la meilleure solution est de s’insérer dans WordPress et d’utiliser la classe $wpdb à votre avantage.
Déclarons dans la classe une nouvelle table ( attention cela ne la crée en aucun cas ) pour qu’elle puisse être utilisée dans nos requêtes.
[pastacode provider= »manual » lang= »php »]
global $wpdb;
$wpdb->ma_table_perso = $wpdb->prefix.’nom_de_ma_table_en_base’;
[/pastacode]
Maintenant dès que vous aurez à faire une requête dans votre base, il vous suffira de faire $wpdb->ma_table_perso ! Comme ça vous ne vous souciez plus du tout du prefix ou autre.
Par contre il faut faire attention aux nom que vous donnez à votre propriété dans $wpdb, il ne faudrait pas écraser un nom existant, donc préfixez votre nom.
Cet exemple doit s’insérer dans le fichier principal d’un plugin, pour qu’il soit bien pris en compte par la suite.
Dans le cas d’un plugin, on veut créer une table perso lors de l’activation de celui-ci et ne plus la recréer si l’utilisateur le désactive puis le réactive. Voici la manière de procéder que j’utilise :
[pastacode provider= »manual » lang= »php »]
register_activation_hook ( __FILE__, ‘my_plugin_activate’ );
function my_plugin_activate() {
global $wpdb;
if ( ! empty($wpdb->charset) )
$charset_collate = « DEFAULT CHARACTER SET $wpdb->charset »;
if ( ! empty($wpdb->collate) )
$charset_collate .= » COLLATE $wpdb->collate »;
// Add one library admin function for next function
require_once( ABSPATH . ‘wp-admin/includes/upgrade.php’ );
// Try to create the meta table
return maybe_create_table( $wpdb->ma_table_perso, « CREATE TABLE $wpdb->ma_table_perso(
`id` int(20) NOT NULL auto_increment,
`object_id` INT( 20 ) NOT NULL,
`user_id` INT( 20 ) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `object_ids` (`object_id`,`user_id`)
) $charset_collate; » );
}
[/pastacode]
Dans ce cas, nous utilisons la classe $wpdb au mieux, nous vérifions bien le charset et le collate si besoin, nous utilisons notre variable de classe pour l’utiliser dans notre requête.
Avec la fonction maybe_create_table, nous ne créons la table que si elle n’existe pas encore, cela nous permet de ne pas générer d’erreurs lors de l’activation de notre plugin.
Avec ça, la table est créée proprement et ne posera pas de problèmes, vous pourrez ainsi utiliser autant de fois que vous le voudrez la classe avec cette variable
Les tables multi-site
Il s’avère que dans le cas d’un multisite avec la méthode citée ci-dessus la table n’est créée que pour le site principal, donc quand les requêtes seront effectuées, elle le seront sur la table du site actuel mais elles n’existeront pas et donc rien n’en resortira.
Cela peut être utile si vous avez des données qui doivent être lues depuis les sous-sites sur le site principal et pas depuis sa table personnelle.
Si vous voulez que la table soit créée pour le site principal ainsi que sur les sous-sites, vous devez procéder de la manière suivante:
[pastacode provider= »manual » lang= »php »]
global $wpdb;
$wpdb->ma_table_perso = $wpdb->prefix.’nom_de_ma_table_en_base’;
$wpdb->tables[] = ‘ma_table_perso’;
[/pastacode]
De cette manière, vous créerez aussi cette table lors de l’activation de votre plugin sur le sous-site.
Il ne faut pas perdre de vue que créer des tables dans WordPress reste quand même assez rare et je ne vous conseille pas de le faire, utilisez au maximum les possibilités des API de WordPress et de sa structure de base de données qui est assez flexible.
Si vous utilisez des tables personnalisées, faites le avec le plus grand soin et surtout pensez à la sécurité et à limiter les actions de vos utilisateurs. La plupart du temps ce sont les plugins et non WordPress lui même qui a des failles de sécurité. 😉
ouèè un tuto sans failles et qui parle de sécu …
Merci et bravo
Nicolas, ton précédent post est inaccessible.
message d’erreur :
l’image https://blog.nicolas-juen.fr/2011/10/27/utiliser-lapi-endpoint-pour-creer-une-regle-de-reecriture-durl-simple/#comment-36 ne peut être affichée car elle contient des erreurs.
Effectivement lors de l’affichage de l’article sous firefox j’ai une erreur.. bizarre !
Je ne sais pas si ça vient de mon .htaccess ou d’autre part mais je vais aller voir, rafraîchir la page semble régler le problème…
Je vais faire ma petite investigation ^^
Merci ! 🙂
effectivement. Très étrange. ^^
Ce code est propre et bien sécurisé. Le tuto est vraiment bien conçu pour éviter d’utiliser la base de données n’importe comment dans WordPress. Merci pour l’info.
Petite question!
Concernant le bout de code:
« global $wpdb;
$wpdb->ma_table_perso = $wpdb->prefix.’nom_de_ma_table_en_base’; »
Dans quel fichier dont-on le mettre? functions.php? ou directement dans le fichier php du programme?
Merci d’avance.
Gwen
Bonjour,
Ce morceau de code se situe idéalement après la déclaration du plugin dans le fichier de base du plugin.
Le mettre dans le fichier functions.php fait que le site est totalement dépendant du thème et le changement de thème peut tout faire planter
Je débute avec wordpress et franchement bravo pour vos articles simple clair et très explicatif
Bonjour, merci pour cet article. En ce qui me concerne, j’aimerai créer un plugin qui va parcourir chaque jour le nombre d’article et insérer une valeur en base. Pour cela, il y a les champs personnalisés. Le problème est que ma variable ne dépend pas que de l’article, mais aussi du jour. Chaque jour, il y aura une nouvelle valeur et j’aimerai conserver l’historique. Je pense donc être contraint de créer une nouvelle table dans wordpress
Bonjour,
Si cet historique risque d’être très gros, une table peut être une solution. Sinon une option pourrait faire l’affaire, tout dépendra de la taille de l’historique 😉
Bonjour,
Merci pour ce tuto qui à l’air très intéressant, mais dans tous les exemples de code j’ai :
1
2 add_action( ‘wp_ajax_getPost’, ‘get_the_posts’ );
3
Que ce soit sous Firefox, Chrome, IE …
Oula.. Effectviement i l y a un problème, le plugin de code colorer sûrement… Je vais voir ça ! Merci 🙂
C’est corrigé 😉
Service plus que rapide !!!
Cool, je vais pouvoir bosser 🙂
Merci
De rien 🙂
Pile poil ce que je cherché. Merci pour le tuto efficace.
Juste une petit question, serait il possible d’avoir un exemple de bonne pratique pour les requetes Update, Insert, Delete ?
Histoire d’avoir un panel, concernant les possibilités de failles ou d’erreur d’insertion. Merci encore !
Salut ! Il suffit de regarder la documentation de wpdb 😉 : https://codex.wordpress.org/Class_Reference/wpdb
Merci !
J’avais lu jusqu’en bas du codex, sa m’apprendra 😉
Et j’ai trouver un truc sympas pour confirmer la réussite d’une requete.
if ( $wpdb->query( $ma_requete ) === false){
//error requete message
}else{
//succes requete message
}
et les methodes :
show_errors(); ?>
hide_errors(); ?>
Oui, et aussi les propriétés de l’object WPDB, telles que last_query, last_error etc.
Il faut aussi penser à activer la constante SAVEQUERIES en phase de développement 😉
Hi my friend! I want to say that this post is amazing, great written and include almost all significant infos. I’d like to see more posts like this. agddaeddbkkd
Bonjour, je m’amuse à coder dans une page wordpress en php.
J’arrive très bien à faire des requêtes avec ma table wp_users.
Or je n’arrive à rien avec une table personnalisé : alpha_test.
Je remarque que dans le code je fait : FROM $wpdb->users et non FROM $wpdb->wp_users
Avec ma table personnalisé je fait FROM $wpdb->alpha_test. Ce qui bien-sûr ne marche pas.
Vous aurez donc compris mon problème, comment faire pour utiliser une table créer par nos soins?
C’est un très bon article, très intéressant … Beau travail !
Bonjour,
merci pour cet article.
Je me lance aussi dans la création de plugins WordPress.
C’est vrai que WordPress utilise des fonctions bien spécifiques.
Cependant, quand j’utilise la fonction que vous avez donné, aucune table ne se créée.
J’ai désactivé le plugin, et je l’ai réactivé, mais rien n’y fait. La table : ma_table_perso ne se créée pas.
ma_table_perso = $wpdb->prefix. 'nom_de_ma_table_en_base';
register_activation_hook ( __FILE__, 'my_plugin_activate' );
function my_plugin_activate() {
global $wpdb;
if ( ! empty($wpdb->charset) )
$charset_collate = " DEFAULT CHARACTER SET $wpdb->charset";
if ( ! empty($wpdb->collate) )
$charset_collate .= " COLLATE $wpdb->collate ";
// Add one library admin function for next function
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
// Try to create the meta table
return maybe_create_table( $wpdb->ma_table_perso, " CREATE TABLE $wpdb->ma_table_perso(
'id' int(20) NOT NULL auto_increment,
'object_id' INT( 20 ) NOT NULL,
'user_id' INT( 20 ) NOT NULL,
PRIMARY KEY ('id'),
UNIQUE KEY 'object_ids' ('object_id','user_id')
) $charset_collate; " );
}
my_plugin_activate();