EDIT 2 – FINAL EDIT
Just before I start with this edit, just a few things here
-
The code in my answer are the basics and meant to demonstrate the concepts. I have now tested both sets of code, and all works as expected. You have to add all your desired mark-up, styling and template tags yourself.
-
On the part of implementation, this is entirely up to you, we are not here to implement code for you. It is expected that you do at least have some knowledge on how to use and implement this code into your theme
You have confirmed that this is your search.php, which renders my code in my original answer useless. You should never change the main query with a custom one as this is always problematic as you have seen. Please see this post I have done on this subject
My solution for you would be is to paste the code from my first edit (EDIT 1) in your functions.php. You will have to modify the pre_get_posts
action as needed. I have tested the code and it works perfectly. For this to work, you need to make use of the main loop in search.php.
ORIGINAL ANSWER
What I’ve read and understood from your question is that all pro
values must be first ordered by date, and then free
to follow ordered randomly
We are going to make use of basically the same idea as in your previous question with a few modifications
Just one note before I start, you should not make use of custom SQL queries. Make use of pre_get_posts
, WP_Query
or get_posts
rather to create/adjust/modify your queries accordingly
I’m still a SQL learner, so I’m going to scrap your idea and work with WP_Query
The type of sorting that you want is not possible with the above mentioned native functions. Also, you don’t need to run two queries to achieve this, one is enough.
My idea here would be to retrieve all posts within the meta_value
of pro
and free
. We will keep the default ‘orderby’ and ‘order’ parameters as this does not matter. We will use PHP to achieve the desired sort orders
So, lets code:
STEP 1
As previously stated, we need get all the posts which have a meta_value
of pro
and value
$args = [
'post_type' => 'listing',
'meta_key' => 'geocraft_listing_type',
'meta_value' => 'free, pro',
'meta_compare' => 'IN',
'posts_per_page' => -1,
];
$the_query = new WP_Query( $args );
STEP 2
In my personal opinion, the best way to achieve your sort order is to break the $posts
array into two separate arrays, one array will hold all posts with a meta_value
of pro
while the other array will hold posts with meta_value
free
.
$free = [];
$pro = [];
foreach ( $the_query->posts as $post ) {
$meta = get_post_meta( $post->ID, 'geocraft_listing_type', true );
if ( 'free' == $meta ) {
$free[] = $post;
}else{
$pro[] = $post;
}
}
unset($post);
STEP 3
The array which holds the posts with meta_value
pro
will be left untouched, this array of posts will already be sorted according to post date. We need to sort the array which holds the posts with meta_value
free
As you need this posts to be sorted randomly, we are going to make use of the PHP function shuffle()
to shuffle these posts so that they are going to be displayed randomly
shuffle($free);
STEP 4
We need to unset the original $posts
and reset it with a new value. This value will be created by merging our two arrays together, pro
first, then free
. To achieve this, make use of array_merge()
. This new array will be set as the new value of $posts
unset($the_query->posts);
$the_query->posts = array_merge($pro, $free);
STEP 5
You can now loop through your posts as normal
if ( $the_query->have_posts() ) {
echo '<ul>';
while ( $the_query->have_posts() ) {
$the_query->the_post();
echo '<li>' . get_post_meta( $post->ID, 'geocraft_listing_type', true ). '</br>' . get_the_title() . '</li>';
}
echo '</ul>';
}
wp_reset_postdata();
ALL TOGETHER NOW
<?php
$args = [
'post_type' => 'listing',
'meta_key' => 'geocraft_listing_type',
'meta_value' => 'free, pro',
'meta_compare' => 'IN',
'posts_per_page' => -1,
];
$the_query = new WP_Query( $args );
$free = [];
$pro = [];
foreach ( $the_query->posts as $post ) {
$meta = get_post_meta( $post->ID, 'geocraft_listing_type', true );
if ( 'free' == $meta ) {
$free[] = $post;
}else{
$pro[] = $post;
}
}
unset($post);
shuffle($free);
unset($the_query->posts);
$the_query->posts = array_merge($pro, $free);
if ( $the_query->have_posts() ) {
echo '<ul>';
while ( $the_query->have_posts() ) {
$the_query->the_post();
echo '<li>' . get_post_meta( $post->ID, 'geocraft_listing_type', true ). '</br>' . get_the_title() . '</li>';
}
echo '</ul>';
}
wp_reset_postdata();
You can modify this code to use pre_get_posts
and the_posts
filter if this is suppose to be the main query.
EDIT 1
If this was suppose to be done on the search page as the main query, you would use pre_get_posts
to alter the main query on the search page. I’m not going to explain everything in detail as it should work in the same way as above with WP_Query
In your functions.php, add the following code. This will change the main query only for the search page
add_action( 'pre_get_posts', function ( $query ) {
if ( !is_admin() && $query->is_search() && $query->is_main_query() ) {
$query->set( 'post_type', 'listings' );
$query->set( 'meta_key', 'geocraft_listing_type' );
$query->set( 'meta_value', 'free, pro' );
$query->set( 'meta_compare', 'IN' );
$query->set( 'posts_per_page', '-1' );
}
});
You are now going to use the_posts
filter to filter the sorting order on the search page.
So, again in your functions.php, add the following code
add_filter( 'the_posts', function ($posts, $query) {
if( $query->is_main_query() && $query->is_search() ) {
$free = [];
$pro = [];
foreach ( $posts as $post ) {
$meta = get_post_meta( $post->ID, 'geocraft_listing_type', true );
if ( 'free' == $meta ) {
$free[] = $post;
}else{
$pro[] = $post;
}
}
unset($post);
shuffle($free);
$posts = array_merge($pro, $free);
}
return $posts;
},
10, 2 );
Inside your search.php you don’t need to modify anything. All you need in there is the default loop.