SOLVED: Query Filters + Pagination not working quite well

Browser: Chrome 110
OS: macOS / Windows / Linux / etc.
URL: https://try.bricksbuilder.io/t76ae529/testing-filters/
Video: https://jam.dev/c/9f095475-996a-4ab2-9514-704a0eb0d17d

Hello Bricks Support,

I am experiencing an issue with Query Fitlers and the Pagination element.

When I go to the last page of a Query ( in this case some Posts ) and click on a filter ( in this case a Category that has one Post in it ) the posts are filtered by the category but the page stays the same (
you can view the Fetch/XHR request in Dev Tools and you will see that in the payload there is a “paged” key in queryArgs with the value of the page you filtered from ).
I personally think then when you use any Query Filters, no matter which page you are on, the page should be reset to 1.

I created a try.bricksbuilder enviroment just to test if the issue is present on a plain Bricks installation, and it is. That is the link that I am providing in the topic as well, but I this issue is present on two of the websites that I am currently working on. Can you please check out the configuration in the Bricks Admin panel as I think I got it right but I am not a 100% sure ( I can provide you with Admin creds for the try.bricksbuilder enviroment ).

Thanks in advance!

2 Likes

+1 I have experienced the same issue which still can’t figure out how to solve it.

Hi there,

Thanks for the report.

Yes, we are aware of this behavior, this will be enhanced (auto remove the page param, when applying a new “non-pagination” filter on the same query)

Regards,
Jenn

Thanks for the clarification @itchycode!

Can you please notify us when the update rolls out, as I wish to use the built in Filtering instead of using a third party plugin.

Sure yes, we will update this thread when it is released.

Regards,
Jenn

Hey @itchycode,

Hope all is good
I saw that a new Bricks version ( 1.10 ) rolled out last week. Do you know if the issue with Query Filters + Pagination is solved or it will be handled in a later update?

Hi @nikis ,

Unfortunately, the fix for this issue is not included in 1.10
This issue will be tackled together with the new enhanced Query Filters which can support URL param.
(WIP: URL parameters for filters)

Regards,
Jenn

Browser: Chrome 110
OS: macOS / Windows / Linux / etc.
URL: Link to a page that illustrates this issue
Video: Jam

Hi, @itchycode , I think I’m having the same problem here.

I tried to use a filter with cards. My cards are generated by a query loop.

But I want to have only 9 items on my page, so I added pagination. But it turns out that pagination breaks the filter I use to filter my cards. It seems that if I could go back to the first pagination when I select a new filter, that would solve the problem. I’m not even sure this is the right solution.

What’s the problem? Is it a bug? If not, how can I fix the problem?

My loop is based on posts, and the filter is based on the post category. The same post can have several categories.
I can send a temporary admin link to the support if you need it.

Is this problem the same as this thread topic? If yes, is an update or a fix ready?
Thank you

3 Likes

Hello @ICCintern,

FYI, the link to the illustration page leads to an Under Construction visualization.

Based on the video that you have provided it seems the issue is the same as the one I reported a few months back. I not sure when will the Bricks team roll out an update to fix this issue. However as a temporary solution you can query all the categories and have them dynamically link to the corresponding archive page, this way you are not using any AJAX filtering and you sure that there is nothing to go wrong.

I have the same issue on 2 sites. @itchycode Is there an eta when this will be resolved/tackled because both sites should go online soon. Here is one site. you can also see that the filtering breaks when you choose a /page/2 + Also the filtering doesn`t work at all when coming direct to the category like here. If i have setup something wrong on my end plz advise how to tackle this. Thx

How did you do the setup in the query? I have the same issues. Thx for your help

Hey @Kh4N,

Since the link you have sent seems to be an archive it will be easy to query the terms of you taxonomy.

I suppose your terms set-up is heretical, this means that you can have nested queries.
The parent query should consist of custom PHP arguments, something like this:

'taxonomy'         => 'taxonomy_name',
'include_children' => false

And nested in that query should consist of another custom PHP set of arguments, something like this:

'taxonomy' => 'taxonomy_name',
'parent'   => '{term_id}' --> this should be dynamically pulled from the parent query's term id
1 Like

thx i will give it a try :pray:t3:

Hi guys,

We’ve fixed this issue in Bricks 1.11 BETA, now available as a manual download (Bricks – Account)

Please let us know if you are still experiencing issues.

You can see the full changelog here: Bricks 1.11 Changelog – Bricks

As with any beta release, please do not use it on a production/live website. It is only meant for testing in a local or staging environment.

Best regards,
Matej

I think I found another issue,

After submitting a query, the pagination module doesn’t refresh so it keeps displaying the original amount of pages before the query, then after refreshing the page it shows the proper number.

Am I doing something wrong?

THanks

Hello @danidorado. Can you point out the issue in the test enviroment that I have created: Testing Filters – Try Bricks – t76ae529

Hi,

I’ve created a custom query loop, and i cannot make the pagination work with that query.

In addition, when I selected some of the options the query strings at the url were changing but the products were not until i refreshed the url manually. so i had to add this javascript to force the reload

https://premierstreet.frameworkdm.net/test-filters/

document.addEventListener('DOMContentLoaded', function () {
    const searchButton = document.querySelector('#brxe-ubgtqj'); // Botón de búsqueda
    const paginationLinks = document.querySelectorAll('.page-numbers a[data-ajax-pagination="1"]');

    if (searchButton) {
        searchButton.addEventListener('click', function () {
            setTimeout(() => {
                const currentUrl = new URL(window.location.href);
                const currentParams = currentUrl.search;

                if (currentParams && currentParams.length > 0) {
                    // Revisamos si en el pathname existe /page/X/
                    let pathname = currentUrl.pathname;
                    // Remover la sección /page/X/ si existe. Ej: /test-filters/page/2/ => /test-filters/
                    pathname = pathname.replace(/\/page\/\d+\//, '/');
                    
                    // Reconstruimos la URL sin la sección /page/X/
                    const newUrl = currentUrl.origin + pathname + currentParams;
                    window.location.href = newUrl;
                }
            }, 10); // Ajusta este tiempo si es necesario
        });
    }

    // Comportamiento para enlaces de paginación (opcional, como antes)
    paginationLinks.forEach(link => {
        link.addEventListener('click', function (e) {
            e.preventDefault();
            setTimeout(() => {
                const newUrl = this.href;
                const newUrlParams = new URL(newUrl).search;

                if (newUrlParams && newUrlParams.length > 0) {
                    window.location.href = newUrl;
                } else {
                    window.location.href = newUrl;
                }
            }, 10);
        });
    });
});

Using this code, the query loop was working but the pagination element said “the pagination was not supported by the query loop (or this type of query loop)” can’t remember exactly

add_filter( 'bricks/setup/control_options', function( $control_options ) {
    $control_options['queryTypes']['custom_woo_query'] = esc_html__( 'Custom Woo Query', 'my-plugin' );
    return $control_options;
});

add_filter( 'bricks/query/run', function( $results, $query_obj ) {
    if ( $query_obj->object_type !== 'custom_woo_query' ) {
        return $results;
    }

    // Leer parámetros de la URL
    $paged = isset($_GET['paged']) ? max(1, (int)$_GET['paged']) : 1; // Asegura que `paged` nunca sea menor a 1
    $posts_per_page = 12;

    // Construir meta_query según los filtros
    $meta_query = [];
    $chosen_fys = isset($_GET['fys'][0]) ? (int)$_GET['fys'][0] : null;
    $chosen_fye = isset($_GET['fye'][0]) ? (int)$_GET['fye'][0] : null;
    if ($chosen_fys && $chosen_fye) {
        $meta_query = [
            'relation' => 'AND',
            [
                'key'     => 'yearstart',
                'value'   => $chosen_fys,
                'compare' => '<=',
                'type'    => 'NUMERIC'
            ],
            [
                'key'     => 'yearend',
                'value'   => $chosen_fye,
                'compare' => '>=',
                'type'    => 'NUMERIC'
            ]
        ];
    } elseif ($chosen_fys) {
        $meta_query = [
            'relation' => 'AND',
            [
                'key'     => 'yearstart',
                'value'   => $chosen_fys,
                'compare' => '<=',
                'type'    => 'NUMERIC'
            ],
            [
                'key'     => 'yearend',
                'value'   => $chosen_fys,
                'compare' => '>=',
                'type'    => 'NUMERIC'
            ]
        ];
    } elseif ($chosen_fye) {
        $meta_query = [
            'relation' => 'AND',
            [
                'key'     => 'yearstart',
                'value'   => $chosen_fye,
                'compare' => '<=',
                'type'    => 'NUMERIC'
            ],
            [
                'key'     => 'yearend',
                'value'   => $chosen_fye,
                'compare' => '>=',
                'type'    => 'NUMERIC'
            ]
        ];
    }

    // Construir tax_query para categorías
    $tax_query = [];
    $chosen_fcat = isset($_GET['fcat']) ? array_map('sanitize_text_field', $_GET['fcat']) : [];
    if (!empty($chosen_fcat)) {
        $tax_query[] = [
            'taxonomy' => 'product_cat',
            'field'    => 'slug',
            'terms'    => $chosen_fcat,
            'operator' => 'IN',
        ];
    }

    // Construir args para WP_Query
    $args = [
        'post_type'      => 'product',
        'post_status'    => 'publish',
        'posts_per_page' => $posts_per_page,
        'paged'          => $paged,
        'orderby'        => 'date',
        'order'          => 'DESC',
        'no_found_rows'  => false, // Necesario para la paginación
    ];

    if (!empty($meta_query)) {
        $args['meta_query'] = $meta_query;
    }

    if (!empty($tax_query)) {
        $args['tax_query'] = $tax_query;
    }

    // Ejecutar la consulta personalizada
    $posts_query = new WP_Query($args);

    // Ajustar los resultados para Bricks
    $query_obj->found_posts = $posts_query->found_posts; // Total de resultados
    $query_obj->max_num_pages = $posts_query->max_num_pages; // Total de páginas

    return $posts_query->posts;
}, 10, 2);

add_filter( 'bricks/query/loop_object', function( $loop_object, $loop_key, $query_obj ) {
    if ( $query_obj->object_type !== 'custom_woo_query' ) {
        return $loop_object;
    }

    global $post;
    $post = get_post( $loop_object );
    setup_postdata( $post );

    return $loop_object;
}, 10, 3 );

I was able to make it work with all those workaround without using a custom query loop and making the pagination to work, but the reload of the products had to be made via javascript triggering a url reload, to do that this was my final code

<?php 

global $saved_params;

add_filter( 'bricks/posts/query_vars', function( $query_vars, $settings, $element_id, $element_name ) {
    global $saved_params;

    if ( $element_id !== 'ifzttb' ) {
        return $query_vars;
    }

    // // Depuración
    // error_log("**** bricks/posts/query_vars triggered ****");
    // error_log("Element ID: " . print_r($element_id, true));
    // error_log("Initial Query Vars: " . print_r($query_vars, true));
    // error_log("URL Query String: " . print_r($_SERVER['QUERY_STRING'], true));

    // Decodificar la query string sin urldecode
    $params = [];
    if (!empty($_SERVER['QUERY_STRING'])) {
        parse_str($_SERVER['QUERY_STRING'], $params);
    }

    //error_log("Decoded Params: " . print_r($params, true));

    // Guardar params para usos posteriores
    static $once = false;
    if (!empty($params) && !$once) {
        $saved_params = $params;
        $once = true;
    } elseif ($once && empty($params) && !empty($saved_params)) {
        // Si no llegan parámetros ahora, usamos los guardados
        $params = $saved_params;
        //error_log("Using saved_params: " . print_r($params, true));
    }

    // Extraer parámetros
    $chosen_fys = (isset($params['fys']) && is_array($params['fys']) && !empty($params['fys'][0])) ? (int)$params['fys'][0] : null;
    $chosen_fye = (isset($params['fye']) && is_array($params['fye']) && !empty($params['fye'][0])) ? (int)$params['fye'][0] : null;
    $chosen_fcat = (isset($params['fcat']) && is_array($params['fcat'])) ? array_map('sanitize_text_field', $params['fcat']) : [];

    // error_log("Chosen FYS: " . print_r($chosen_fys, true));
    // error_log("Chosen FYE: " . print_r($chosen_fye, true));
    // error_log("Chosen FCAT: " . print_r($chosen_fcat, true));

    // Construir meta_query
    $meta_query = ['relation' => 'AND'];

    if ($chosen_fys && $chosen_fye) {
        //error_log("Case: Both FYS and FYE chosen");
        $meta_query[] = [
            'key' => 'yearstart',
            'value' => $chosen_fys,
            'compare' => '<=',
            'type' => 'NUMERIC',
        ];
        $meta_query[] = [
            'key' => 'yearend',
            'value' => $chosen_fye,
            'compare' => '>=',
            'type' => 'NUMERIC',
        ];
    } elseif ($chosen_fys) {
        //error_log("Case: Only FYS chosen");
        $meta_query[] = [
            'key' => 'yearstart',
            'value' => $chosen_fys,
            'compare' => '<=',
            'type' => 'NUMERIC',
        ];
        $meta_query[] = [
            'key' => 'yearend',
            'value' => $chosen_fys,
            'compare' => '>=',
            'type' => 'NUMERIC',
        ];
    } elseif ($chosen_fye) {
        //error_log("Case: Only FYE chosen");
        $meta_query[] = [
            'key' => 'yearstart',
            'value' => $chosen_fye,
            'compare' => '<=',
            'type' => 'NUMERIC',
        ];
        $meta_query[] = [
            'key' => 'yearend',
            'value' => $chosen_fye,
            'compare' => '>=',
            'type' => 'NUMERIC',
        ];
    } else {
        //error_log("Case: No FYS or FYE chosen");
        if (count($meta_query) === 1) {
            $meta_query = [];
        }
    }

    // Construir tax_query
    $tax_query = [];
    if (!empty($chosen_fcat)) {
        //error_log("Case: Filtering by categories");
        $tax_query[] = [
            'taxonomy' => 'product_cat',
            'field' => 'slug',
            'terms' => $chosen_fcat,
            'operator' => 'IN',
        ];
    }

    // Asignar consultas
    if (!empty($meta_query)) {
        $query_vars['meta_query'] = $meta_query;
    }

    if (!empty($tax_query)) {
        $query_vars['tax_query'] = $tax_query;
    }

    // Configurar paginación
    $query_vars['posts_per_page'] = 12;
    $query_vars['no_found_rows'] = false;

    // error_log("Final Meta Query: " . print_r($meta_query, true));
    // error_log("Final Tax Query: " . print_r($tax_query, true));
    // error_log("Final Query Vars: " . print_r($query_vars, true));
    // error_log("**** bricks/posts/query_vars ended ****");

    return $query_vars;
}, 9999, 4 );


// Usar bricks/query/run para devolver resultados manualmente vía WP_Query
add_filter('bricks/query/run', function($results, $query_obj) {
    global $saved_params;

    if (!$saved_params) {
        return $results;
    }

    $chosen_fys = (isset($saved_params['fys']) && is_array($saved_params['fys']) && !empty($saved_params['fys'][0])) ? (int)$saved_params['fys'][0] : null;
    $chosen_fye = (isset($saved_params['fye']) && is_array($saved_params['fye']) && !empty($saved_params['fye'][0])) ? (int)$saved_params['fye'][0] : null;
    $chosen_fcat = (isset($saved_params['fcat']) && is_array($saved_params['fcat'])) ? array_map('sanitize_text_field', $saved_params['fcat']) : [];

    $meta_query = ['relation' => 'AND'];
    if ($chosen_fys && $chosen_fye) {
        $meta_query[] = ['key' => 'yearstart', 'value' => $chosen_fys, 'compare' => '<=', 'type' => 'NUMERIC'];
        $meta_query[] = ['key' => 'yearend', 'value' => $chosen_fye, 'compare' => '>=', 'type' => 'NUMERIC'];
    } elseif ($chosen_fys) {
        $meta_query[] = ['key' => 'yearstart', 'value' => $chosen_fys, 'compare' => '<=', 'type' => 'NUMERIC'];
        $meta_query[] = ['key' => 'yearend', 'value' => $chosen_fys, 'compare' => '>=', 'type' => 'NUMERIC'];
    } elseif ($chosen_fye) {
        $meta_query[] = ['key' => 'yearstart', 'value' => $chosen_fye, 'compare' => '<=', 'type' => 'NUMERIC'];
        $meta_query[] = ['key' => 'yearend', 'value' => $chosen_fye, 'compare' => '>=', 'type' => 'NUMERIC'];
    } else {
        // sin filtros de año
        $meta_query = [];
    }

    $tax_query = [];
    if (!empty($chosen_fcat)) {
        $tax_query[] = [
            'taxonomy' => 'product_cat',
            'field' => 'slug',
            'terms' => $chosen_fcat,
            'operator' => 'IN',
        ];
    }

    $args = [
        'post_type'      => 'product',
        'post_status'    => 'publish',
        'posts_per_page' => 12,
        'orderby'        => 'date',
        'order'          => 'DESC',
        'meta_query'     => $meta_query,
        'tax_query'      => $tax_query,
        'no_found_rows'  => false,
    ];

    $q = new WP_Query($args);
    //error_log("**** bricks/query/run override: found " . $q->found_posts . " posts");

    // Devolvemos directamente los posts a Bricks
    return $q->posts;
}, 9999, 2);

I am not quite sure I understand why do you need to hook into ‘bricks/query/run’. You can try and import the custom query setup that I have made on the testing enviroment (Use one of the options. For the first one save the code in a .json file and import it. For the second one just copy and paste the code in a blank Bricks page. After that fiddle around with the settings as you probably have different categories):

  1. This is a .json export of the template:
{
  "id": 33,
  "name": "testing-filters",
  "title": "Testing Filters",
  "date": "2024-12-09 14:34:43",
  "date_formatted": "December 9, 2024",
  "author": {
    "name": "t76ae529",
    "avatar": "https://secure.gravatar.com/avatar/cf417b19d3f706c4e5f8909a27a4e864?s=60&d=mm&r=g",
    "url": ""
  },
  "permalink": "https://try.bricksbuilder.io/t76ae529/template/testing-filters/",
  "thumbnail": null,
  "bundles": [],
  "tags": [],
  "type": "section",
  "content": [
    {
      "id": "2eb275",
      "name": "section",
      "parent": 0,
      "children": ["354a33"],
      "settings": { "_margin": { "bottom": "150", "top": "150" } }
    },
    {
      "id": "354a33",
      "name": "container",
      "parent": "2eb275",
      "children": ["b2ea7e", "aacb38"],
      "settings": []
    },
    {
      "id": "b2ea7e",
      "name": "block",
      "parent": "354a33",
      "children": ["f060e6", "f14a7f"],
      "settings": []
    },
    {
      "id": "f060e6",
      "name": "heading",
      "parent": "b2ea7e",
      "children": [],
      "settings": {
        "text": "Filters",
        "tag": "h3",
        "_margin": { "bottom": "20" }
      }
    },
    {
      "id": "f14a7f",
      "name": "filter-checkbox",
      "parent": "b2ea7e",
      "children": [],
      "settings": {
        "filterQueryId": "81cacc",
        "filterSource": "taxonomy",
        "filterTaxonomy": "category"
      }
    },
    {
      "id": "aacb38",
      "name": "block",
      "parent": "354a33",
      "children": ["c252de", "68a872", "e59ee5"],
      "settings": { "_margin": { "bottom": "40", "top": "40" } }
    },
    {
      "id": "c252de",
      "name": "heading",
      "parent": "aacb38",
      "children": [],
      "settings": {
        "text": "Dummy Posts",
        "tag": "h3",
        "_margin": { "bottom": "20" }
      }
    },
    {
      "id": "68a872",
      "name": "block",
      "parent": "aacb38",
      "children": ["81cacc"],
      "settings": {
        "_display": "grid",
        "_gridGap": "30",
        "_gridTemplateColumns": "repeat(2, 1fr)"
      }
    },
    {
      "id": "81cacc",
      "name": "div",
      "parent": "68a872",
      "children": ["3c927b", "5f5661"],
      "settings": {
        "hasLoop": true,
        "query": {
          "post_type": ["post"],
          "posts_per_page": "2",
          "ajax_loader_animation": "ellipsis"
        }
      }
    },
    {
      "id": "3c927b",
      "name": "heading",
      "parent": "81cacc",
      "children": [],
      "settings": {
        "text": "{post_title}",
        "tag": "h6",
        "link": {
          "type": "meta",
          "useDynamicData": "{post_url}",
          "newTab": true
        },
        "_margin": { "bottom": "5" }
      }
    },
    {
      "id": "5f5661",
      "name": "text-basic",
      "parent": "81cacc",
      "children": [],
      "settings": { "text": "{post_excerpt}" }
    },
    {
      "id": "e59ee5",
      "name": "pagination",
      "parent": "aacb38",
      "children": [],
      "settings": {
        "queryId": "81cacc",
        "ajax": true,
        "_margin": { "top": "30" },
        "navigationTypographyActive": { "font-weight": "900" }
      }
    }
  ],
  "templateType": "section"
}
  1. This is .json from the Copy/Paste function that Bricks has built in it:
{
  "content": [
    {
      "id": "elddbf",
      "name": "section",
      "parent": 0,
      "children": ["rkrymn"],
      "settings": { "_margin": { "bottom": "150", "top": "150" } }
    },
    {
      "id": "rkrymn",
      "name": "container",
      "parent": "elddbf",
      "children": ["gpshcr", "fpmfsq"],
      "settings": []
    },
    {
      "id": "gpshcr",
      "name": "block",
      "parent": "rkrymn",
      "children": ["xmpuug", "zcsuhm"],
      "settings": []
    },
    {
      "id": "xmpuug",
      "name": "heading",
      "parent": "gpshcr",
      "children": [],
      "settings": {
        "text": "Filters",
        "tag": "h3",
        "_margin": { "bottom": "20" }
      }
    },
    {
      "id": "zcsuhm",
      "name": "filter-checkbox",
      "parent": "gpshcr",
      "children": [],
      "settings": {
        "filterQueryId": "umkunn",
        "filterSource": "taxonomy",
        "filterTaxonomy": "category"
      }
    },
    {
      "id": "fpmfsq",
      "name": "block",
      "parent": "rkrymn",
      "children": ["tjcxfe", "jzjcgv", "fuhjgg"],
      "settings": { "_margin": { "bottom": "40", "top": "40" } }
    },
    {
      "id": "tjcxfe",
      "name": "heading",
      "parent": "fpmfsq",
      "children": [],
      "settings": {
        "text": "Dummy Posts",
        "tag": "h3",
        "_margin": { "bottom": "20" }
      }
    },
    {
      "id": "jzjcgv",
      "name": "block",
      "parent": "fpmfsq",
      "children": ["umkunn"],
      "settings": {
        "_display": "grid",
        "_gridGap": "30",
        "_gridTemplateColumns": "repeat(2, 1fr)"
      }
    },
    {
      "id": "umkunn",
      "name": "div",
      "parent": "jzjcgv",
      "children": ["uwuqvu", "zllsei"],
      "settings": {
        "hasLoop": true,
        "query": {
          "post_type": ["post"],
          "posts_per_page": "2",
          "ajax_loader_animation": "ellipsis"
        }
      }
    },
    {
      "id": "uwuqvu",
      "name": "heading",
      "parent": "umkunn",
      "children": [],
      "settings": {
        "text": "{post_title}",
        "tag": "h6",
        "link": {
          "type": "meta",
          "useDynamicData": "{post_url}",
          "newTab": true
        },
        "_margin": { "bottom": "5" }
      }
    },
    {
      "id": "zllsei",
      "name": "text-basic",
      "parent": "umkunn",
      "children": [],
      "settings": { "text": "{post_excerpt}" }
    },
    {
      "id": "fuhjgg",
      "name": "pagination",
      "parent": "fpmfsq",
      "children": [],
      "settings": {
        "queryId": "umkunn",
        "ajax": true,
        "_margin": { "top": "30" },
        "navigationTypographyActive": { "font-weight": "900" }
      }
    }
  ],
  "source": "bricksCopiedElements",
  "sourceUrl": "https://try.bricksbuilder.io/t76ae529",
  "version": "1.11.1.1",
  "globalClasses": [],
  "globalElements": []
}
1 Like

Hey @itchycode can you aslo have a look at @danidorado’s issue?

Hi @danidorado

As you are using a custom Query loop, query filters wouldn’t work because Query Filters only support native Post loop

If you are using a Pagination element with Query Filters, please ensure it’s AJAX-enabled.