Ajouter un champ dans une table SPIP

mercredi 24 décembre 2008 // Semences SPIP

Petit tutoriel pour ajouter un champ SQL dans une table SPIP existante. Nous découvrirons par cet exemple quelques points d’entrées forts utiles pour SPIP.

Ce soir, peu avant Noël, nous remarquons que nous avons un besoin urgent d’un champ ’PS’ sur une des rubriques de notre site. Eh oui ! Nous voulons dire : « PS : Cher Père Noël, n’oublie pas l’équipe SPIP ! »

Bien... Mais comme en plus, vous pensez à vos copains, vous dites : « si j’en fais un plugin, ça pourra servir à d’autres ! » Là, vous marquez un bon point. Encore faut-il y arriver. Commençons donc simplement.

Créer le plugin

Un plugin, c’est rien d’autre qu’un dossier (au sens dossier squelettes de SPIP) avec un fichier « plugin.xml » dedans. Jusque là, tout va bien. Créons ce dossier (plugins/post_scriptum/) avec ce premier fichier (plugin.xml) dedans contenant :

  1. <plugin>
  2.         <nom>Post Scriptum sur les Rubriques</nom>
  3.         <auteur>
  4.         Vous, bien s&ucirc;r, en &eacute;chappant les accents !
  5.         </auteur>
  6.         <licence>GNU/GLP</licence>
  7.         <version>0.1</version>
  8.         <description>
  9.         Ajoute un champ "ps" sur les rubriques de SPIP.
  10.         </description>
  11.         <etat>dev</etat>
  12.         <prefix>postscriptum</prefix>
  13.         <necessite id="SPIP" version="[2.0;]" />
  14. </plugin>

Ce fichier XML contient donc quelques informations sur votre plugin : son nom, son auteur, sa licence, son identifiant, sa stabilité (dev (développement), test (en test), stable), sa description et d’éventuelles dépendances : ici, nous affirmons que nous devons posséder SPIP 2.0.

Il nous faudra ajouter quelques informations dans ce fichier au fil de l’avancement, mais à ce stade, vous pouvez déjà voir la présence de ce plugin dans la page ecrire/?exec=admin_plugin.

Déclarer le champ SQL

Pour ajouter le champ SQL dans la table ’spip_rubriques’ il faut 2 choses : premièrement, déclarer son existence, et deuxièmement, expliquer à SPIP qu’il doit le créer à l’installation du plugin (et le supprimer à la désinstallation complète du plugin aussi !).

Prenons les éléments dans l’ordre et commençons par déclarer la présence du champ SQL. Pour cela, nous allons créer un répertoire dans notre plugin nommé base qui va contenir les informations relatives à la base de donnée.

Dedans, nous créons un premier fichier nommé postscriptum.php (on nommera de préférence les fichiers comme le préfixe du plugin) qui va contenir la définition du champ dans une fonction précise :

  1. <?php
  2. if (!defined("_ECRIRE_INC_VERSION")) return;
  3. function postscriptum_declarer_tables_principales($tables_principales){
  4.         // Extension de la table rubriques
  5.         $tables_principales['spip_rubriques']['field']['ps'] = "text DEFAULT '' NOT NULL";     
  6.         return $tables_principales;
  7. }
  8. ?>

La fonction postscriptum_declarer_tables_principales est une fonction crée pour un point d’entrée de SPIP, le pipeline « declarer_tables_principales » servant donc à déclarer, définir, les tables dites principales de SPIP.

D’autres tables dites « auxiliaires » peuvent aussi être déclarées via le pipeline « declarer_tables_auxiliaires » exactement de la même façon. Les tables auxiliaires sont souvent des tables dites « de liaison », c’est à dire servant à lier des informations de 2 tables principales (exemple : « spip_mots_articles » pour lier « spip_mots » et « spip_articles », ou « spip_documents_liens » pour lier « spip_documents » à « spip_xx »).

Pour revenir au code de la fonction, nous disons simplement qu’il existe un champs (tableau ’field’) nommé ’ps’ de type ’text’.

Pour certains champs ajoutés, ce n’est pas le cas ici, qui servent très souvent aux requêtes de sélections SQL (via des boucles SPIP par exemple), il peut être judicieux de poser un index sur le champ, permettant aux gestionnaires de base de donnée d’être plus rapide (mais la table prend alors plus de place). Voici un exemple extrait du plugin « openID » faisant cela :

  1. <?php
  2. if (!defined("_ECRIRE_INC_VERSION")) return;
  3. function openid_declarer_tables_principales($tables_principales){
  4.         // Extension de la table auteurs
  5.         $tables_principales['spip_auteurs']['field']['openid'] = "text DEFAULT '' NOT NULL";
  6.         $tables_principales['spip_auteurs']['key']['KEY openid'] = "openid";
  7.         return $tables_principales;
  8. }
  9. ?>

Pour terminer la prise en compte de ce champs par SPIP, il faut expliquer à SPIP que notre plugin utilise le pipeline « declarer_table_principales ». Pour cela, nous allons ajouter dans notre fichier « plugin.xml » les informations suivantes, avant </plugin> :

  1. <pipeline>
  2.         <nom>declarer_tables_principales</nom>
  3.         <inclure>base/postscriptum.php</inclure>
  4. </pipeline>

Installer le champ SQL

Nous allons maintenant définir une installation automatique de ce champs à l’installation du plugin. Pour cela, nous allons faire appel à un fichier base/postscriptum_install.php qu’il faudra créer.

Comme les pipelines, nous informons SPIP de la présence d’une méthode d’installation dans le fichier plugin.xml en ajoutant 2 champs : <install> et <version_base>, ce dernier définissant une version de la structure de la base de donnée du plugin. Cela sert à l’installation ou à la mise à jour du plugin (si la structure évolue un jour, par exemple pour renommer le champ ’ps’ en ’chapo’ ou je ne sais quoi).

  1. <version_base>0.1</version_base>
  2. <install>base/postscriptum_install.php</install>

Dans le fichier d’installation (postscriptum_install.php), nous allons maintenant déclarer 2 fonctions : une pour installer, une pour désinstaller.

  1. <?php
  2. if (!defined("_ECRIRE_INC_VERSION")) return;
  3. function postscriptum_upgrade($nom_meta_base_version,$version_cible){
  4.         $current_version = 0.0;
  5.         if ((!isset($GLOBALS['meta'][$nom_meta_base_version]))
  6.         || (($current_version = $GLOBALS['meta'][$nom_meta_base_version])!=$version_cible)){
  7.                 include_spip('base/postscriptum');
  8.                 // cas d'une installation
  9.                 if ($current_version==0.0){
  10.                         include_spip('base/create');
  11.                         maj_tables('spip_rubriques');
  12.                         ecrire_meta($nom_meta_base_version, $current_version=$version_cible, 'non');
  13.                 }
  14.         }
  15. }
  16. function postscriptum_vider_tables($nom_meta_base_version) {
  17.         sql_alter("TABLE spip_rubriques DROP ps");
  18.         effacer_meta($nom_meta_base_version);
  19. }
  20. ?>

upgrade()

Détaillons un peu ce qu’elles font et comment. La première, xx_upgrade() prend 2 arguments qui sont automatiquement transmis par SPIP : le nom de la variable contenant (ou devant contenir) la valeur actuelle de la version de la base de donnée du plugin installée sur le SPIP en cours. Le second paramètre contient la version de la base de donnée fournie par l’entrée « version_base » du fichier plugin.xml.

Un premier test indique que si la version cible est différente de la version actuelle, alors on a des choses à faire. Un second test à l’intérieur, définit les différentes actions en fonction de la version actuelle de la base. Pour l’instant, notre base n’a jamais été installée, nous devons donc simplement créer les champs manquant.

Pour cela, nous utilisons une fonction prévue par SPIP, maj_tables(); qui met à jour toutes les tables ou seulement la table dont le nom est transmis : les champs manquants sont alors automatiquement créés. Pour notre cas, le champs ’ps’ va se créer.

Enfin, la fonction ecrire_meta() met à jour la valeur de la version de la base du plugin installé dans le SPIP.

vider_table()

La suppression se passe aussi en deux temps dans une fonction xx_vider_table() : on supprime les champs utilisés par le plugin, puis l’information de version de la base du plugin.

La fonction sql_alter("TABLE spip_rubriques DROP ps"); permet de supprimer le champ SQL ’ps’ de la table ’spip_rubriques’.

Permettre aux rédacteurs de modifier le nouveau champ

A ce stade là, le plugin ajoute et supprime le champs PS lors de l’installation ou de la suppression complète du plugin. Les boucles RUBRIQUES peuvent alors utiliser #PS.

Cependant, pour le moment, le nouveau champs ne s’affiche pas dans l’interface de rédaction. Nous allons résoudre ce petit problème en deux temps ! D’abord, nous ajoutons la possibilité de saisie dans le formulaire d’édition de rubrique, ensuite, nous prenons en compte la saisie du rédacteur dans l’enregistrement fait par SPIP.

Nous allons utiliser deux pipelines pour cela : « editer_contenu_objet » et « pre_edition ».

Ajouter le champ de saisie

Pour ajouter le champ de saisie, nous allons ajouter le résultat de la compilation d’un petit squelette à un endroit précis du formulaire de saisie prive/formulaires/editer_rubrique.html.

Prenons exemple sur le champ PS du formulaire d’édition d’article, et créons un dossier et un fichier formulaires/inc-rubriques-ps.html contenant :

  1. <li class="editer_ps[ (#ENV**{erreurs}|table_valeur{ps}|oui)erreur]">
  2.         <label for="ps"><:info_post_scriptum:></label>[
  3.         <span class='erreur_message'>(#ENV**{erreurs}|table_valeur{ps})</span>
  4.         ]<textarea name='ps' id='ps'[ lang='(#LANG)'] rows='5' cols='40'>[(#ENV**{ps})]</textarea>
  5. </li>

Cette structure HTML est une base standard de tous les formulaires d’édition SPIP actuels.

Nous allons maintenant indiquer à SPIP d’ajouter le résultat de la compilation de ce squelette dans le formulaire d’édition de rubrique. On déclare donc l’utilisation du pipeline editer_contenu_objet dans son fichier plugin.xml et on en profite pour ajouter le pipeline qui servira tout à l’heure : pre_edition.

  1. <pipeline>
  2.         <nom>editer_contenu_objet</nom>
  3.         <inclure>postscriptum_pipelines.php</inclure>
  4. </pipeline>    
  5. <pipeline>
  6.         <nom>pre_edition</nom>
  7.         <inclure>postscriptum_pipelines.php</inclure>
  8. </pipeline>

Et oui ! Vous l’avez compris, il faut créer le fichier postscriptum_pipelines.php. Commençons par lui mettre ce contenu :

  1. <?php
  2. if (!defined("_ECRIRE_INC_VERSION")) return;
  3. // ajouter un champ ps sur le formulaire CVT editer_rubrique
  4. function postscriptum_editer_contenu_objet($flux){
  5.         if ($flux['args']['type']=='rubrique') {
  6.                 $flux['args']['contexte']['ps'] = sql_getfetsel('ps','spip_rubriques','id_rubrique='.sql_quote($flux['args']['contexte']['id_rubrique']));
  7.                 $ps = recuperer_fond('formulaires/inc-rubriques-ps', $flux['args']['contexte']);
  8.                 $flux['data'] = preg_replace('%(<!--extra-->)%is', $ps."\n".'$1', $flux['data']);
  9.         }
  10.         return $flux;
  11. }
  12. ?>

Ce pipeline est appelé pour tous les formulaires d’édition de SPIP. Ici, on regarde si le formulaire concerne les rubriques. Si c’est le cas, on récupère la valeur du champs ’ps’, en exécutant une requête SQL. Ce résultat est ajouté au contexte par la même occasion. On utilise ensuite la fonction magique recuperer_fond() pour obtenir le résultat de la compilation du squelette « inc-rubriques-ps » avec le contexte en cours. Ce résultat est stocké dans la variable $ps. Enfin, on place le contenu de $ps juste avant le texte "<!--extra-->" du formulaire d’édition en cours.

On aurait pu placer le ps en ciblant plus le lieu. Par exemple, le plugin « openID » place le champ openid dans un formulaire auteur juste après le champ email de la sorte :

  1. $openid = recuperer_fond('formulaires/inc-openid', $flux['args']['contexte']);
  2. $flux['data'] = preg_replace('%(<li class="editer_email(.*?)</li>)%is', '$1'."\n".$openid, $flux['data']);

Prendre en compte l’enregistrement

A ce moment là de l’histoire, le champ ps est bien présent dans la table et dans le formulaire... mais nous avons beau valider... Rien n’est sauvé. Là encore, il faut le dire à SPIP et utiliser le pipeline « pre_edition » que nous avons déjà déclaré.

Ajoutons simplement la fonction dans notre fichier « postscriptum_pipelines.php » de la sorte :

  1. // ajouter le ps soumis soumis lors de la soumission du formulaire CVT editer_rubrique
  2. function postscriptum_pre_edition($flux){
  3.         if ($flux['args']['table']=='spip_rubriques') {
  4.                 if ($ps = _request('ps')) {
  5.                         $flux['data']['ps'] = corriger_caracteres($ps);
  6.                 }
  7.         }
  8.         return $flux;
  9. }

Et voir le résultat !!!!

C’est bien beau, le ps s’enregistre, il s’affiche bien dans le formulaire, mais on ne le voit pas sur la page ecrire/?exec=naviguer de la rubrique ? Bien oui ! C’est encore autre chose ! Il faut encore utiliser un pipeline nouveau : « afficher_contenu_objet ». Vous connaissez le principe maintenant. On ajoute donc dans plugin.xml :

  1. <pipeline>
  2.         <nom>afficher_contenu_objet</nom>
  3.         <inclure>postscriptum_pipelines.php</inclure>
  4. </pipeline>    

On se crée un squelette avec ce que l’on souhaite voir s’afficher en plus, c’est à dire le PS de la rubrique, dans le fichier prive/contenu/inc-rubriques-ps.html :

  1. <BOUCLE_rub(RUBRIQUES){id_rubrique}{statut==.*}>
  2. <div class="#EDIT{ps} ps"><strong><:info_post_scriptum:></strong> #PS</div>
  3. </BOUCLE_rub>

Enfin, on ajoute le résultat du squelette grâce au pipeline sus-mentionné, en ajoutant la fonction qui va bien :

  1. // ajouter le champ ps sur la visualisation des rubriques
  2. function postscriptum_afficher_contenu_objet($flux){
  3.         if ($flux['args']['type']=='rubrique') {
  4.                 $ps = recuperer_fond('prive/contenu/inc-rubriques-ps', $flux['args']['contexte']);
  5.                 $flux['data'] .= "\n".$ps;
  6.         }
  7.         return $flux;
  8. }

Pour tester, il faudra certainement recalculer votre page en cours (exemple : ecrire/?exec=naviguer&id_rubrique=26&var_mode=recalcul).

Et c’est tout !

Comment ça c’est long ? Mais non, je vous assure que non ! La preuve, le plugin zippé est joint à l’article, il n’a presque rien, et grâce à lui, vous saurez créer un plugin pour ajouter les champs qui vous sont nécessaires...

Documents joints

17 commentaires


Vos commentaires :

  1. Le 26 décembre 2008 à 18:43, par marjorie

    merci pour cet article vraiment très très très utile (et très bien expliqué !

  2. Le 29 décembre 2008 à 13:46, par Bernard

    Excellent tuto comme tous ceux que j’ai pu lire sur ce site !

    Une référence, c’est évident !

    Bravo et merci..

    Juste une question : dans la boucle rubrique du inc on voit : statut==.* ceci je suppose veut dire tous les statuts ?

  3. Le 29 décembre 2008 à 17:36, par chryjs

    il y a un "openid_" qui traine dans les sources...

    Excellent !

  4. Le 29 décembre 2008 à 17:37, par chryjs

    Forcément j’ai mal lu :-)

  5. Le 29 décembre 2008 à 17:45, par Matthieu Marcillaud

    @Bernard : oui, exactement, {statut==.*} signifie tous les status, et en plus, comme SPIP est malin, quand il voit : {champ==.*} , plutôt que d’écrire la ligne SQL WHERE champ REGEXP '.*' (ou quelque chose comme ça), il ne met rien du tout, ce qui revient au même puisqu’on demande à tous les voir. En fait, les boucles, du moins articles et rubriques, sont filtrées par défaut par statut. Ca sert ici à enlever ce filtre.

    @Chryjs : c’est pour ça qu’il est écrit juste avant :

    Voici un exemple extrait du plugin « openID » faisant cela

  6. Le 7 janvier 2009 à 07:16, par Jean-Pierre

    Explications très claires et très pédagogiques. Ce site va devenir incontournable sous peu et SPIP 2.0 en a besoin.
    Merci pour ce cadeau de Noël que je découvre aujourd’hui.
    Allez je me mets au travail.

  7. Le 8 janvier 2009 à 17:18, par Michel

    Merci pour le tutoriel que j’essaie de comprendre... Je n’ai jamais eu l’occasion de faire un plug-in et j’aimerais bien apprendre.

    Une remarque/question : lorsque l’on désactive le plug-in, le champs postscriptum_base_version de la table spip_meta n’est-il pas censé être vidé ou effacé de la base SQL ? J’ai testé et le champs ne change pas.
    Le champs ps de la table spip_rubriques existe toujours également dans la base SQL.

    D’autre part, j’ai essayé de modifier le plug-in pour ajouter un champs "adresse" non pas dans les rubriques, mais dans les articles. Pour cela, en gros, j’ai remplacé les mentions "postscriptum" par "adresse" un peu partout, dans tous les fichiers (que j’ai eux-même renommés), et les mentions "rubriques" par "articles".

    Cependant, cela ne fonctionne pas vraiment. J’ai bien les champs adresse dans la table spip_articles et annuaire_base_version dans la table spip_meta, et j’ai aussi le champs dans le formulaire de saisie. Mais, lorsque je modifie le champs les modifications ne sont pas prises en compte et le champs adresse ne s’affiche pas dans la "fiche" de l’article (par exemple ?exec=articles&id_article=1).

    Qu’ai-je bien pu oublier... ?

  8. Le 8 janvier 2009 à 17:29, par Matthieu Marcillaud

    @michel : Il y a une différence entre "désactiver" le plugin (le décocher et valider) et le "désinstaller" (cliquer dessus pour avoir son descriptif puis cliquer l’icône en vas à droite (un carton je crois) pour le désinstaller).

    Seule la désinstallation supprime les champs et les informations méta.


    Lorsque vous modifiez un plugin qui est déjà actif, l’installation ne vas pas se refaire, par conséquent, votre champ ’adresse’ de la table spip_articles risque de ne pas se créer. Il faut alors désinstaller le plugin, puis le réactiver. Par ailleurs, il est parfois obligatoire de supprimer les fichiers tmp/charger_*.php lorsqu’on modifie un plugin actif. Cependant, se rendre sur la page ?exec=admin_plugin doit en théorie recréer ces fichiers...

    Enfin, regardez bien vos fichiers tmp/*.log qui ont parfois des indications des problèmes survenus (il ne faut pas hésiter à les supprimer), comme par exemple mysql.log pour voir les erreurs SQL

  9. Le 9 janvier 2009 à 09:56, par Eric Leroy

    Merci beaucoup pour ce didacticiel. Il m’a aidé à contourner un problème important pour mon site qui est passé sous spip 2.0.2. J’ai adapté votre plugin à mon problème, ajouter un champ lieu aux articles (j’utilisais le plugin champ_homonyme pour ajouter ce champ sous spip 1.9.2e.).

    Ce nouveau plugin m’a donc permis de retrouver le champ et de l’éditer dans mes articles.

    Petite question : Est-ce que ce champ sera sauvé lorsque je sauvegarde la base ?

  10. Le 12 janvier 2009 à 09:29, par Michel

    Merci pour ces infos, Matthieu.
    Effectivement, je ne faisais que désactiver le plug-in.
    Je m’en vais de ce pas faire de nouveaux essais.

  11. Le 14 janvier 2009 à 09:21, par Michel

    J’ai progressé même si je n’ai pas encore tout résolu, mais je vais m’appuyer sur les gens de l’apéro Spip de mon coin pour aller plus loin.
    Grand merci en tous cas Matthieu !

  12. Le 18 janvier 2009 à 13:02, par Gérard

    Bonjour Matthieu,

    Ce tuto est en effet très bien fait ; un grand merci ! J’essaie pour ma part de l’adapter à l’ajout d’un nouveau champ au niveau des documents. J’avoue qu’au niveau de l’ajout je suis un peu dans le flou car je n’ai pas trouvé de formulaire équivalent à editer_rubrique. Mais pour l’instant mon problème est ailleurs, étant sous 1.9.2g, l’activation du plugin me retourne l’erreur suivante : function maj_tables() undefined. Cette fonction n’est elle apparue que depuis la version 2 ?

  13. Le 18 janvier 2009 à 13:19, par Matthieu Marcillaud

    Je rappelle que ces tutoriaux sont pour SPIP 2.0 d’une part, et que d’autre part, un plugin Champs Extras 2 a été créé suite à cela : http://www.spip-contrib.net/Champs-... .

    Enfin, cette façon de faire ne peut fonctionner qu’avec les objets respectant un certain mode de fonctionnement. Ce n’est pas encore le cas des documents ou des forums par exemple. Peut être dans la prochaine version de SPIP.

  14. Le 18 janvier 2009 à 16:20, par Gérard

    Merci pour cette réponse rapide ! J’avais vu en effet que cette solution s’adressait à spip 2 mais ne pouvant changer de version pour l’instant j’ai quand-même tenté le coup ! Dommage qu’elle ne soit pas indépendante de la version, mais c’est aussi le propre des majs d’offrir de nouvelles possibilités...

    Ceci-dit, de la même façon que l’on déclare un champ titre ou descriptif pour un document il doit être possible de lui définir un nouveau champ, non ? Reste à trouver la façon de procéder, qui ne sera pas forcément aussi pratique et automatique qu’un plugin (tout le mérite de ce tuto). Je vais donc poursuivre mes efforts et ne manquerai pas de faire une contrib si je trouve une solution. Connaissant mes limites ça risque de prendre du temps ;) Heu, s’il y a de bonnes raisons de dire que ce ne sera pas réalisable, je suis preneur ! Autant arrêter tout de suite de chercher pour rien.

    Encore merci, Gérard

    P.S.

    cette façon de faire ne peut fonctionner qu’avec les objets respectant un certain mode de fonctionnement

    Ce n’est pas tout à fait ce que sous-entend le titre de l’article mais voilà qui est rectifié...

  15. Le 23 janvier 2009 à 22:04, par FX

    très clair, indispensable

  16. Le 20 août 2009 à 20:40, par Loiseau2nuit

    OK alors méthode parfaite, comme toujours. Je crois que je ne louerai jamais assez tes capacités didactiques (et non je n’en fais pas trop ! Quoique... :P )

    En revanche, j’ai un soucis : lorsque j’édite ma rubrique, et que je donne une valeur au champ, je valide et là pouf ! j’ai un comportement bizarre : le navigateur bloque sur /ecrire/ ?exec=rubriques_edit&retour=nav
    et ca m’affiche un message : "Si votre navigateur n’est pas redirigé, cliquez ici pour continuer."

    le truc c’est que ca ne recharge rien du tout (bon, le lien pointe au bon endroit, c’est déjà ça)

    Une idée ?
    Merci d’avance.

  17. Le 1er septembre 2009 à 17:49, par rudy

    Super tuto ! De bonnes explications et tout et tout ... Vraiment bien !
    Mais j’ai une petite question : si au lieu d’ajouter un champs dans ma table spip_rubriques, j’avais voulu créer une Table ’PS’ : comment ça se passerai ?


Ajoutez votre commentaire

Ajoutez votre commentaire
Qui êtes-vous ? (optionnel)

Pour afficher votre trombine avec votre message, enregistrez-la d'abord sur gravatar.com (gratuit et indolore). Postez ensuite votre message ici, sans oublier d'indiquer votre adresse e-mail (non publiée).



Site réalisé avec SPIP | © 2001-2010 marcimat. Ma Graine .Net | Archives