I read this article about how to set an offset for one specific category in the loop. I was wondering about if this was possible for more than just one category.
My problem with the code suggested in the article linked above is that I am using a single widget to show five posts from a category I can set in the widget options by ID.
I cannot wrap my head around how to collect the post_ID
s of the posts already looped through.
Situation:
I am using custom widgets on my blog page to show the 5 latest posts in two specified categories - let's call them categoryA and categoryB.
Answer to @Pieter Goosen's question in the comments: Yes, I can specify the category by manually entering an ID in the widget settings and it's saved in the DB.
Below these widgets the blog page loops through all my posts and shows them.
Effect:
The main loop on the blog page (the one below the widgets) displays the posts from categoryA and categoryB again. They appear double because the widgets above already looped through the categories A and B.
Question:
Is it possible to exclude the first five posts from categoryA and categoryB in the main loop? Could it be done with pre_get_posts
? Using get_query_var
? How?
EDIT
My own approaches
FIRST TRY
I modified my widget codes like this:
global $do_not_duplicate;
while($featured_posts->have_posts()): $featured_posts->the_post();
$do_not_duplicate[] = get_the_ID();
As my widgets are being registered in my functions.php
file, I added these lines to it:
global $do_not_duplicate;
$do_not_duplicate = array();
include('widgets/widgets.php');
Finally on the index.php
, I added this line to the main loop:
if(have_posts()) :
global $do_not_duplicate;
while(have_posts()): the_post();
if( in_array( $post->ID, $do_not_duplicate ) ) continue;
get_template_part('content');
endwhile;
endif;
Please note: This basically works but does not exactly remove the posts from the main query but skip them. This means the default posts_per_page
setting won't be fulfilled and this won't work with Jetpack infinite scroll. Also, using globals is not the way to go.
SECOND TRY
I adapted @birgire suggestion and modified it a little. I thought about using the suggested pre_get_posts filter in combination with a foreach-loop for the categories used by the widgets in order to gather the IDs of the posts queried by them. Here's my snippet:
add_action('pre_get_posts', 'modified_mainloop', 10);
function modified_mainloop($query) {
if(!is_admin() && $query->is_home() && $query->is_main_query()) {
$cats_to_exclude = get_categories('include=3,4,9');
foreach($cats_to_exclude as $category) {
if($category->term_id == '9') {
$num_posts_to_exclude = 3;
} else {
$num_posts_to_exclude = 5;
}
$posts_to_exclude = get_posts(array(
'posts_per_page' => $num_posts_to_exclude,
'category__in' => array($category->term_id),
'fields' => 'ids'
));
}
$query->set('post__not_in', (array) $posts_to_exclude);
}
}
Please note: Idea is OK, but code does not work.