# Dupliquer un menu de navigation complet entre deux emplacements en un clic

> Reconstruire un menu à la main pour un second emplacement fait perdre du temps et introduit des erreurs. Une action de duplication programmatique règle le problème.

- Auteur : Clément Hadrot
- Publié le : 2024-08-08
- Mis à jour le : 2024-08-08
- Catégorie : Tips
- URL : https://wpmoderne.dev.wordpress-developpement.fr/tips/dupliquer-menu-navigation-complet-entre-emplacements/

## L’essentiel

- wp_get_nav_menu_items pour lire la structure source
- wp_update_nav_menu_item pour recréer chaque entrée
- Conserver la hiérarchie parent-enfant lors de la copie

Une enseigne de literie avec plusieurs emplacements de menu (un menu principal, un menu pied de page partiellement identique, et un menu mobile qui devait reprendre exactement la même structure) demandait à son équipe marketing de recréer à la main, dans le pied de page, une bonne partie des entrées du menu principal chaque fois qu'une catégorie de produit était ajoutée. Le résultat : des oublis fréquents, et un pied de page qui prenait du retard sur le menu principal.

La solution consiste à ajouter, dans l'écran d'administration des menus, un bouton « Dupliquer vers » qui recopie programmatiquement toute la structure d'un menu existant vers un nouvel emplacement, hiérarchie comprise.

## Lire la structure du menu source

La fonction `wp_get_nav_menu_items()` renvoie l'ensemble des éléments d'un menu, chacun sous forme d'objet contenant notamment son titre, son URL, son `menu_item_parent` (l'ID de l'élément parent, 0 pour un élément racine), et son ordre `menu_order` :

```
function literie_dupliquer_menu( int $id_menu_source, string $nom_menu_cible ) {
    $elements = wp_get_nav_menu_items( $id_menu_source );

    if ( ! $elements ) {
        return new WP_Error( 'menu_vide', 'Le menu source est introuvable ou vide.' );
    }

    $menu_cible = wp_create_nav_menu( $nom_menu_cible );

    if ( is_wp_error( $menu_cible ) ) {
        return $menu_cible;
    }

    return literie_copier_elements( $elements, $menu_cible );
}
```

## Recréer chaque élément en conservant la hiérarchie

> L'essentiel à retenir : wp_get_nav_menu_items pour lire la structure source ; wp_update_nav_menu_item pour recréer chaque entrée ; Conserver la hiérarchie parent-enfant lors de la copie

Le point délicat de cette copie : un sous-menu référence son parent par l'ID de l'élément d'origine, qui n'existera évidemment plus dans le nouveau menu. Il faut donc garder une table de correspondance entre les anciens ID et les nouveaux, en traitant les éléments dans l'ordre où `wp_get_nav_menu_items()` les retourne (les parents avant leurs enfants, ce qui est déjà l'ordre naturel de cette fonction) :

```
function literie_copier_elements( array $elements, int $id_menu_cible ) {
    $correspondance_ids = array();

    foreach ( $elements as $element ) {
        $nouveau_parent = 0;

        if ( $element->menu_item_parent && isset( $correspondance_ids[ $element->menu_item_parent ] ) ) {
            $nouveau_parent = $correspondance_ids[ $element->menu_item_parent ];
        }

        $nouvel_id = wp_update_nav_menu_item( $id_menu_cible, 0, array(
            'menu-item-title'     => $element->title,
            'menu-item-url'       => $element->url,
            'menu-item-status'    => 'publish',
            'menu-item-parent-id' => $nouveau_parent,
            'menu-item-position'  => $element->menu_order,
            'menu-item-type'      => $element->type,
            'menu-item-object'    => $element->object,
            'menu-item-object-id' => $element->object_id,
        ) );

        if ( ! is_wp_error( $nouvel_id ) ) {
            $correspondance_ids[ $element->ID ] = $nouvel_id;
        }
    }

    return $id_menu_cible;
}
```

Le second argument `0` passé à `wp_update_nav_menu_item()` indique qu'il s'agit d'une création et non d'une mise à jour d'un élément existant : c'est ce paramètre qui déclenche la création d'un nouvel élément de menu plutôt que la modification d'un élément portant cet ID.

### Affecter le nouveau menu à un emplacement

Une fois la copie terminée, il reste à associer ce nouveau menu à l'emplacement souhaité, via l'option `theme_mods` dédiée aux emplacements de menu :

```
function literie_assigner_a_emplacement( int $id_menu, string $emplacement ) {
    $emplacements = get_theme_mod( 'nav_menu_locations', array() );
    $emplacements[ $emplacement ] = $id_menu;
    set_theme_mod( 'nav_menu_locations', $emplacements );
}
```

## Ajouter le bouton dans l'écran d'administration

Pour rendre l'outil accessible sans passer par la console ou un script ponctuel, un simple lien d'action ajouté via le filtre `nav_menu_options` ou une action personnalisée déclenchée par un bouton dans l'écran `nav-menus.php` suffit à déclencher la fonction, avec une vérification de nonce classique :

```
add_action( 'admin_post_literie_dupliquer_menu', function () {
    check_admin_referer( 'literie_dupliquer_menu' );

    if ( ! current_user_can( 'edit_theme_options' ) ) {
        wp_die( 'Action non autorisée.' );
    }

    $id_source = (int) ( $_POST['menu_source'] ?? 0 );
    $resultat  = literie_dupliquer_menu( $id_source, 'Pied de page (copie)' );

    wp_safe_redirect( admin_url( 'nav-menus.php?message=dupliquee' ) );
    exit;
} );
```

## Ce qui casse souvent si on va trop vite

- Oublier de conserver l'ordre `menu_order` : les éléments réapparaissent alors dans le désordre.
- Copier les éléments avant leurs parents si la source a été manipulée manuellement (rare, mais possible après une modification directe en base).
- Ne pas gérer les éléments de type `taxonomy` ou `post_type` dont l'objet source pourrait avoir été supprimé entre-temps : toujours vérifier `object_id` avant la copie.

> Sur un site à plusieurs emplacements de menu proches mais pas identiques, je préfère une duplication ponctuelle suivie d'ajustements manuels à une synchronisation permanente automatique : cette dernière finit presque toujours par bloquer une personnalisation légitime propre à un emplacement.

## Pour aller plus loin

Cette même mécanique de copie avec table de correspondance d'ID s'applique à d'autres structures hiérarchiques de WordPress : taxonomies avec parents, pages avec pages enfants, ou blocs réutilisables imbriqués. Le principe reste identique : traiter les parents avant les enfants, et toujours garder la table de correspondance entre anciens et nouveaux identifiants pendant toute la durée de la copie.
