On my CPT archive page I have custom "sort by" form that works perfectly but breaks with my pagination - here is example from my archive-listing.php
file:
<div class="col-xxl-9 col-xl-9 col-lg-8 col-md-7">
<?php
if ( have_posts() ) :
global $wp_query;
$paged = ! empty( $wp_query->query_vars['paged'] ) ? $wp_query->query_vars['paged'] : 1;
$per_page = $wp_query->query_vars['posts_per_page'];
$prev_posts = ( $paged - 1 ) * $per_page;
$from = 1 + $prev_posts;
$to = count( $wp_query->posts ) + $prev_posts;
$total = $wp_query->found_posts; ?>
<div class="viewItems">
<div class="row align-items-center">
<div class="col-12 col-lg-6">
<strong>
<?php
if ( 1 == $total ) {
echo 'Showing one result';
} else {
printf( 'Showing %s - %s of %s results', $from, $to, $total );
}
?>
</strong>
</div>
<div class="col-12 col-lg-6">
<div class="select-itms">
<?php wpse_288655_display_form(); ?>
</div>
</div>
</div>
</div>
<div class="gridView">
<div id="response">
<div class="row">
<?php
while ( have_posts() ) : the_post();
echo '<div class="col-xl-4 col-lg-6 col-md-12 col-sm-6">';
get_template_part( 'content', 'listing' );
echo '</div>';
endwhile;
?>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="col-lg-12">
<div class="pagination mt-60">
<?php
global $wp_query;
$total_pages = $wp_query->max_num_pages;
if ( $total_pages > 1 ) {
$current_page = max( 1, get_query_var( 'paged' ) );
echo paginate_links( array(
'base' => get_pagenum_link( 1 ) . '%_%',
'format' => '/page/%#%',
'current' => $current_page,
'total' => $total_pages,
'prev_text' => '<i class="las la-angle-left"></i>',
'next_text' => '<i class="las la-angle-right"></i>',
'type' => 'list',
'end_size' => 3,
'mid_size' => 3,
) );
}
?>
</div>
</div>
</div>
<?php
endif;
?>
</div>
sorting form code in functions.php
source :
<?php
/**
* Display sort form
*/
function wpse_288655_display_form() {
/**
* Get all params from url which are not part of our sort form
* and display it in form as hidden inputs.
*/
$search_param = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );
/**
* Current order value to select proper field
*/
$value = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_STRING );
?>
<form method="get">
<?php wpse_288655_display_select('order', 'Sort by:', wpse_288655_get_order_by_options(), $value ); ?>
<?php
/**
* Display all params from url which do not apply to our sort form
*/
?>
<input type="hidden" name="s" value="<?php esc_attr_e( $search_param ); ?>">
<button type="submit"><?php esc_html_e('Sort'); ?></button>
</form>
<?php
}
/**
* Get sort options
*/
function wpse_288655_get_order_by_options(){
return array(
'' => '',
'newest' => __('Newest'),
'oldest' => __('Oldest'),
'most_expensive' => __('Most Expensive'),
'least_expensive' => __('Least Expensive'),
'largest' => __('Largest'),
'smallest' => __('Smallest'),
);
}
/**
* Display select field
*/
function wpse_288655_display_select( $name, $label, $options, $value = '' ) {
?>
<label><?php esc_html_e( $label ) ?></label>
<select name="<?php esc_attr_e( $name ) ?>">
<?php wpse_288655_display_options( $options, $value ); ?>
</select>
<?php
}
/**
* Display select options
*/
function wpse_288655_display_options( $options, $value ) {
foreach( $options as $option_value => $option_label ):
$selected = ( $option_value === $value ) ? ' selected' : '';
?>
<option value="<?php esc_attr_e( $option_value ) ?>"<?php esc_attr_e( $selected ) ?>><?php esc_html_e( $option_label ) ?></option>
<?php
endforeach;
}
/**
* Sort posts using pre_get_posts filter
*/
function wpse_288655_order_posts( $query ) {
/**
* Execute query only when we are on search page and this is main query
*/
if ( $query->is_search() && $query->is_main_query() ) {
$value = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_STRING );
$order_by_options = wpse_288655_get_order_by_options();
if( isset( $order_by_options[ $value ] ) && !empty( $value ) ) {
switch( $value ) {
case 'newest':
$query->set( 'order', 'desc' );
$query->set( 'orderby', 'date' );
break;
case 'oldest':
$query->set( 'order', 'asc' );
$query->set( 'orderby', 'date' );
break;
case 'most_expensive':
$query->set( 'meta_key', 'price' ); // Your custom meta_key
$query->set( 'order', 'desc' );
$query->set( 'orderby', 'meta_value_num' );
break;
case 'least_expensive':
$query->set( 'meta_key', 'price' ); // Your custom meta_key
$query->set( 'order', 'asc' );
$query->set( 'orderby', 'meta_value_num' );
break;
case 'largest':
$query->set( 'meta_key', 'size' ); // Your custom meta_key
$query->set( 'order', 'desc' );
$query->set( 'orderby', 'meta_value_num' );
break;
case 'smallest':
$query->set( 'meta_key', 'size' ); // Your custom meta_key
$query->set( 'order', 'asc' );
$query->set( 'orderby', 'meta_value_num' );
break;
default:
break;
}
}
}
}
add_filter('pre_get_posts', 'wpse_288655_order_posts');
It creates links like example.com/listings/?order=oldest/page/2
, but you stay on the first page