Using pre_get_posts to filter one loop in a multiloop archive

I have a custom-post-type archive (archive-rg_blog.php) with two loops in it. One of the loops I’d like to filter with the below code. It’s just a basic loop for all posts, and I just need to set it to return all posts instead of the custom posts it’d return by default.

The other loop needs to pull the title and content from a specific custom post. The below seems to be filtering the second loop too, and so that it returns nothing at all.

function blog_filter($query) {
  if ( !is_admin() && $query->is_main_query() ) {
    if ($query->is_post_type_archive('rg_blog')) {
      $query->set('post_type', 'post');
    }
  }
}
add_action('pre_get_posts','blog_filter');

And this is the loop that returns nothing:

<?php // blog title and description query
$post_id = 4606;
$queried_post = get_post($post_id);
$title = $queried_post->post_title;?>
<h1><?php echo $title;?></h1>

Edit: Just to clarify, this is what I’m trying to do.

///loop 1, queries a specific post
///loop 2, a normal loop filtered with pre_get_posts, which should return all posts.
///loop 3, also queries a specific post

So I need to add the filter before loop 2, and remove it again before loop 3. I’ve tried to add the filter into the archive template in the appropriate place, but loop 2 then fails to return all posts.

Edit #2: Here’s the whole loop in action. It’s the SECOND loop that needs to be filtered.

<?php get_header(); ?>

<?php // blog title and description query
$post_id = 4606;
$queried_post = get_post($post_id);
$title = $queried_post->post_title;?>
<h1><?php echo $title;?></h1>

<!-- Main Loop (All Posts)   -->
<?php function blog_filter($query) {
  if ( !is_admin() && $query->is_main_query()) {
    if ($query->is_post_type_archive('rg_blog')) {
      $query->set('post_type', 'post');
    }
  }
}
add_action('pre_get_posts','blog_filter');?>
<?php $post = $posts[0]; $c=0;?>
<?php if (have_posts()) : ?><?php while (have_posts()) : the_post(); ?>
<!--Featured Post-->
<?php $c++;
if( !$paged && $c == 1) :?>
<!--stuff-->
<!--Other Posts-->
<?php else :?>           
<!--the remaining posts-->          
<?php endif;?>
<?php endwhile; endif; ?>
<!-- The end of the loop   -->
<?php remove_action('pre_get_posts','blog_filter');?>

        <!--third loop, a repeat of loop 1-->  
<?php // blog title and description query
$post_id = 4606;
$queried_post = get_post($post_id);
$title = $queried_post->post_title;?>  
                <h1><?php echo $title;?></h1>
                <?php echo $queried_post->post_content;?>
<?php get_footer() ?>

Solutions Collecting From Web of "Using pre_get_posts to filter one loop in a multiloop archive"

Add the filter where you need it and remove it afterwards.

add_action('pre_get_posts','blog_filter');
// your query; presumably WP_Query
// your first loop
remove_action('pre_get_posts','blog_filter');
// another query; presumably WP_Query
// your second loop

You can also make your action self-removing:

function blog_filter($query) {
  remove_action('pre_get_posts','blog_filter');
  if ( !is_admin() && $query->is_main_query() ) {
    if ($query->is_post_type_archive('rg_blog')) {
      $query->set('post_type', 'post');
    }
  }
}
add_action('pre_get_posts','blog_filter');

Your code, reorganized:

// define the function; can be here or functions.php
function blog_filter($query) {
  if ( !is_admin() && $query->is_main_query()) {
    if ($query->is_post_type_archive('rg_blog')) {
      $query->set('post_type', 'post');
    }
  }
}

get_header(); 

  // blog title and description query
  $post_id = 4606;

  // add the filter
  add_action('pre_get_posts','blog_filter'); 

  $queried_post = get_post($post_id);

  // remove the filter; the query is done
  remove_action('pre_get_posts','blog_filter'); 

  $title = $queried_post->post_title; ?>
  <h1><?php echo $title;?></h1><?php

  // Main Loop (All Posts)
  $post = $posts[0];
  $c=0;
  if (have_posts()) {
    while (have_posts()) {
      the_post(); 
      // Featured Post
      $c++;
      if( !$paged && $c == 1) {
        // stuff 
        // Other Posts
      } else {           
        // the remaining posts          
      }
    }
  }
  // The end of the loop
  // third loop, a repeat of loop 1

  // blog title and description query
  $post_id = 4606;
  $queried_post = get_post($post_id);
  $title = $queried_post->post_title; ?>  
  <h1><?php echo $title;?></h1><?php 
  echo $queried_post->post_content;
get_footer();

I have not tested that but I think it will work better. Hopefully, I don’t have any syntax errors.

Edit:

I took a closer look at your code (while not watching “Defiance”) and notices a couple of things.

  1. Your filter will only effect the main query so you will need to add
    it before the page template loads– probably in functions.php. But
    my understanding of the question is that the filter is meant for a
    secondary Loop. That would mean that that “main query” check is
    wrong.
  2. You are requesting posts via get_post by ID, so for those the
    filter is irrelevant.

So, to filter that main query you need this in functions.php:

// define the function; can be here or functions.php
function blog_filter($query) {
  // remove the filter; the query is done
  remove_action('pre_get_posts','blog_filter');
  if ( !is_admin() && $query->is_main_query()) {
    if ($query->is_post_type_archive('rg_blog')) {
      $query->set('post_type', 'book');
    }
  }
}
add_action('pre_get_posts','blog_filter'); 

And no add or remove actions anywhere else. I am not sure that is what you want though. The two get_post calls should be unaffected.

I would suggest checking the is_post_type_archive before adding the filter:

global $wp_query;
if ($wp_query->is_post_type_archive('rg_blog')) {
    add_action('pre_get_posts','blog_filter');
}

This way, it won’t run on every page load.


Try removing the filter before the second query:

remove_action('pre_get_posts', 'blog_filter');