Agregar programáticamente un menú de navegación y elementos del menú

7 mar 2012, 15:43:00
Vistas: 58.2K
Votos: 51

A través de funciones de la API, quiero definir un nuevo menú de navegación, seleccionarlo en el tema actual y luego insertar algunas Páginas como elementos del menú. Esto se debe hacer, por ejemplo, en la activación de un tema.

A través de un proceso (moderadamente doloroso) de ingeniería inversa de las inserciones y actualizaciones de la base de datos después de configurar manualmente el menú de navegación y los elementos, he reunido los siguientes pasos, donde 'footer-nav' es el ID slug del menú de navegación que estoy creando:

if (!term_exists('footer-nav', 'nav_menu')) {

    // Insertar nuevo menú
    $menu = wp_insert_term('Footer nav', 'nav_menu', array('slug' => 'footer-nav'));

    // Seleccionar este menú en el tema actual
    update_option('theme_mods_'.get_current_theme(), array("nav_menu_locations" => array("primary" => $menu['term_id'])));

    // Insertar nueva página
    $page = wp_insert_post(array('post_title' => 'Blog',
                                 'post_content' => '',
                                 'post_status' => 'publish',
                                 'post_type' => 'page'));

    // Insertar nuevo elemento de menú
    $nav_item = wp_insert_post(array('post_title' => 'Noticias',
                                     'post_content' => '',
                                     'post_status' => 'publish',
                                     'post_type' => 'nav_menu_item'));


    add_post_meta($nav_item, '_menu_item_type', 'post_type');
    add_post_meta($nav_item, '_menu_item_menu_item_parent', '0');
    add_post_meta($nav_item, '_menu_item_object_id', $page);
    add_post_meta($nav_item, '_menu_item_object', 'page');
    add_post_meta($nav_item, '_menu_item_target', '');
    add_post_meta($nav_item, '_menu_item_classes', 'a:1:{i:0;s:0:"";}');
    add_post_meta($nav_item, '_menu_item_xfn', '');
    add_post_meta($nav_item, '_menu_item_url', '');

    wp_set_object_terms($nav_item, 'footer-nav', 'nav_menu');
}

Esto parece funcionar, pero:

  • ¿es una forma robusta y elegante de hacerlo?
  • ¿me estoy perdiendo algo totalmente obvio que haría todo esto en una línea de código?
0
Todas las respuestas a la pregunta 4
7
53

Podría estar entendiéndote mal, pero ¿por qué no usar wp_create_nav_menu()?

Por ejemplo, esto es lo que hago para crear un menú personalizado de BuddyPress cuando detecto que BP está activo:

    $menuname = $lblg_themename . ' BuddyPress Menu';
$bpmenulocation = 'lblgbpmenu';
// ¿Existe ya el menú?
$menu_exists = wp_get_nav_menu_object( $menuname );

// Si no existe, vamos a crearlo.
if( !$menu_exists){
    $menu_id = wp_create_nav_menu($menuname);

    // Configurar enlaces predeterminados de BuddyPress y agregarlos al menú.
    wp_update_nav_menu_item($menu_id, 0, array(
        'menu-item-title' =>  __('Inicio'),
        'menu-item-classes' => 'home',
        'menu-item-url' => home_url( '/' ), 
        'menu-item-status' => 'publish'));

    wp_update_nav_menu_item($menu_id, 0, array(
        'menu-item-title' =>  __('Actividad'),
        'menu-item-classes' => 'activity',
        'menu-item-url' => home_url( '/activity/' ), 
        'menu-item-status' => 'publish'));

    wp_update_nav_menu_item($menu_id, 0, array(
        'menu-item-title' =>  __('Miembros'),
        'menu-item-classes' => 'members',
        'menu-item-url' => home_url( '/members/' ), 
        'menu-item-status' => 'publish'));

    wp_update_nav_menu_item($menu_id, 0, array(
        'menu-item-title' =>  __('Grupos'),
        'menu-item-classes' => 'groups',
        'menu-item-url' => home_url( '/groups/' ), 
        'menu-item-status' => 'publish'));

    wp_update_nav_menu_item($menu_id, 0, array(
        'menu-item-title' =>  __('Foros'),
        'menu-item-classes' => 'forums',
        'menu-item-url' => home_url( '/forums/' ), 
        'menu-item-status' => 'publish'));

    // Tomar las ubicaciones del tema y asignar nuestro menú recién creado
    // a la ubicación del menú de BuddyPress.
    if( !has_nav_menu( $bpmenulocation ) ){
        $locations = get_theme_mod('nav_menu_locations');
        $locations[$bpmenulocation] = $menu_id;
        set_theme_mod( 'nav_menu_locations', $locations );
    }
7 mar 2012 16:03:26
Comentarios

No conocía esta función. Sí, supongo que hará que el código anterior sea mucho más corto. Creo que debería ir más allá del Codex y sumergirme en el código real, ya que encuentro que las funciones de la API a menudo, como en este caso, son de muy bajo nivel. ¡Gracias!

julien_c julien_c
7 mar 2012 16:18:16

@julien_c si esto está resuelto, márcalo como tal para permitir que quienes vengan después de ti se beneficien de tu experiencia aquí.

mor7ifer mor7ifer
7 mar 2012 17:07:59

Solo quiero probarlo en la vida real para estar seguro de que hace lo que necesito. ¡Recordaré marcarlo como resuelto tan pronto como termine!

julien_c julien_c
7 mar 2012 17:51:33

Si ves funciones útiles como estas que no están en el codex, es una buena idea agregarlas (hurra por el wiki) =p

Tom J Nowell Tom J Nowell
9 mar 2012 21:51:56

Lamento que me tomara tanto tiempo verificar que funcionara en mi caso. ¡Respuesta aceptada! Además, estás definiendo elementos de menú de enlaces personalizados, he agregado una respuesta a continuación para definir enlaces a páginas (que será más robusto ante cambios de URL, por ejemplo).

julien_c julien_c
15 may 2012 14:06:01

¿Cómo puedes crear subpáginas usando este enfoque?

Joren Joren
28 dic 2013 16:47:48
Mostrar los 2 comentarios restantes
1
17

Tengo algunos problemas con la respuesta aceptada - eso no la hace incorrecta, pero publicaré mi propio código a continuación que creo podría tener un mejor resultado para algunas personas, ya que tuve la misma pregunta pero quise hacer lo mismo con menos código.

Primero, el código anterior crea elementos de navegación de tipo "URL", lo cual está bien para algunos, pero yo quiero enlazar a PÁGINAS, no a URLs porque esta es una característica importante de las navegaciones en WordPress y los clientes inevitablemente mueven cosas, así que nunca uso el tipo de elemento de navegación URL.

Además, solo se maneja una matriz plana de hijos en el código publicado. He creado una función para declarar recursivamente los nuevos elementos de navegación, almacenando sus metadatos devueltos (principalmente el ID después de ser creados en el bucle), y un parámetro para aceptar hijos.

Solo edita $nav_items_to_add y el resto se maneja recursivamente. Hay 3 claves requeridas en cada matriz. Primero, la clave de la matriz es el slug, así que 'shop' => array( ... ) es lo que quieres para una página con el slug shop. ['title'] es cómo se etiquetará el elemento de navegación en el front-end. path es la ruta a la página dentro de la jerarquía de páginas de WordPress, así que esto es idéntico al slug si la página es un padre de nivel superior, y si shop fuera hijo de home entonces sería 'path' => 'home/shop'.

La última clave opcional del array es ['parent'] donde puedes declarar otra clave en el array como padre de la actual. Es importante notar que los elementos se añaden recursivamente, así que el padre debe existir antes de intentar crear un hijo. Esto significa que la declaración debe ocurrir para el elemento de navegación padre antes que para sus hijos.

    $locations = get_nav_menu_locations();

    if (isset($locations['primary_navigation'])) {
        $menu_id = $locations['primary_navigation'];

        $new_menu_obj = array();

        $nav_items_to_add = array(
                'shop' => array(
                    'title' => 'Tienda',
                    'path' => 'shop',
                    ),
                'shop_l2' => array(
                    'title' => 'Tienda',
                    'path' => 'shop',
                    'parent' => 'shop',
                    ),
                'cart' => array(
                    'title' => 'Carrito',
                    'path' => 'shop/cart',
                    'parent' => 'shop',
                    ),
                'checkout' => array(
                    'title' => 'Pago',
                    'path' => 'shop/checkout',
                    'parent' => 'shop',
                    ),
                'my-account' => array(
                    'title' => 'Mi Cuenta',
                    'path' => 'shop/my-account',
                    'parent' => 'shop',
                    ),
                'lost-password' => array(
                    'title' => 'Contraseña Perdida',
                    'path' => 'shop/my-account/lost-password',
                    'parent' => 'my-account',
                    ),
                'edit-address' => array(
                    'title' => 'Editar Mi Dirección',
                    'path' => 'shop/my-account/edit-address',
                    'parent' => 'my-account',
                    ),
            );

    foreach ( $nav_items_to_add as $slug => $nav_item ) {
        $new_menu_obj[$slug] = array();
        if ( array_key_exists( 'parent', $nav_item ) )
            $new_menu_obj[$slug]['parent'] = $nav_item['parent'];
        $new_menu_obj[$slug]['id'] = wp_update_nav_menu_item($menu_id, 0,  array(
                'menu-item-title' => $nav_item['title'],
                'menu-item-object' => 'page',
                'menu-item-parent-id' => $new_menu_obj[ $nav_item['parent'] ]['id'],
                'menu-item-object-id' => get_page_by_path( $nav_item['path'] )->ID,
                'menu-item-type' => 'post_type',
                'menu-item-status' => 'publish')
        );
    }

    }
2 may 2013 02:04:18
Comentarios

Esa es una solución muy inteligente. Gracias por agregar esto. Exactamente lo que necesitaba.

Juergen Schulze Juergen Schulze
2 jun 2020 12:47:23
0
14

Como complemento a la respuesta de ZaMoose, aquí te mostramos cómo crear un elemento de menú de tipo "Página" (no uno "Personalizado"):

wp_update_nav_menu_item($menu_id, 0, array('menu-item-title' => 'Acerca de',
                                           'menu-item-object' => 'page',
                                           'menu-item-object-id' => get_page_by_path('acerca-de')->ID,
                                           'menu-item-type' => 'post_type',
                                           'menu-item-status' => 'publish'));

Asumiendo que solo conoces el slug de la página, por ejemplo.

15 may 2012 14:04:09
0

Para agregar un elemento de menú programáticamente, puedes usar el filtro wp_nav_menu_items. Coloca el siguiente código en el archivo functions.php de tu tema para agregar un elemento de menú de inicio/cierre de sesión en el menú principal. 'Primary' es el nombre/ID del menú registrado.

/**
 * Agregar enlace de inicio/cierre de sesión en el menú principal.
 * =============================================================
 */

add_filter( 'wp_nav_menu_items', 'lunchbox_add_loginout_link', 10, 2 );
function lunchbox_add_loginout_link( $items, $args ) {
    /**
     * Si el menú principal está configurado y el usuario está conectado.
     */
    if ( is_user_logged_in() && $args->theme_location == 'primary' ) {
        $items .= '<li><a href="'. wp_logout_url() .'">Cerrar Sesión</a></li>';
    }
    /**
     * De lo contrario, mostrar el elemento de menú de inicio de sesión.
     */
    elseif ( !is_user_logged_in() && $args->theme_location == 'primary' ) {
        $items .= '<li><a href="'. site_url('wp-login.php') .'">Iniciar Sesión</a></li>';
    }
    return $items;
}
8 mar 2016 20:49:56