Prevent members-only content from appearing in WP_Query loops

7 Replies ·

  1. Hi Justin —

    Hoping you can help. I’m using Members on a client’s membership site. The site has multiple post types, and I have multiple queries on custom template files to call posts.

    Most of the content (about two-thirds) is for members only, and the client doesn’t want non-members to be able to see anything about the restricted posts.

    Is it possible to prevent restricted posts from appearing in wp_query?

  2. Justin Tadlock

    You can filter the_posts. It’s not a well-documented hook, but here’s one doc: http://hookr.io/4.1.1/filters/the_posts/

    A quick and unpolished method might be something like:

    add_filter( 'the_posts', 'my_posts_filter' );
    
    function my_posts( $posts ) {
    
        $return_posts = array();
    
        if ( !empty( $posts ) ) {
    
            foreach ( $posts as $post ) {
    
                if ( members_can_current_user_view_post( $post->ID ) )
                    $return_posts[] = $posts;
            }
        }
    
        return $return_posts;
    }
    
  3. mbec_sean

    Thanks Justin. I’m trying to get this up and running on my site, but am getting about a dozen or so errors like the following:

    “Notice: Trying to get property of non-object in /Sites/mysite/wp-includes/query.php on line 4373”

    … and a mostly otherwise blank page.

    I’ve tweaked the code a bit (shouldn’t the function be “my_posts_filter”?), but can’t figure out where I’ve gone wrong. Any advice?

    Thanks!

    add_filter( 'the_posts', 'my_posts_filter' );
    
    function my_posts_filter( $posts ) {
    
    	$return_posts = array();
    
    	if ( !empty( $posts ) ) {
    
    		foreach ( $posts as $post ) {
    
    			if ( members_can_current_user_view_post( $post->ID ) )
    				$return_posts[] = $posts;
    		}
    	}
    
    	return $return_posts;
    }
  4. Justin Tadlock

    Try this:

    add_filter( 'the_posts', 'my_posts_filter' );
    
    function my_posts_filter( $posts ) {
    
        $return_posts = array();
    
        if ( !empty( $posts ) ) {
    
            foreach ( $posts as $post ) {
    
                if ( members_can_current_user_view_post( $post->ID ) )
                    $return_posts[] = $post;
            }
        }
    
        return $return_posts;
    }
    

    I changed this line:

    $return_posts[] = $post;
    
  5. mbec_sean

    That almost did the trick! 🙂

    The restricted posts no longer show, but they’re still taking up “space” in the query.

    So my WP_query with these args:

    $args = array (
    	'post_type'              => 'resources',
    	'posts_per_page'   => '5',
    	'paged'		           => $paged
    			);

    … will show a different number of posts on each page, depending on how many of the posts are restricted.

    So, a given page might show 4 posts, or 2 posts, or 5 posts.

    Is there a way to tweak the query so it doesn’t count restricted posts?

  6. Justin Tadlock

    No, there’s no way to change how the above behaves to not “count” the posts. At that point, the posts have already been loaded.

    To do what you want, you’d need to change what posts are loaded altogether.

    Off the top of my head, something like this might work. If nothing else, it’s a starting point. And, I’m not even certain how meta_query (used below) works when there are multiple meta values (how the roles are stored per-post in the plugin). It might take some trial and error.

    add_action( 'pre_get_posts', 'my_pre_get_posts' );
    
    function my_pre_get_posts( $query ) {
    
        if ( is_user_logged_in() ) {
    
            $user = new WP_User( get_current_user_id() );
    
            $meta_query = array(
                'relation' => 'OR',
                array(
                    'key'     => '_members_access_role',
                    'compare' => 'NOT EXISTS',
                ),
                array(
                    'key'     => '_members_access_role',
                    'value'   => $user->roles[0],
                    'compare' => '!='
                )
            );
        } else {
    
            $meta_query = array(
                array(
                    'key'     => '_members_access_role',
                    'compare' => 'NOT EXISTS'
                )
            );
        }
    
        $query->set( 'meta_query', $meta_query );
    }