{{{Introduction}}}
Les champs extra sont une fonctionalité officieuse de Spip 1.7. Ils permettent de rajouter des champs aux éléments traditionnels de Spip sans avoir à modifier ni créer de nouvelles tables dans la base de donnée. Une fois définis, ces champs apparaissent dans la partie privée de Spip où ils peuvent être valorisés tout comme les autres champs.

Ensuite, l'exploitation des champs extra est faite à la manière Spip, c'est à dire avec des boucles et des balises. En revanche, la création de ces champs est spécifique. On peut au choix:

- passer par le PHP; cette méthode est expliquée dans le présent article.  Elle est relativement simple et n'exige pas de connaissance préalable du langage, mais elle reste manuelle,

- utiliser un outil graphique qui rend la chose simple et intuitive. Cet outil réalisée par Michaël Courcy, est décrit dans la contribution "[Interface graphique pour les extras->http://www.uzine.net/spip_contrib/ecrire/articles.php3?id_article=424]". Il nécessite une petite installation supplémentaire.

Une autre caractéristique de cette fonction extra est qu'elle offre la possibilité de limiter la portée de ces champs; par exemple en restreignant les champs extras d'articles à un secteur.

Cet article a pour but d'expliciter tous ces aspects; il se termine par un exposé sur la manière de saisir des champs extra depuis la partie publique; ceci pourra intéresser ceux qui ne sont pas rebutés par le PHP.

Attention : le mécanisme des extras est puissant mais on ne doit l'utiliser que si vraiment on exploite déjà tous les champs normaux de Spip, par ailleurs ceci reste un détournement d'utilisation qu'il faut gérer avec prudence, car c'est aussi un moyen de faire faire à SPIP ce pour quoi il n'est pas fait ! 

{{{Le principe général}}}
Seuls les éléments suivants peuvent avoir des champs extra: {{articles, brèves, auteurs, rubriques}}, et {{mot_clés}}.

A chacun de ces éléments correspond une table de la base de donnée pour laquelle SPIP a prévu un champ spécial (appelons le: champ-conteneur) qui sert à contenir tous les champs-extra qu'on aura défini pour cet élément. 

Comme on va le voir, cette définition de champs-extra est faite au moyen d'un tableau PHP. Celui ci  va être importé ou exporté du champ-conteneur par des opérations dites de "sérialisation". 

Note: Le champ-conteneur des extra est appelé lui-même extra (!) mais nous gardons l'appellation champ-conteneur pour éviter toute ambiguité. Ce champ-conteneur est un BLOB dans le jargon SQL; grace à lui, il n'est nul besoin de modifier le structure de la base de donnée puisqu'il permet de stocker tous les champs extra dont vous avez besoin, sans limitation de taille ni de nombre (mais attention aux performances!).

{{{Utilisation dans une boucle}}}
Supposons que nous voulons mettre l'âge des auteurs en base de donnée. Les champs extra étant faits pour ce genre d'utilisation, nous allons donc créer un nouveau champ dans la table auteur que l'on pourra nommer (après mûre réflexion) {{age}}. Sa valeur pourra alors être récupérée comme suit dans une boucle AUTEUR:

- (#EXTRA|age)
- (#EXTRA|extra(age))

Ces deux écritures sont équivalentes, mais la première est plus simple. Ici la balise #EXTRA représente le champ-conteneur dans la table auteur car nous sommes dans une boucle AUTEUR.

Etant donné que notre balise #EXTRA est quasiment inexploitable à l'état brut (c'est du PHP sérialisé), il faut lui appliquer un filtre pour extraire le champ extra qui nous interesse, à savoir l'âge de l'auteur; d'où l'écriture (#EXTRA|age).

{{{Création manuelle des champs extra}}}
Comme on l'a vu, il faut passer par un tableau PHP. Celui-ci se nomme {{champs_extra}} et il doit se trouver dans le fichier {{ecrire/mes options.php3}}. Ca donne quelque chose comme ceci:

<code>
<?php
$GLOBALS['champs_extra'] = Array (
   'auteurs' => Array (
        "sexe" => "select|clignotant|sexe|homme,femme",
        "age" => "ligne|propre|&Acirc;ge du capitaine",
        "biblio" => "bloc|propre|Bibliographie"
      ),

   'articles' => Array (
         "numero_suivi" => "auto_increment|typo|Numero de suivi",
         "financeur" => "ligne|propre|Nom du financeur",
         "jours_prestes" => "nombre|presente_jour|Jours préstés",
         "jours_vendus" => "nombre|presente_jour|Jours vendus",
          "montant_devis" => "nombre|presente_argent|Montant du devis",
          "montant_honoraire" => "nombre|presente_argent|Montant des honoraires",
          "visibilite" => "select|propre|Visibilité de la fiche|consultant,publique",
        )
    );
?>
</code>
<doc776|center>

Dans cet exemple, plutôt copieux, on a enrichi les tables "auteurs" et "articles" avec de nouveaux attributs, nos champs extra, qui se nomment sexe, age, biblio pour la table auteurs.

A droite du  => on trouve les propriétés qui servent à définir la manière dont le champ va être géré (ç'est à dire saisi et affiché) dans la partie privée de Spip.

Dans l'exemple, notre champ {{age}} sera saisi sous forme d'un champ d'une ligne puis filtré par la procédure "propre" avec le libellé "Age du capitaine" dans le formulaire de saisie. Les 2 dernières propriétés sont facultatives (faute de libellé, le nom même du champ sera utilisé).

<h3>Présentation du tableau champs_extra</h3>

Observons un exemple ou l'on utilise la structure d'un article spip pour supporter les informations d'un projet.

<img775|center>

Cette capture d'écran nous permet de comprendre que :

- extra est un tres gros tableau composé de sous_tableaux 

- ces sous tableaux correspondent respectivement aux éléments de spip : {{auteurs, articles, rubriques, mots clés}} et on peut définir des champs extra dans les lignes de ces sous-tableaux pour les éléments qui nous intéressent.

- si je rajoute un élément dans le sous_tableau article, par exemple jour_vendu, et bien les articles possederont  dans leur définition un champs supplémentaire jour_vendu, qu'on pourra remplir dans l'admin spip article. C'est le champ extra "jour_vendu".

- si je rajoute l'élément sexe dans le sous_tableau auteur, et bien le sexe sera un champs supplémentaire dans la définition d'un auteur, à renseigner dans l'admin dans les fiches auteurs.

Nous allons étudier dans le détail l'utilisation mes_options.php3 et des champs extra dans la partie publique comme dans la partie privée.

<h3>Anatomie d'un champs extra</h3>

Observons un élément du sous-tableau article

<img777|center>

- ligne : définit la façon dont sera construit le  champs de saisie dans l'interface privée c'est à dire dans articles_edit.php3.
<img779|center>

- propre : définit la façon dont sera présenté ce champs dans articles.php3
<img778|center>

- Nom du financeur : sera le commentaire placé devant le champs.
<img780|center>

- financeur : définit la variable pour pourvoir obtenir sa valeur dans un squelette : il faudra écrire ce code dans votre squelette.html 

<code>

<BOUCLE_article(ARTICLES)>
  .................
  .................
  [(#EXTRA|financeur)]
</BOUCLE_article>

</code>

{{{Gestion fine des extra}}}

On peut affiner l'affichage des champs extra définits comme précédemenent.

Observons une autre organisation du tableau des extras dans {{mes_options.php3}}, cette nouvelle configuration permet aux auteurs de disposer d'un {{avatar}} et du {{choix du format}} d'une newsletter. On rajoute aussi le champ {{isbn}} aux articles

<cadre>

<?php

//
// Definition de tous les extras possibles
//

	$GLOBALS['champs_extra'] = Array (

'auteurs' => Array (

      'avatar' =>'ligne|propre|Entrer l'URL de votre avatar',
      'abo' => 'radio|brut|Abonnement &agrave; lettre d'information|html|html,texte,non|Format Html,Format texte,Ne pas recevoir de courrier',

		    ),

'articles' => Array (

     'isbn' => 'ligne|typo|ISBN'
		    
                    )

		                          );
		
$GLOBALS['champs_extra_proposes'] = Array (

'auteurs' => Array (

// tous : par defaut
'tous' => 'abo',
// 6forum : les visiteurs
'6forum' => 'abo|avatar',
// inscription : groupe utile à l'inscription
'inscription' => 'abo',
		
                   ),

'articles' => Array (

// tous : par defaut
'tous' => '',
// 1 : id_secteur=1;
'1' => 'isbn'
		   )

                   );

?>

</cadre>

Vous remarquez un second tableau <code>$GLOBALS['champs_extra_proposes'] = ... </code> ; ce tableau permet optionnellement d'affiner les extras :
- pour les articles/rubriques/breves en fonction du secteur ;
- pour les auteurs en fonction du statut ou plus généralement d'un groupe arbitraire
- pour les mots-cles en fonction du groupe de mots

Grâce à ce second tableau, on affiche les extra qu'on veut où on veut, c'est à dire qu'on définit le contexte dans lequel chaque extra est valide et utilisé.

Ici les "visiteurs" ont en plus des autres auteurs la possibilité de choisir un avatar, et les articles du secteur 1 disposent d'un champs "isbn".

Il faut pour chaque sous-groupe définir l'ensemble des champs extra à afficher,  on voit dans l'exemple qu'il faut reprendre la définition "abo" pour les 6forum (visiteurs) bien qu'elle soit déjà précisée pour le groupe "tous"

{{{Comprendre les champs extra}}}

Attention ceci est réservé à un public qui connait un peu Spip et le PHP

{{{Comment fonctionnent les champs extra ?}}}

Ils utilisent le principe de la sérialisation

- {{La sérialisation}}

Cette technique permet de stocker des données de manière à s'en reservir par la suite.

Le processus de sérialisation existe dans tous les langages un tant soit peu moderne.

Il consiste à écrire une structure de donnée dans une variable ou dans la base de donnée ou dans un fichier : 

Un petit exemple valant mieux qu'un long discours, voici un fichier php de test et mise en évidence :

{test_serialisation.php}

<code>
<?php
$tableau=array(	 				                        "titi"=>"grosminet","laurel"=>"hardy","starky"=>"hutch"
);

//serialisation
$tableau_serialised=serialize($tableau);

//ouverture d'un fichier de sauvegarde
$fichier=fopen("sauvegarde","w");
//sauvegarde
fputs($fichier,$tableau_serialised);
fclose($fichier);
?> 
</code>

L'exécution de ce fichier sérialise un tableau complet et l'enregistre sous cette forme dans un fichier. Ouvrons ce fichier et voyons ce qu'il y a dedans

<code>
a:3:{s:4:"titi";s:9:"grosminet";s:6:"laurel";s:5:"hardy";s:6:"starky";s:5:"hutch";}
</code>

Pas très clair mais on s'en moque car php  s'y retrouve...Et on peut ainsi sauver des structures très complexes de données sur un fichier sans trop se poser de questions.

Maintenant imaginons qu'un autre script ait besoin de ce tableau voila ce que l'on peut faire pour le récupérer

<code>
<?
	$tableau=implode("",@file("sauvegarde"));
	$tableau=unserialize($tableau);
	
	//utilisation du tableau 
	echo $tableau['titi'];
	//ecrit grosminet 
?>
</code>

Et voila vous avez récupéré votre tableau flambant neuf pret à l'emploi.

Eh bien c'est exactement de cette manière là que marchent les extra : 

Ce sont des tableaux remplis avec tout ce qu'on veut, que Spip peut stocker tout seul dans sa base de donnée, pour ensuite les ressortir quand on le lui demande pour nous donner les extra qu'on avait rangé dedans.

{{{Afficher un groupe de saisie de champs extra choisis}}}

Si dans un squelette je souhaite afficher certains champs de saisie pour les champs extra j'appelle

<cadre>
include('ecrire/inc_presentation.php3');
include('ecrire/inc_extra.php3');
extra_saisie('', 'auteurs', 'inscription');
</cadre>

ici "inscription" correspond au groupe créé plus haut dans le tableau des auteurs et qui contient les différents champs à afficher

cette fonction affiche le bon formulaire en fonction de la définition du groupe de champs et du type de chacun des champs extra. (le premier paramètre est la valeur actuelle du champ extra si elle est renseignée).

Après la validation du formulaire, on recupère les valeurs choisis par l'utilisateur dans des variables du style :

<cadre>

$GLOBALS['suppl_XXX']

</cadre> 

ou XXX est le nom de notre extra, par exemple

<cadre>

<?php
$extra_selectionné = $GLOBALS['suppl_abo'];
?>

</cadre> 

{{{Remplir ou vider un champs extra en particulier}}}

Bon, on peut récupérer l'extra après qu'un utilisateur a choisi une valeur dans un formulaire mais encore faut il l'insérer dans la base de donnée !

Grâce aux fonctions {{set_extra()}} et  {{get_extra()}} de Beatnik on peut remplir puis réutiliser un champ extra en particulier (de l'auteur 1 seulement par exemple). 

- Le code php des fonctions

<cadre>

// Récupération des extra déjà remplis dans la base
// Retourne un tableau associatif.
// NB on retourne extra
// sous forme de tableau associatif
// Merci a beatnick

function get_extra ($id, $objet) {
	if(!$id) return false;
	// On construit qqch qui ressemble à "SELECT extra FROM spip_articles WHERE id_article=$id_article"
	$query = "SELECT statut, extra FROM spip_".$objet."s";
	$query .= " WHERE id_".$objet."=".$id;
	$res = spip_query($query);
	$cells = spip_fetch_array($res);
	
	if(!$cells["extra"]) return false ;
	$extra = unserialize ($cells["extra"]);
	return $extra;
}

// Enregistrement des extras après saisie d'un formulaire ou après un script php.
// On testera avec bonheur la valeur de retour
// de cette fonction lors de son appel
function set_extra ($id, $extra, $objet) {
	if(!$id) return false;
	$extra = addslashes(serialize($extra));
	// On construit qqch qui ressemble à "UPDATE spip_articles SET extra='$extra' WHERE id_article=$id_article"
	$query = "UPDATE spip_".$objet."s";
	$query .= " SET extra='$extra' ";
	$query .= " WHERE id_".$objet."=".$id;
	return spip_query($query);
}

</cadre>

-{{Comment ça marche ?}}

On peut par exemple remplir le champs extra "abo" du du sous groupe "auteurs" pour l'auteur 1.

Souvenez vous que le "abo" est un élément du tableau "auteurs" donc attention de bien remplir d'abord une variable de type tableau avec des cases correspondant aux extra du style :

<code>

$var_tab['extra_exemple1'] = $truc ;
$var_tab['extra_exemple2'] = $machin ;

</code>

puis on utilise le tableau $var_tab qui contient maintenant tous les éléments qu'il faut pour remplir les champs extra du tableau "auteurs" pour l'auteur 1, ce qui donne par exemple :

<cadre>
<?php
$extra_auteur['abo']= $GLOBALS['suppl_abo'];
set_extra(1,$extra_auteur,'auteur');
?>

</cadre>

Voyons enfin comment resortir la valeur de l'extra "abo" du tableau "auteurs" pour l'auteur 1. Ca peut servir pour un script php.

<cadre>
<?php
$extra=get_extra(1,'auteur'); //on récupère tous les extra de l'auteur 1
$abo = $extra['abo']; //on ne prend que 'abo'

echo $abo;

?>

</cadre>

(vous avez remarqué que les types d'extra sont au singulier (auteur)).

- {{Rappel}}

Si on veut juste afficher cette valeur dans un squelette, ce n'est pas la peine d'utiliser la fonction get_extra(), (ouf!), il suffit d'accéder à sa valeur au moyen des filtres appliqués à #EXTRA. Il y a 2 possibilités :

1) avec le filtre {extra} :

<cadre>
<BOUCLE_aut1(AUTEURS){id_auteur=1}>
[(#EXTRA|extra{abo})]
</BOUCLE_aut1>
</cadre>

2) en utilisant le nom de l'extra comme filtre pour y accéder :

<cadre>
<BOUCLE_aut1(AUTEURS){id_auteur=1}>
[(#EXTRA|abo)]
</BOUCLE_aut1>
</cadre>

*** Astuce pour les malins ***
_ 
Si on décide de ne pas déclarer un champ extra dans mes_options.php3, le champ en question ne sera pas affiché dans le site, mais rien n'empeche de le remplir quand même : On peut ainsi stocker des valeurs dans la base de donnée en toute discrétion sans avoir à créer de nouveaux champs ordinaires dans la base de donnée.
_ 
***