Ask your WordPress questions! Pay money and get answers fast! (more info)

Pagination fix after dropdown filter on Custom Taxonomy Archive

  • SOLVED

I have a custom taxonomy archive for the custom taxonomy called 'sale'.

I wanted to filter these posts using a dropdown.

I have built the dropdown using Advanced Custom Fields. Admin populates it with options by selecting two product codes, pre_get_posts queries all products between). Front end form submits user's selection using $_POST, then pre_get_posts function adjusts the query based on the dropdown selection. This appears to be working.

The archive shows 50 posts per page, which is correct. There are usually more than 50 results once a dropdown selection has been made, so it paginates.

The problem is, that when posts are filtered by the dropdown, moving to page 2 etc resets the dropdown selection and the corresponding pagination. I need it to remember and display the user's selection with the relevant results when moving between pages.

Wondered if using $_SESSION to store the dropdown value was an option, but I don't know how to link this with the pagination function.

Dropdown setup in functions.php:


function sale_selector() {

global $wp_query;

if(isset($_POST['sale_submit'])) {
$dropdown_value = $_POST['section_name'];
}

// Query for current sale name
$sale = $wp_query->get_queried_object();
$sale_id = $sale->term_id;
$acf_lookup = 'sale_'.$sale_id;

echo '<form id="sale_selector" method="post" action="' . get_term_link($sale) . '">
<select name="section_name">
<option value="">All Lots</option>';

if( have_rows('sections', $acf_lookup ) ){
while( have_rows('sections', $acf_lookup ) ): the_row();
$section_title = get_sub_field('sectionname');
if($section_title == $dropdown_value){
$selected = ' selected';
} else {
$selected = '';
}
echo '<option value="' . $section_title . '" ' . $selected . '>' . $section_title . '</option>'; // not sure selected is working properly here
endwhile;
}

echo '</select>
<input type="submit" name="sale_submit" id="filter_button" value="OK">
</form>';
}


relevant part of pre_get_post function in functions.php:

if(is_tax('sale')&& $query->is_main_query()){
if(isset($_POST['sale_submit'])) {

$dropdown_selection = $_POST['section_name'];

// get all rows from the postmeta table where the sub_field (section_title) equals the dropdown selection
global $wpdb;

$section_rows = $wpdb->get_results(
"
SELECT *
FROM {$wpdb->prefix}postmeta
WHERE meta_key LIKE 'sections_%_sectionname'
AND meta_value = '{$dropdown_selection}'
"
);

// loop through the results
if( $section_rows ){
foreach( $section_rows as $section_row ){
// for each result, find the 'repeater row number' and use it to load the sub fields
preg_match('_([0-9]+)_', $section_row->meta_key, $matches);
$meta_key1 = 'sections_' . $matches[0] . '_firstlot'; // $matches[0] contains the row number
$meta_key2 = 'sections_' . $matches[0] . '_lastlot';

// use get_post_meta to load the image sub field
$first_lot = get_post_meta( $section_row->post_id, $meta_key1, true );
$last_lot = get_post_meta( $section_row->post_id, $meta_key2, true );
}
}

$query->set( 'meta_query', array(
'relation' => 'AND',
array(
'key' => 'online_lot_number',
'compare' => '>=',
'type' => 'NUMERIC',
'value' => $first_lot // first number in the section from ACF repeater field.
),
array(
'key' => 'online_lot_number',
'compare' => '<=',
'type' => 'NUMERIC',
'value' => $last_lot // last number in the section from ACF repeater field.
),
));

}
}


Pagination function in template-tags.php

function catalogue_nav() {

$total_pages = $GLOBALS['wp_query']->max_num_pages;
if ($total_pages > 1){
$current_page = max(1, get_query_var('paged'));
echo '<nav class="pagination">';
echo paginate_links(array(
'base' => @add_query_arg( 'paged', '%_%' ),
'format' => '%#%',
'current' => $current_page,
'total' => $total_pages,
'mid_size' => 4,
));
echo '</nav>';}

}

Answers (4)

2016-01-22

Rempty answers:

Send the select value via GET and not via POST.


Rempty comments:

<form id="sale_selector" method="post" action="' . get_term_link($sale) . '">
to
<form id="sale_selector" method="get" action="' . get_term_link($sale) . '">

And change $_POST['section_name'] AND $_POST['sale_submit'] to $_GET['section_name'] $_GET['sale_submit']


theflyingant comments:

Thanks. Unfortunately doing it this way results in undefined variables ($first_lot & $last_lot) within the meta_query.


Rempty comments:

I modified your code take a look
Dropdown setup

function sale_selector() {
global $wp_query;

if(isset($_GET['sale_submit'])) {
$dropdown_value = $_GET['section_name'];
}
// Query for current sale name

$sale = $wp_query->get_queried_object();
$sale_id = $sale->term_id;
$acf_lookup = 'sale_'.$sale_id;

echo '<form id="sale_selector" method="get" action="' . get_term_link($sale) . '">
<select name="section_name">
<option value="">All Lots</option>';
if( have_rows('sections', $acf_lookup ) ){
while( have_rows('sections', $acf_lookup ) ): the_row();
$section_title = get_sub_field('sectionname');
if($section_title == $dropdown_value){
$selected = ' selected';
} else {
$selected = '';
}

echo '<option value="' . $section_title . '" ' . $selected . '>' . $section_title . '</option>'; // not sure selected is working properly here
endwhile;
}

echo '</select>
<input type="submit" name="sale_submit" id="filter_button" value="OK">
</form>';
}


The relevant part of pre_get_posts in your functions.php

if(is_tax('sale')&& $query->is_main_query()){
if(isset($_GET['sale_submit'])) {
$dropdown_selection = $_GET['section_name'];
// get all rows from the postmeta table where the sub_field (section_title) equals the dropdown selection
global $wpdb;

$section_rows = $wpdb->get_results( "
SELECT *
FROM {$wpdb->prefix}postmeta
WHERE meta_key LIKE 'sections_%_sectionname'
AND meta_value = '{$dropdown_selection}'
"
);



// loop through the results

if( $section_rows ){
foreach( $section_rows as $section_row ){
// for each result, find the 'repeater row number' and use it to load the sub fields
preg_match('_([0-9]+)_', $section_row->meta_key, $matches);
$meta_key1 = 'sections_' . $matches[0] . '_firstlot'; // $matches[0] contains the row number
$meta_key2 = 'sections_' . $matches[0] . '_lastlot';

// use get_post_meta to load the image sub field
$first_lot = get_post_meta( $section_row->post_id, $meta_key1, true );
$last_lot = get_post_meta( $section_row->post_id, $meta_key2, true );

}

}



$query->set( 'meta_query', array(
'relation' => 'AND',
array(
'key' => 'online_lot_number',
'compare' => '>=',
'type' => 'NUMERIC',
'value' => $first_lot // first number in the section from ACF repeater field.

),
array(
'key' => 'online_lot_number',
'compare' => '<=',
'type' => 'NUMERIC',
'value' => $last_lot // last number in the section from ACF repeater field.
),
));

}
}


theflyingant comments:

Thanks, unfortunately still getting the undefined variables for the 'value' part of the meta_query.


Rempty comments:

Check you url parameters after send the form

must be like myurl.com/term_slug/?sale_submit=ok&section_name=valuexyz


theflyingant comments:

?section_name=Perfumes&sale_submit=OK is correctly appended to end of url.


Rempty comments:

check the querys
inside if(is_tax('sale')&& $query->is_main_query()){

First echo $_GET['section_name'] and check the value
echo $dropdown_selection = $_GET['section_name'];

check the results of $section_rows


theflyingant comments:

$_GET['section_name'] returns undefined index error.

since switching from $_POST to $_GET, section rows now returns empty array. It worked with $_POST.


Rempty comments:

Plz test echo $_GET['sale_submit'] ;
If don't show error "undefined....";

Try changing the name of the select to other maybe like section_namex, don't forget to replace the name new in all $_GET['section_name'];

If show the error undefined
add this to your functions.php

add_filter( 'query_vars', 'addnew_query_vars', 10, 1 );
function addnew_query_vars($vars)
{
$vars[] = 'section_name';
$vars[] = 'sale_submit';
return $vars;
}


theflyingant comments:

The undefined index was my fault - now sorted and GET method appears to be working perfectly. Thanks for your help.


Rempty comments:

Don't forget "vote to award prize"

2016-01-22

Andrea P answers:

you have to pass the selector choice as a get variable within the pagination urls.

and as a consequence, then you must change your functions in order to pass/retrieve that value as GET instead of POST

so, in the function sale_selector, change this line


echo '<form id="sale_selector" method="post" action="' . get_term_link($sale) . '">


with this


echo '<form id="sale_selector" method="get" action="' . get_term_link($sale) . '">


AND these lines


if(isset($_POST['sale_submit'])) {
$dropdown_value = $_POST['section_name'];
}


to this:


if(isset($_GET['sale_submit'])) {
$dropdown_value = $_GET'section_name'];
}



____________________________

then in pre_get_post filter, change this line:


if(isset($_POST['sale_submit'])) {
$dropdown_selection = $_POST['section_name'];


to this

if(isset($_GET['sale_submit'])) {
$dropdown_selection = $_GET['section_name'];




then in the catalogue_nav function, change the pagination_link to be like this


$big = 999999999; // need an unlikely integer
echo paginate_links( array(
'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
'format' => '?paged=%#%',
'current' => $current_page,
'total' => $total_pages,
'add_args' => array( 'section_name' => $_GET['section_name'] )
) );


theflyingant comments:

Thanks, but this gives Undefined index: section_name error when I make a selection from the dropdown.


theflyingant comments:

Thanks, but this gives Undefined index: section_name error when I make a selection from the dropdown.


Andrea P comments:

oh I'm sorry, I haven't spotted another place where you should change POST to GET.

I've edited my first answer, let me know you it goes now


Andrea P comments:

ah!

and probably it's safer if the paginate_links will conditionally add the string only if a value has been set for the section_name


$big = 999999999; // need an unlikely integer
$paginate_links = paginate_links( array(
'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
'format' => '?paged=%#%',
'current' => $current_page,
'total' => $total_pages
) );

if ( $_GET['section_name'] ){
$paginate_links['add_args'] = array( 'section_name' => $_GET['section_name'] );
}
echo $paginate_links;


Andrea P comments:

no sorry, forget my previous comment! it's been a long day..

here is the correct one:


$big = 999999999; // need an unlikely integer
$paginate_links_args = array(
'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
'format' => '?paged=%#%',
'current' => $current_page,
'total' => $total_pages
);

if ( $_GET['section_name'] ){
$paginate_links_args[]['add_args'] = array( 'section_name'=>$_GET['section_name'] );
}

echo paginate_links($paginate_links_args);


Andrea P comments:

you have to keep the rest of your navigation functions, so at the end it'll look like this:


function catalogue_nav() {

$total_pages = $GLOBALS['wp_query']->max_num_pages;

if ($total_pages > 1){

$current_page = max(1, get_query_var('paged'));
echo '<nav class="pagination">';

$big = 999999999; // need an unlikely integer
$paginate_links_args = array(
'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
'format' => '?paged=%#%',
'current' => $current_page,
'total' => $total_pages
);

if ( $_GET['section_name'] ){
$paginate_links_args[]['add_args'] = array( 'section_name'=>$_GET['section_name'] );
}

echo paginate_links($paginate_links_args);

echo '</nav>';
}

}


Andrea P comments:

sorry.. typo:
this

$paginate_links_args[]['add_args'] = array( 'section_name'=>$_GET['section_name'] );


should be this:

$paginate_links_args['add_args'] = array( 'section_name'=>$_GET['section_name'] );


I hate the fact that you cannot edit a reply in this website.. -_-


theflyingant comments:

Andrea,

Thanks for your suggestions - problem is since switching from $_POST to $_GET the $wpdb->get_results query is returning empty. The variable $dropdown_selection still contains the selected value though.


theflyingant comments:

Andrea, now sorted the issue. Your answers were helpful in solving it thanks a lot.


Andrea P comments:

Hi there!

that's weird.. especially if $dropdown_selection is actually populated correctly..

I fear I might have confused you with my suggestions (mixed with other people suggestions too..) and maybe you have missed to edit something, or you are mixmatching our suggestions?

edit: I just had a quick look at your replies to others, is all working fine now?

2016-01-22

Reigel Gallarde answers:

do you have a link?


Reigel Gallarde comments:

if it's allowed to change the code, I would suggest you change the method of the form to be GET... and use $_GET instead of $_POST['sale_submit']


Reigel Gallarde comments:

also $_POST['section_name'] to $_GET['section_name'].


theflyingant comments:

Thanks but see reply to Rempty below.


Reigel Gallarde comments:

did you change $dropdown_selection = $_POST['section_name']; to $dropdown_selection = $_GET['section_name'];


theflyingant comments:

Hi thanks for reply, yes code is as Rempty has supplied below.


Reigel Gallarde comments:

I'm still on my last suggestion, and may I ask where is this function sale_selector() called?

2016-01-22

dimadin answers:

How about changing line

'base' => @add_query_arg( 'paged', '%_%' ),

in paginate_links() to something like

'base' => @add_query_arg( array( 'paged' => '%_%', 'dropdown_field_1' => 'value_1' ),

and then checking for both $_POST and $_GET?
So you should also get values just before paginate_links() and pass them to the base.