1

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'
     )
  );
}

1 Answer 1

0

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 ).

3
  • Thanks for the reply! I am considering what is the best solution for my situation. I was hoping to find a solution that would avoid having to manually link page-slug-> menu , but I accept your answer because it is an interesting idea. Commented Jun 18, 2021 at 23:33
  • Oh, I missed that! If you can come up with some sort of mapping that could be created programmatically (e.g. on all pages, the page with the slug page-{identifier} maps to the menu secondary-menu-{identifier} - or the page with the slug {page slug} uses the menu menu-page-{page slug}), I can totally add an example for that use-case
    – bosco
    Commented Jun 18, 2021 at 23:51
  • Having thought a little bit more on it, I think it would be plausible to create something like the Template Hierarchy for nav menus - attempt to load conventionally identified secondary menus corresponding to the query and "queried object," and fall back to more general menus when it is not available.
    – bosco
    Commented Jun 19, 2021 at 0:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.