Custom Pagination Code Help

I'm using the paginate_links( $args ) function in a loop in my site. And I'm also using a filter in the loop to filter the posts under different taxonomies. The problem is, when I filter the posts, some query strings are added to the URL of the page. Now, when I use the pagination links to go to the next page, the query strings stay added to the URL. Any ideas on how I could avoid that?

Here's the full code I'm using: https://pastebin.com/RjFHgh5d

  • Adam Czajczyk

    Hello Sebastián

    I hope you're well today and thank you for your question!

    There are two basic issues there, in my opinion. First one is the way the filter form is built. The code is fetching a current URL (which at some point already contains filtering attributes) and using it as a form URL. Then you are sending the form with GET method and the GET method means data being sent via URL, thus "visible" and being "added" to the URL.

    The form should be send using POST method, the form URL (action) should be a specific "fixed" url - ideally, that would be just an URL of your site and the rest would be handled by the code but at least it would be yourdomain.com/peliculas.

    All the additional data (filtering attributes) then should be sent as a hidden input fields inside the form.

    The second issue is the way these "filtering variables" are handled. The way the form is sending them is indeed just "appending" them to URL (because of the GET method and because of using already "built up" URL as a form action URL). With POST method, "fixed" URL and hidden fields it will not be doing that.

    However, you would also need to read the values first to know what to put into these hidden fields and to use them in an actually WP query then. This is where "query_vars" WP filter comes handy to "register" those variables and a "get_query_var()" function. Take a look at the "Custom Query Vars" section here please:

    https://codex.wordpress.org/Function_Reference/get_query_var

    This way you should be able to re-factor the code to make it more "WP Codex compliant" and fixing the issue as well.

    Please note that this is complex, entirely custom code and as such is outside the scope of this support: https://premium.wpmudev.org/docs/getting-started/getting-support/#chapter-1. If you need it to be re-factored/rewritten for you, please consider posting a job request either on our "Jobs & Pros" job board (please note: no WPMU DEV staff involved) here:

    https://premium.wpmudev.org/wordpress-development/

    or e.g. at Codeable.io here:

    https://codeable.io/

    If you are going to proceed on your own and have any additional questions, don't hesitate to ask and I'll be happy to share any suggestions and tips that I can come up with.

    Kind regards,
    Adam

  • Sebastián

    First, thank you so much Adam for your advices.

    I've already refactor the code but, after it, I have new issue. Now, after change between pages, the $args query is reset.

    I appreciate new comments about this issue.

    Here is my code after refactor it:

    // --- --- --- --- GLOBALS --- --- --- //
    global $post;
    global $wpdb;

    // --- --- --- --- INICIALIZO VARIABLES --- --- --- //
    $anio_final = 'final';
    $anio_final_query_str = '';
    $anio_inicio = 'inicio';
    $anio_inicio_query_str = '';
    $anios_param = array();
    $args = array();
    $duracion = 'duracion';
    $duracion_param = array();
    $duracion_query_str = '';
    $genero = 'genero';
    $genero_query_arr = array();
    $genero_query_str = '';
    $generos = array();
    $genero_params = array();
    $sin_genero = array(
    'slug' => 'sin-genero-asignado',
    'name' => 'Sin género asignado'
    );
    $indice = 'indice';
    $indice_alfabetico = array();
    $letra_query_str = '';
    $output = '';
    $peliculas_url = '';
    $posts = 0;
    $relation_str = 'AND';
    $tax_query = array();
    $todas = 'TODAS';
    $url = home_url();

    // --- --- --- --- ALFABÉTICA --- --- --- //
    $indice_alfabetico = cd_obtener_alfabeticas();
    array_unshift($indice_alfabetico, $todas);

    if(get_query_var($indice) != '')
    $letra_query_str = get_query_var($indice);

    // --- --- --- --- GÉNEROS --- --- --- //
    $generos = get_terms(array('taxonomy' => 'movie_cat', 'orderby' => 'name'));
    array_unshift($generos, $sin_genero);

    if(get_query_var($genero) != '')
    $genero_query_str = get_query_var($genero);

    // --- --- --- --- AÑOS --- --- --- //
    //Si el año final no está seteado, se incluye hasta el año actual con la función date('Y');
    $anio_inicio_query_str = '1900';
    $anio_final_query_str = date('Y');
    if(get_query_var($anio_inicio) != '')
    $anio_inicio_query_str = get_query_var($anio_inicio);

    if(get_query_var($anio_final) != '')
    $anio_final_query_str = get_query_var($anio_final);

    // --- --- --- --- DURACION --- --- --- //
    if(get_query_var($duracion) != '')
    $duracion_query_str = get_query_var($duracion);

    // --- --- --- --- PELICULAS URL --- --- --- //
    if (strpos($url, 'local') === false)
    $peliculas_url = 'peliculas';
    else
    $peliculas_url = 'movies';

    // --- --- --- --- PAGINA ACTUAL --- --- --- //
    $paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1;

    if($show_top == 'yes'){
    $output .= '<hr>';
    $output .= '<div class="filtro-cinedata">';
    $output .= '<form id="filtros" action="'.esc_url( $url ).'/'.$peliculas_url.'" method="POST">';

    //INDICE ALFABETICO
    $output .= '<div class="moview-filters clearfix mb5">';
    $output .= '<div class="pull-left">';
    $output .= '<label>Índice alfabético:</label>
    ';
    $output .= '<ul class="list-inline list-style-none">';
    foreach ($indice_alfabetico as $key => $value) {
    if($letra_query_str == '' && $key == 0)
    $output .= '<li class="active">'.$todas.'';
    elseif($letra_query_str != '' && $key == 0)
    $output .= '

  • '.$todas.'
  • ';
    elseif($key > 0 && $letra_query_str == $value->slug)
    $output .= '<li class="active" >slug.'">'.esc_attr($value->slug).'';
    else
    $output .= '
  • slug.'">'.esc_attr($value->slug).'
  • ';
    }
    $output .= '<input type="hidden" id="indice" name="indice" value="">';
    $output .= '';
    $output .= '</div>';
    $output .= '</div>';

    //GENEROS ESPECÍFICOS
    $output .= '<div id="generos">';
    $output .= '<label>Géneros:</label>
    ';
    foreach ( $generos as $key => $value ) {
    //Distingo entre la primera posición del array y todas las otras porque en la posición 0 hay un array (TODAS) y en las otras posiciones son objetos WP_Term
    if($key == 0)
    if (isset($genero_query_str[0]) && strpos($genero_query_str[0], $value['slug']) !== false){
    array_push($genero_params,$value['slug']);
    $output .= '<label for="'.$value['slug'].'"><input class="genero" type="checkbox" name="gen_ui[]" id="'.$value['slug'].'" data-genero="'.$value['slug'].'" checked>'.$value['name'].'</label>';
    }
    else
    $output .= '<label for="'.$value['slug'].'"><input class="genero" type="checkbox" name="gen_ui[]" id="'.$value['slug'].'" data-genero="'.$value['slug'].'">'.$value['name'].'</label>';
    else
    if (isset($genero_query_str[0]) && strpos($genero_query_str[0], $value->slug) !== false){
    array_push($genero_params,$value->slug);
    $output .= '<label for="'.$value->slug.'"><input class="genero" type="checkbox" name="gen_ui[]" id="'.$value->slug.'" data-genero="'.$value->slug.'" checked>'.$value->name.'</label>';
    }
    else
    $output .= '<label for="'.$value->slug.'"><input class="genero" type="checkbox" name="gen_ui[]" id="'.$value->slug.'" data-genero="'.$value->slug.'">'.$value->name.'</label>';
    }
    $genero_query_str = implode(',', $genero_params);
    $output .= '<input type="hidden" id="genero" name="genero[]" value="'.$genero_query_str.'">';
    $output .= '
    ';
    $output .= '</div>';

    // ANIO INICIO y ANIO FIN
    $output .= '<div id="anios">';
    $output .= '<label for="">Período:</label>

    ';
    $output .= '<input type="hidden" id="anio-inicio" name="inicio" value="'.$anio_inicio_query_str.'">';
    $output .= '<input type="hidden" id="anio-final" name="final" value="'.$anio_final_query_str.'">';
    $output .= '<input id="valores_rango" type="hidden" data-desde="'.$anio_inicio_query_str.'" data-hasta="'.$anio_final_query_str.'" class="slider-input range-slider anios" value="'.$anio_inicio_query_str.','.$anio_final_query_str.'"/>';
    $output .= '</div>';

    // DURACION
    $output .= '<div id="duracion">';
    $output .= '<label for="duracion">Duración:</label>';
    $output .= '<select name="duracion" id="select-duracion">';
    //TODOS
    if($duracion_query_str != '' && $duracion_query_str == 'todos'){
    $output .= '<option value="todos" selected="selected">Todos</option>';
    }
    else
    $output .= '<option value="todos">Todos</option>';
    //CORTO
    if($duracion_query_str != '' && $duracion_query_str == 'corto'){
    $output .= '<option value="corto" selected="selected">Corto (menos de 30 minutos)</option>';
    }
    else
    $output .= '<option value="corto">Corto (menos de 30 minutos)</option>';
    //MEDIO
    if($duracion_query_str && $duracion_query_str == 'medio'){
    $output .= '<option value="medio" selected="selected">Medio (entre 30 y 60 minutos)</option>';
    }
    else
    $output .= '<option value="medio">Medio (entre 30 y 60 minutos)</option>';
    //LARGO
    if($duracion_query_str && $duracion_query_str == 'largo'){
    $output .= '<option value="largo" selected="selected">Largo (más de 60 minutos)</option>';
    }
    else
    $output .= '<option value="largo">Largo (más de 60 minutos)</option>';
    $output .= '</select>';
    $output .= '<input type="hidden" id="duracion_input" name="duracion" value="'.$duracion_query_str.'">';
    $output .= '</div>';
    $output .= '<button id="btn_enviar" type="submit" class="btn mr20 ml10">Filtrar</button>';
    $output .= '<button class="btn invertido" type="button" id="limpiar-filtros">Limpiar filtros</button>';
    $output .= '</form>';
    $output .= '</div>';
    $output .= '<hr>';
    }

    if($letra_query_str != "" && $letra_query_str != "TODAS"){
    $tax_query[] = array(
    'taxonomy' => 'movie_alfabetica',
    'field' => 'slug',
    'terms' => $letra_query_str,
    );
    };

    if($genero_query_str != ""){
    $genero_query_arr = explode(",",$genero_query_str);
    $tax_query[] = array(
    'taxonomy' => 'movie_cat',
    'field' => 'slug',
    'terms' => $genero_query_arr,
    'operator' => 'IN'
    );
    };

    if($duracion_query_str != "" && $duracion_query_str != "todos"){
    $tax_query[] = array(
    'taxonomy' => 'movie_duracion',
    'field' => 'slug',
    'terms' => $duracion_query_str
    );
    };

    $cant_tax_query = count($tax_query);

    if($cant_tax_query > 1)
    $tax_query['relation'] = $relation_str;

    $args = array(
    'post_type' => 'movie',
    'post_status' => 'publish',
    'posts_per_page'=> esc_attr($number),
    'meta_type' => 'NUMERIC',
    'meta_key' => 'themeum_anio_final',
    'orderby' => 'meta_value_num',
    'order' => 'DESC',
    'paged' => $paged,
    'meta_query' => array(
    'relation' => 'AND',
    array(
    'key' => 'themeum_anio_final',
    'value' => $anio_inicio_query_str,
    'compare' => '>=',
    'type' => 'NUMERIC'
    ),
    array(
    'key' => 'themeum_anio_final',
    'value' => $anio_final_query_str,
    'compare' => '<=',
    'type' => 'NUMERIC'
    )
    )
    );

    if($letra_query_str != "" || $genero_query_str != "" || $duracion_query_str != "")
    $args['tax_query'] = $tax_query;

    $posts = get_posts( $args );
    $cantidad_posts = count($posts);
    $the_query = new WP_Query( $args );

    // THE LOOP

    if( $x == (12/$column) ){
    $output .= '</div>'; //row
    $x = 1;
    }else{
    $x++;
    }
    endforeach;
    $output .= '</div>';//spotlight-common
    if($x != 1 ){
    $output .= '</div>'; //row
    }

    wp_reset_postdata();

    // Total Post
    $args['posts_per_page'] = -1;
    $total_post = get_posts( $args );
    $var = $number;

    if( $var == "" || $var == 0 ){
    $total_post = 1;
    }else{
    $total_post = ceil( count($total_post)/(int)$var );
    }

    $output .= '<div class="themeum-pagination">';
    $big = 999999999; // need an unlikely integer
    $output .= paginate_links( array(
    'type' => 'list',
    'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) )),
    'format' => '?paged=%#%',
    'current' => max( 1, get_query_var('paged') ),
    'total' => $total_post
    ) );
    $output .= '</div>'; //pagination-in
    }

    return $output;
    });

  • Adam Czajczyk

    Hi @sebastianvillar!

    So, it seems we're "getting there" then :slight_smile: Good to hear that!

    If I may have one small request before I respond, I would like to ask you to post a code in a bit different way. Whenever you're posting a code here please use "code" tool of post editor to mark it: highlight the code part of the post and click on this icon above text field:

    This will apply additional formatting to the code block, making it more "readable" and easier to copy. If you're posting a bit longer piece of code (such as this one) please also consider using a service like e.g https://pastebin.com for sharing it.

    None of this is a requirement but I would appreciate it a lot as it just speeds up my assistance, making it much easier to handle the code :slight_smile:

    As for the code. If I'm not mistaken the problem now is that you're actually "rebuilding" the query each time the page is loaded. Here's what happens:

    1. a page is loaded for the first time so default query args are set
    2. user sets the search filters and submits the form
    3. this returns results and paginates
    4. user changes a page - now at this point no data is send via any form and an URL is "clear" (because data from form is sent via POST request) so as a result default query args are set again.

    That's, actually, pretty common with custom pagination, regardless of what set of "custom filters" (args) are you using.

    This is a part of your code:

    $posts = get_posts( $args );
    $cantidad_posts = count($posts);
    $the_query = new WP_Query( $args );

    Query args here ($query) are what's causing WP_Query to return expected results, right? But if no custom filter data is sent (which happens on initial load and on pagination "switch":wink: they are "initialized" again.

    That means we need a way to "forward" the original query args to another page. You are using a custom code to output pagination links

    / Total Post
    $args['posts_per_page'] = -1;
    $total_post = get_posts( $args );
    $var = $number;
    
    if( $var == "" || $var == 0 ){
    $total_post = 1;
    }else{
    $total_post = ceil( count($total_post)/(int)$var );
    }
    
    $output .= '<div class="themeum-pagination">';
    $big = 999999999; // need an unlikely integer
    $output .= paginate_links( array(
    'type' => 'list',
    'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) )),
    'format' => '?paged=%#%',
    'current' => max( 1, get_query_var('paged') ),
    'total' => $total_post
    ) );
    $output .= '</div>'; //pagination-in
    }

    I think the solution here could be a "paginate_links()" native WP template tag:

    https://codex.wordpress.org/Function_Reference/paginate_links

    You can define the format of the pagination links and you can adjust it to show as many posts as you need but you should also be able to "forward" query args to the next page via its "add_args" parameter.

    Another way to solve that would actually be to store all the query args in the cookie or - better yet - as a session variable (see here: https://wordpress.stackexchange.com/a/117260) and retrieve them on page load - if they are set, don't "initialize" query args but assign fetched data from session; if they are not set - initialize, if a user click on some sort of "remove filters" - just unset session and reload the page.

    Best regards,
    Adam

  • Adam Czajczyk

    Hello Sebastián,

    Since you are already reading data using "get_query_vars()" instead of directly reading "$_GET"/"$_POST" that should actually work. You might, however, need to just register your custom variables. There's a filter for this:

    function add_query_vars_filter( $vars ) {
      $vars[] = "my_var";
      return $vars;
    }
    add_filter( 'query_vars', 'add_query_vars_filter' );

    The $vars[] array contains query variables (query_vars) that are "registered" with the site so they can be seen and used by get_query_vars (the same way as e.g. you are fetching "paged" query var).

    Another way to go would be, as I mentioned before, just saving values of your variables to a session ($_SESSION[]) and reading them upon page load, but that complicates the code even more and I'd try just registering the vars (see above) and after that giving a paginate_links() a shot.

    Have you tried that already?

    Best regards,
    Adam

Thank NAME, for their help.

Let NAME know exactly why they deserved these points.

Gift a custom amount of points.