0

I have looked online and found different pieces of code together but unable to find a solution that holds up.

Basically i need to display 10 posts in total, but the first three need to be random.

This is what I have so far (taken from a similar question).

$args = array( 
    'post_type' => 'post', 
    'posts_per_page'        => -1,
    'orderby'               => 'publish_date',
    'order'                 => 'DESC',
    '_shuffle_and_pick'     => 3 // <-- our custom argument
);
$loop = new \WP_Query( $args );

With the following function in my functions.php

add_filter( 'the_posts', function( $posts, \WP_Query $query )
{
if( $pick = $query->get( '_shuffle_and_pick' ) )
{
    shuffle( $posts );
    $posts = array_slice( $posts, 0, (int) $pick );
}
return $posts;
}, 10, 2 );

But this just displays three random posts and that's all.

Can this be adapted so that the it displays 10 in total, with the first 3 being random the rest in date order?

Or do I need a new approach?

3
  • 2
    I'm not clear on what you mean, you want 3 random posts followed by 10 date ordered posts for a total of 13? or 10 posts, the first 3 random, so 10 total, 3 random, 7 date ordered? That sounds like 2 separate queries, but keep in mind random ordering is stupendously expensive on the database. It's one of the worst things you can do for scaling and speed/performance.
    – Tom J Nowell
    Commented Feb 22, 2018 at 0:53
  • 10 posts in total, first three random Commented Feb 22, 2018 at 8:07
  • ok but does it really have to be random? Could it not be the first 3 posts after a date randomly chosen in PHP? I cannot understate how awful for your server ordering by RAND is, it's truly horrific for performance, completely uncachable, and will increase the page loading time enormously, getting worse as your add more content to the site
    – Tom J Nowell
    Commented Feb 22, 2018 at 14:11

2 Answers 2

1

Try this:

// All the rendered HTML
$output = '';

// The posts to exclude from the random query.
$exclude = [];

// The remaining non-random posts
$remaining = new WP_Query([ 
   'posts_per_page' => 7,
]);

// Run the remaining query first because we have to know what should be excluded in the random query.
if {$remaining->have_posts()) {
    while ($remaining->have_posts()) {
        $remaining->the_post();

        // Ensure that all remainig posts are excluded from the random query.
        $exclude[] = get_the_ID();

        ob_start();

        // Render the output, consider using a function for consistency.
        the_title();
        the_content();

        // Gather and append the ouput.
        $output .= ob_get_clean();
    }
    wp_reset_query();
}

// Setup the random query with the excluded posts array.
$random = new WP_Query([
    'posts_per_page' => 3,
    'exclude' => $exclude,
    'orderby' => 'rand',
]);

// Run the random query
if {$random->have_posts()) {
    while ($random->have_posts()) {
        $random->the_post();

        ob_start();

        // Again render the output, consider using a function for consistency.
        the_title();
        the_content();

        // Gather and prepend the ouput.
        $output = ob_get_clean() . $ouput;
    }
    wp_reset_query();
}

// Display the ouput
print $output;

You could also turn it around, first run the random query and store their ids to exclude in the remainig query what runs after the random query.

You need two loops because you can't "include" 3 specific posts what are shown on top of the remaining posts. However you can try doing it with one loop what returns the posts in an incorrect order but than you use grid css to change the display order.

0

Perhaps this psuedocode in the Loop:

// set up a counter
// the WP Loop
   // if counter < 4 (so first three posts)
      // put the post output text into an array element (with title, links, content, etc)
   // if counter > 3 (so the next posts after the first three
      // put the post output into a different array element
   // increment the counter
// the WP Loop end
// now output the loop's content
// for the elements in the first array (the first three posts)
   // output each element in the loop (each element is the post title.content/etc
// end the first loop
// for the elements in the 2nd array (the rest of the post)
   // output each element in the loop
// end the second loop
// all done

Very rough, but you might get the idea.

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.