# add_meta_box : des métaboxes propres pour l’éditeur classique et Gutenberg

> Un champ « référence fournisseur » à ajouter sur une fiche produit, mais l'éditeur de blocs vient de sortir : comment déclarer une métabox qui survit aux deux mondes ?

- Auteur : Clément Hadrot
- Publié le : 2020-02-10
- Mis à jour le : 2020-02-10
- Catégorie : Extensions
- URL : https://wpmoderne.dev.wordpress-developpement.fr/extensions/add-meta-box-metaboxes-classique-gutenberg/

## L’essentiel

- add_meta_box s'accroche au hook add_meta_boxes
- La sauvegarde passe par save_post avec vérification du nonce
- show_in_rest et custom-fields conditionnent la visibilité dans Gutenberg

Une agence de voyages m'a demandé d'ajouter un champ « code fournisseur interne » sur ses fiches de séjours, un Custom Post Type maison nommé `sejour`. Rien de spectaculaire, sauf que le site venait tout juste de migrer vers l'éditeur de blocs, et que la première version de ma métabox refusait obstinément de s'afficher dans la nouvelle interface.

Voici comment déclarer, sauvegarder et rendre compatible avec Gutenberg une métabox classique, sans dupliquer le code entre les deux éditeurs.

## Déclarer la métabox

Tout commence par le hook `add_meta_boxes`, qui s'exécute après que WordPress a déterminé l'écran d'édition courant. On y appelle `add_meta_box()` en précisant l'écran cible, ici le CPT `sejour`.

```
add_action( 'add_meta_boxes', 'voyages_ajouter_metabox_fournisseur' );

function voyages_ajouter_metabox_fournisseur() {
    add_meta_box(
        'voyages_fournisseur',
        'Fournisseur',
        'voyages_afficher_metabox_fournisseur',
        'sejour',
        'side',
        'default'
    );
}

function voyages_afficher_metabox_fournisseur( $post ) {
    wp_nonce_field( 'voyages_fournisseur_nonce', 'voyages_fournisseur_nonce_field' );
    $code = get_post_meta( $post->ID, '_voyages_code_fournisseur', true );
    echo '<label for="voyages_code_fournisseur">Code fournisseur</label>';
    echo '<input type="text" id="voyages_code_fournisseur" name="voyages_code_fournisseur" value="' . esc_attr( $code ) . '" class="widefat" />';
}
```

Le champ `$screen` (ici `'sejour'`) peut aussi être un tableau d'écrans, ou une chaîne générique comme `'post'` pour cibler tous les types standards. La position (`'side'`, `'normal'`, `'advanced'`) et la priorité influencent seulement l'ordre d'affichage, jamais le comportement.

## Sauvegarder les champs sans casser l'autosave

La sauvegarde se fait sur le hook `save_post`, mais ce hook se déclenche dans des contextes qu'il faut savoir écarter : une sauvegarde automatique, une révision, ou une requête sans les droits suffisants.

> L'essentiel à retenir : add_meta_box s'accroche au hook add_meta_boxes ; La sauvegarde passe par save_post avec vérification du nonce ; show_in_rest et custom-fields conditionnent la visibilité dans Gutenberg

```
add_action( 'save_post_sejour', 'voyages_sauvegarder_metabox_fournisseur' );

function voyages_sauvegarder_metabox_fournisseur( $post_id ) {
    if ( ! isset( $_POST['voyages_fournisseur_nonce_field'] ) ) {
        return;
    }
    if ( ! wp_verify_nonce( $_POST['voyages_fournisseur_nonce_field'], 'voyages_fournisseur_nonce' ) ) {
        return;
    }
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }
    if ( ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    if ( isset( $_POST['voyages_code_fournisseur'] ) ) {
        update_post_meta(
            $post_id,
            '_voyages_code_fournisseur',
            sanitize_text_field( wp_unslash( $_POST['voyages_code_fournisseur'] ) )
        );
    }
}
```

Utiliser `save_post_sejour` plutôt que le générique `save_post` évite d'exécuter la fonction pour chaque article ou page du site, un gain de performance qui passe facilement inaperçu sur un site à faible trafic mais qui compte sur un catalogue de plusieurs milliers de fiches.

## Pourquoi la métabox disparaissait sous Gutenberg

Le vrai problème, dans mon cas, ne venait pas de la métabox elle-même : elle s'affichait bien, mais dans un panneau repliable en bas de l'écran, loin d'où l'équipe éditoriale s'attendait à la trouver. C'est le comportement normal de l'éditeur de blocs pour les métaboxes classiques : elles sont automatiquement déplacées dans une zone dédiée nommée « Options de l'écran ».

Deux options s'offraient à moi : accepter ce placement, ou migrer le champ vers un panneau natif du `PluginDocumentSettingPanel` côté JavaScript. Pour un champ aussi simple, la première option restait la plus rentable en temps de développement.

- Une métabox classique continue de fonctionner sous Gutenberg sans modification, mais change de position visuelle
- Le paramètre `'__back_compat_meta_box'` peut forcer son affichage dans la colonne latérale plutôt qu'en bas de page
- Pour un champ destiné à apparaître dans l'inspecteur natif de blocs, il faut passer par la Block Editor API en JavaScript, une autre approche

## Rendre le champ disponible dans l'API REST si besoin

Si ce champ doit un jour être lu ou modifié par une application externe ou par un bloc dynamique, il faut le déclarer explicitement avec `register_post_meta()` en précisant `show_in_rest` :

```
add_action( 'init', function () {
    register_post_meta( 'sejour', '_voyages_code_fournisseur', array(
        'type'         => 'string',
        'single'       => true,
        'show_in_rest' => true,
        'auth_callback' => function () {
            return current_user_can( 'edit_posts' );
        },
    ) );
} );
```

Sans cette déclaration, le champ reste invisible pour l'API REST, même s'il existe et se sauvegarde correctement via la métabox classique. C'est un détail facile à oublier tant que personne ne réclame l'accès à la donnée depuis l'extérieur.

## En résumé

Une métabox classique reste parfaitement légitime en 2020, y compris sur un site utilisant l'éditeur de blocs : WordPress assure la compatibilité descendante sans qu'il soit nécessaire de tout réécrire en JavaScript. Le vrai travail consiste à sécuriser correctement la sauvegarde — nonce, autosave, capacité — et à décider, au cas par cas, si le champ mérite une exposition REST.

Pour ce projet, la métabox classique a suffi : l'équipe éditoriale s'est habituée en quelques jours à la retrouver dans le panneau « Options de l'écran », et aucune réécriture en composants React n'a été nécessaire.
