Solución al error "Trying to get property of non-object" con Walker personalizado para wp_nav_menu

16 jun 2013, 16:07:29
Vistas: 18.5K
Votos: 10

Estoy intentando añadir funcionalidades relacionadas con ARIA a la función wp_nav_menu. Para ello uso una clase Walker personalizada:

class Walker_Nav_Menu_With_Aria extends Walker_Nav_Menu {
        function start_lvl( &$output, $depth = 0, $args = array() ) {
                $indent = str_repeat("\t", $depth);
                $output .= "\n$indent<ul class=\"sub-menu\" role=\"group\">\n";
        }

        function end_lvl( &$output, $depth = 0, $args = array() ) {
                $indent = str_repeat("\t", $depth);
                $output .= "$indent</ul>\n";
        }

        function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
                $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

                $class_names = $value = '';

                $classes = empty( $item->classes ) ? array() : (array) $item->classes;
                $classes[] = 'menu-item-' . $item->ID;

                $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
                $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';

                $id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
                $id = $id ? ' id="' . esc_attr( $id ) . '"' : '';

                // Añadir atributos para <li>
                $li_attributes  = ' role="treeitem"';
                $li_attributes .= ' aria-expanded="false"';

                $output .= $indent . '<li' . $id . $value . $class_names . $li_attributes .'>';

                $attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
                $attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
                $attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
                $attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';

                // Añadir atributos para <a>
                $attributes .= $depth == 0 ? ' tabindex="0"' : ' tabindex="-1"';

                $item_output = $args->before;
                $item_output .= '<a'. $attributes .'>';
                $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
                $item_output .= '</a>';
                $item_output .= $args->after;

                $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
        }

        function end_el( &$output, $item, $depth = 0, $args = array() ) {
                $output .= "</li>\n";
        }
}

Simplemente copié y pegué la clase original Walker_Nav_Menu y añadí funcionalidad, pero estoy recibiendo errores porque la variable $args, que se trata como un objeto, es realmente un array. Pero lo más raro es que este error persiste, incluso si paso el walker original así:

wp_nav_menu( array(
    'theme_location' => 'main-nav',
    'walker'         => new Walker_Nav_Menu,
) );

Estos son los mensajes de error que recibo:

NOTICE: TRYING TO GET PROPERTY OF NON-OBJECT IN /USERS/RUDOLF/SITES/LOCALHOST/WP/WP-INCLUDES/NAV-MENU-TEMPLATE.PHP ON LINE 88
NOTICE: TRYING TO GET PROPERTY OF NON-OBJECT IN /USERS/RUDOLF/SITES/LOCALHOST/WP/WP-INCLUDES/NAV-MENU-TEMPLATE.PHP ON LINE 90
NOTICE: TRYING TO GET PROPERTY OF NON-OBJECT IN /USERS/RUDOLF/SITES/LOCALHOST/WP/WP-INCLUDES/NAV-MENU-TEMPLATE.PHP ON LINE 90
NOTICE: TRYING TO GET PROPERTY OF NON-OBJECT IN /USERS/RUDOLF/SITES/LOCALHOST/WP/WP-INCLUDES/NAV-MENU-TEMPLATE.PHP ON LINE 92

Las líneas se ven así (¡en el archivo original!):

(88) $item_output = $args->before;
(89) $item_output .= '<a'. $attributes .'>';
(90) $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
(91) $item_output .= '</a>';
(92) $item_output .= $args->after;
8
Comentarios

¿Recibes algún error si no pasas el walker en absoluto? Los argumentos deberían ser un objeto, creo que eso es lo que dicen los documentos en línea y lo que pasa wp_nav_menu().

Rarst Rarst
16 jun 2013 18:02:38

No, si solo uso la función predeterminada wp_nav_menu() sin un walker, funciona perfectamente. Sí, pero ¿por qué es un array entonces? Incluso hice un var_dump, no es un objeto.

Rudolf Rudolf
16 jun 2013 18:26:16

No estoy seguro de lo que está pasando entonces, porque pasar new Walker_Nav_Menu debería comportarse idénticamente a no pasar un walker. ¿Está pasando algo más, algún plugin que esté enganchado o manipulando la funcionalidad del menú?

Rarst Rarst
16 jun 2013 18:27:39

Acabo de hacer algunas pruebas adicionales y estoy seguro de que es un error de WordPress. En mi caso, el menú de navegación que usé no estaba asignado en el backend. Por lo tanto, parece que se utilizó la opción de respaldo de listado de páginas, y en ese caso la variable args es un array. Ahora voy a rastrear esto hasta su origen y luego te diré lo que encontré.

Rudolf Rudolf
16 jun 2013 18:40:24

¡Si asigno correctamente un menú, funciona!

Rudolf Rudolf
16 jun 2013 18:40:42

Al principio no vi la respuesta de abajo, mira allí para encontrar la solución.

Rudolf Rudolf
16 jun 2013 18:59:11

Probé if(wp_nav_menu()) y el código funciona.

User User
1 sept 2015 09:59:09

¿Te das cuenta de que esta pregunta tiene dos años? :D Pero creé un ticket aquí: https://core.trac.wordpress.org/ticket/24587 Todavía no está solucionado, así que siéntete libre de discutirlo allí.

Rudolf Rudolf
1 sept 2015 17:15:05
Mostrar los 3 comentarios restantes
Todas las respuestas a la pregunta 2
5
21

Recibo este error cuando no hay menús definidos o no hay menús asignados para la ubicación en Apariencia->Menús. Cuando eso ocurre, wp_nav_menu utiliza un respaldo (fallback) con un page walker.

  1. El respaldo (por defecto) para wp_nav_menu es wp_walker_page
  2. que utiliza wp_page_menu
  3. que utiliza wp_list_pages
  4. que utiliza walk_page_tree
  5. que utiliza Walker_Page y no Walker_Nav_Menu.

Y aparentemente los dos walkers no son compatibles. No sé por qué no falla de manera elegante. Eso me parece un bug.

Con un menú configurado en wp-admin->Apariencia->Menús, tu código funciona.

Puedes evitar el error verificando que haya un menú asignado a la ubicación antes de intentar usarla.

$locations = get_nav_menu_locations();
if (0 !== $locations['main-nav']) {
  wp_nav_menu( array(
      'theme_location' => 'main-nav',
      'walker'         => new Walker_Nav_Menu_With_Aria,
  ) );
}

O, si prefieres un código menos enredado (gracias @Rarst):

if (has_nav_menu('primary')) {
  wp_nav_menu( array(
      'theme_location' => 'primary',
      'walker'         => new Walker_Nav_Menu_With_Aria,
  ) );
}
16 jun 2013 17:05:02
Comentarios

El fallback solo se usa cuando todo lo demás falla para mostrar páginas. Si el menú funciona correctamente, los elementos se recorren con walk_nav_menu_tree() que sí utiliza Walker_Nav_Menu por defecto.

Rarst Rarst
16 jun 2013 17:57:40

¡Buena actualización! No se me ocurrió que la función incorrecta podría estar tomando el walker (eso es meh). También existe has_nav_menu() para esa verificación.

Rarst Rarst
16 jun 2013 18:34:35

Eso es exactamente lo que quería publicar :D Los $args del menú de navegación se convierten en un objeto en wp-includes/nav-menu-template.php en la línea 145 por la función wp_nav_menu(). La función wp_page_menu() que se usa como fallback no hace eso, sino que simplemente pasa el array al Walker del menú de navegación que necesita el objeto.

Si simplemente agregas un $args = (object) $args al inicio de la función start_el de la clase Walker, puedes solucionarlo. ¿Cuál sería el lugar correcto para informar esto al equipo de WordPress?

Rudolf Rudolf
16 jun 2013 18:57:34

Los errores del núcleo deben reportarse en http://core.trac.wordpress.org/

Rarst Rarst
16 jun 2013 19:00:40

El objeto WP_Post que se pasa como $item también es diferente (Debido a esto, las páginas no tienen nombres y la lista solo consiste en elementos <li> vacíos). Se necesitaría una refactorización importante para hacer que estos dos walkers sean compatibles.

Rudolf Rudolf
16 jun 2013 19:04:43
0

utiliza el siguiente código para reparar:

add_filter( 'wp_get_nav_menu_object', 'override_wp_get_nav_menu_object', 10, 2 );
function override_wp_get_nav_menu_object( $menu_obj, $menu ) {

    if ( ! is_object( $menu_obj ) ) {
        $menu_obj = (object) array( 'name' => '' );
    }

    return $menu_obj;
}
21 ago 2017 03:37:06