Can I use WordPress WP_Query to find a post with a particular menu_order?

I can use WP_Query to order posts matching my query by menu_order.

But can I also specifically find posts that have a particular menu_order?

Read More

Even better, can I find posts with a menu_order larger than a particular number? Or smaller than a particular number?

If so, how?

Note: I can, of course, loop through posts that match my query and find the post I’m looking for in code, but it would be nicer if I can get the result directly from WP_Query.

Related posts

Leave a Reply

1 comment

  1. Matching by menu_order is easy, like this:

    $args = array(
       // find posts with menu_order 4 
       'menu_order' => 4,
       'orderby' => 'menu_order',
       'order' => 'ASC',
       'posts_per_page' => 10,
    );
    $query = new WP_Query( $args );
    

    However, larger or smaller option for menu_order is not provided by default in WP_Query.

    You can still filter the where clause to achieve that. Something like this:

    $args = array(
       'orderby' => 'menu_order',
       'order' => 'ASC',
       'posts_per_page' => 10,
    );
    
    add_filter( 'posts_where', 'wpse_menu_order_filter', 10, 2 );
    function wpse_menu_order_filter( $where, $query ) {
        // remove this filter to avoid changing other queries.
        remove_filter( 'posts_where', 'wpse_menu_order_filter' );
    
        // custom where clause to show only posts with menu_order greater than 4
        return $where . ' AND wp_posts.menu_order > 4 ';
    }
    $query = new WP_Query( $args );
    

    You can do the same thing for smaller check as well.

    More advanced solution:

    You can also enhance the menu_order argument so that the greater or lesser sign and the compare value is not hard coded within the filter function. Like this:

    $args = array( 
       'post_type' => 'page',
       'menu_order' => array(
            'value' => 5,
            'compare' => '<='
        ),
       'orderby' => 'menu_order',
       'order' => 'ASC',
       'posts_per_page' => 10,
    );
    add_filter( 'posts_where', 'wpse_menu_order_filter', 10, 2 );
    
    function wpse_menu_order_filter( $where, $wp_query ) {
    
        remove_filter( 'posts_where', 'wpse_menu_order_filter' );
    
        $args = $wp_query->query;
    
        if( isset( $args['menu_order'] ) && is_array( $args['menu_order'] ) ) {
    
            $menu_order = $args['menu_order'];
    
            if( isset( $menu_order['value'] )
                && isset( $menu_order['compare'] ) ) {
            
                $compare = $menu_order['compare'];
    
                // allow only four comparisons: '>', '>=', '<', '<='   
                if( in_array( $compare, array( '>', '>=', '<', '<=' ) ) ) {
    
                    $value = $menu_order['value'];
    
                    if( is_scalar( $value ) && '' !== $value ) {
                        $value = absint( $value );
                    }
                    else {
                        $value = 0;
                    }                      
                    return sprintf( $where . ' AND wp_posts.menu_order %s %d ',
                                    $compare, $value );
                }
            }
        }
            
        return $where;
    }
    $query = new WP_Query( $args );
    

    With the above implementation, you can just change the $args to get the desired result. For example:

    // menu_order less than or equals 5
    $args = array( 
       'post_type' => 'page',
       'menu_order' => array(
            'value' => 5,
            'compare' => '<='
        )
    );
    
    // menu_order greater than 10
    $args = array( 
       'post_type' => 'page',
       'menu_order' => array(
            'value' => 10,
            'compare' => '>'
        )
    );
    
    // equals will still work like before
    $args = array( 
       'post_type' => 'page',
       'menu_order' => 6
    );