Les tables personnalisées dans WordPress

Introduction

Même si WordPress propose une structure de assez flexible et des API pour en profiter intéressantes, il est parfois nécessaire pour le développeur de créer ses propres . 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 :



global $wpdb;

echo $wpdb->postmeta;

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:



global $wpdb;

$wpdb->query( "SELECT * FROM wp_posts WHERE ID='$_GET['post_id']'" );

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 :



global $wpdb;

$wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE ID = '%d'", $_GET['post_id'] ) );

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 :

  1. Mettre l’un après l’autre les variables que vous souhaitez tester
  2. Donner un tableau à prepare là ou se trouve $_GET ['post_id'], et e mettant les données dans l’ordre de leur apparition.
Chose importante : Il n’y a pas que %d poiur caractériser ses données, vous pouvez utiliser celles de sprintf. WordPress les comprendra et les interprétera, si les variables ne correspondent pas aux attentes, prepare ne les utilisera pas dans sa query.

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.



global $wpdb;

$wpdb->ma_table_perso = $wpdb->prefix.'nom_de_ma_table_en_base';

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 :


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;" );
}

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 principal !

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:



global $wpdb;

$wpdb->ma_table_perso = $wpdb->prefix.'nom_de_ma_table_en_base';
$wpdb->tables[] = 'ma_table_perso';

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é. ;)
 

Share on Google+2Tweet about this on Twitter9Share on Facebook0Pin on Pinterest0

24 Responses

  1. BoiteaWeb 5 décembre 2011 / 22 h 24 min

    ouèè un tuto sans failles et qui parle de sécu …
    Merci et bravo

    • Nicolas 8 décembre 2011 / 12 h 34 min

      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 ! :)

      • Sébastien | WordpressDesigner 11 décembre 2011 / 15 h 13 min

        effectivement. Très étrange. ^^

  2. Tony 30 mars 2012 / 21 h 18 min

    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.

  3. Pingback: La classe wpdb
  4. Gwen 1 août 2013 / 13 h 05 min

    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

    • Nicolas 1 août 2013 / 14 h 36 min

      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

  5. FAVOREL 14 août 2013 / 20 h 34 min

    Je débute avec wordpress et franchement bravo pour vos articles simple clair et très explicatif

  6. Xavier 2 octobre 2013 / 16 h 03 min

    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

    • Nicolas 2 octobre 2013 / 16 h 21 min

      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 ;)

  7. Scargoll 20 novembre 2013 / 17 h 09 min

    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 …

    • Nicolas 20 novembre 2013 / 17 h 13 min

      Oula.. Effectviement i l y a un problème, le plugin de code colorer sûrement… Je vais voir ça ! Merci :)

  8. scargoll 20 novembre 2013 / 17 h 25 min

    Service plus que rapide !!!

    Cool, je vais pouvoir bosser :-)

    Merci

  9. julien 28 février 2014 / 11 h 18 min

    Pile poil ce que je cherché. Merci pour le tuto efficace.

  10. Julien 28 février 2014 / 13 h 20 min

    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 !

      • Julien 28 février 2014 / 13 h 34 min

        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(); ?>

        • Nicolas 28 février 2014 / 14 h 12 min

          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 ;)

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>