Conditional secondary menus

The question:

I have a main menu of 3 items, and for each of these I have a different secondary menu. I would like to show the related secondary menu on each page.
I currently used is_page with slug. I will need to add wpml, so each page will have more slugs. I would like to ask if you can suggest me a cleaner and more maintainable solution.
This is what I did at the moment:

if ( is_page( 'page-1' ) ) {
  wp_nav_menu(
   array(
     'theme_location' => 'secondary-menu-1'
   )
  );
} elseif (is_page( 'page-2' ) ) {
  wp_nav_menu(
    array(
      'theme_location' => 'secondary-menu-2'
     )
  );
}

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

This is just sort of a PHP shortcut, but you could create a mapping describing which conditional values map to what menu locations, and loop through it testing each condition and printing menus for matches:

/**
 * Prints wp_nav_menu(s) by matching conditional function arguments to menu locations.
 *
 * @param array   $locations A mapping of predicate function names to associative
 *                           arrays mapping predicate function arguments to menu
 *                           locations.
 * @param array   $args Optional. An associative array of arguments to pass on to
 *                      wp_nav_menu().
 * @param boolean $multiple Optional. Whether or not to allow multiple menus to
 *                          match/print.
 * @return string|void The menu(s) markup, if $args['echo'] is set to false.
 **/
wpse390599_conditional_nav_menu( $locations, $args = [], $multiple = false ) {
  $conditional_locations = $locations;
  $markup                = '';
  $return                = isset( $args['echo'] ) && $args['echo'] === false;

  foreach( $conditional_locations as $predicate => $locations ) {
    foreach( $locations as $condition => $location ) {
      if( ! call_user_func( $predicate, $condition ) )
        continue; // No match - skip location.

      // Predicate conditional returned TRUE - print the menu
      $args['location'] = $location;
      $markup = wp_nav_menu( $args );

      // If we're not facilitating multiple matching menus, bail out.
      if( ! $multiple ) {
        // Return the markup if `$args['echo'] is false, per wp_nav_menu() convention.
        if( $return )
          return $markup;
    
        return;
      }
    }
  }

  // If multiple menus were matched and $args['echo'] is false, return the markup.
  if( $return )
     return $markup;
}

Usage:

wpse390599_conditional_nav_menu(
  [
    'is_page' => [
      'page-1' => 'secondary-menu-1',
      'page-2' => 'secondary-menu-2',
      ''       => 'secondary-menu-default',
     ],
  ]
);

As an added benefit, when $multiple is false, the ordering of the $conditional_locations arrays can be used to specify precedence in the case that multiple conditions would match.

Whether or not this is more maintainable than your current solution really depends on the specifics of the use-case. Of note, this structure would not support WordPress conditional predicates which take multiple arguments, e.g. is_tax( $taxonomy, $term ).


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment