Complex query breaks paginator

Using cakephp 4. I have a complex search form with check boxes, date ranges, and text input controls. The user may enter none, some, or all of the inputs available on the form. I build the query with a series of ‘if’ statements to check whether the user entered a value for the search and create the query parameters. For example assume the table is Products with a long list of attributes for each record like color( red, green, blue, yellow, orange, etc), size (s,m,lg), manufacture date, price, description.

The form is created with checkboxes for each color and size, low and high manufacture date range, low and high price range, text input for description keywords.

To create the controller builds the query like this:

$query = $this->Products->find();

   $query = $query->where(['color =' => '"red"']);

  $query = $query->where(['color =' => '"blue"']);

  $query = $query->where(['color =' => '"yellow"']);
  $query = $query->where(['size =' => '"small"']);

   $query = $query->where(['description like' => "% keyword %"]);

$products = $this->paginate($query);

The search can be repeated, so the form posts back to the original template to display results. In the controller I test if the incoming request was a first request or a post back request:

 if (!$this->request->is('get') ) {

//This is a postback request from a 'form submit' action query code
$products = $this->paginate($query);

} else {

//This is an initial page access, build a blank form
$product = "";


This approach seems to break the paginator.
When I click on a page number or ‘next’, none of the query parameters are preserved and I get a blank results. I’m thinking is has something to do with page and limit parameters of the query, but I’m stuck.

Thanks for any assistance. Much appreciated!

The same params should always be in the next request, i suggest you use the search plugin, or at least use the Post/Redirect/Get pattern and build the query based on $_GET query params

I switched the form ‘type’ to ‘get’ and now use $this->getQuery() to get the parameters passed by $_GET.
When I click the ‘submit’ button, all of the search parameters are sent in the url even the blank ones. The uRL looks something like:

The pagination helper only merges the non-blank parameters into the url. Which works for my use case but is different than the ‘submit’ button control behaves. Clicking on page 2 from the above search creates a url like:

The Paginator->limitControl() isn’t merging any of the parameters into the url and breaks the page. All of the search parameters are lost. Changing the limit control dropdown after the first search above creates a url like:

I looked at the documentation for Paginator->limitControl() method, but I don’t see an option to merge get parameters or ignore them…did I miss the option?

The paginator control helper has a test that checks that the generated URL keeps the data

The limit control creates a form and that could collide if you put it inside another form, so maybe test that your limit control is outside of your search form

Thanks for the suggestion raul. It looks like this may be the root of the issue. When I look at the source html created by $this-limitControl() it appears the relative url in the ‘form’ tag is being html encoded. I see something like this:

<form method="get" accept-charset="utf-8" action="/controller/search?size=small&amp;color=red&amp;keyword=hoodie&amp;page=2"><label for="limit"></label>

‘amp;’ follows each ‘&’ in the url. I’m pretty sure that’s what breaks the postback.

Is this a reported bug with a fix?

That seems normal to me. Its part of an encoding maybe.
If you submit that form it should work normally

I have found a satisfactory resolution so I am posting it for the future person looking at similar problems.

What I have learned:

The url in the form action=“url” does not preserve the url parameters when the submit button is clicked. In my previous code example

<form method="get" accept-charset="utf-8" action="/controller/search?size=small&amp;color=red&amp;keyword=hoodie&amp;page=2"><label for="limit"></label>

the size, color, keyword, and page parameters are not sent to the server in the url requested when the submit button is pressed.

My solution is to build a dropdown with the url and parameters in each and request the url onChange like this:

$uri = $this->request->getUri();

<ul class="pagination">
        <select name="limitcont" onchange="location = this.value;">
            <option value=""></option>
            <option value="<?= $uri . '&limit=50' ?>">50</option>
            <option value="<?= $uri . '&limit=100' ?>">100</option>
            <option value="<?= $uri . '&limit=200' ?>">200</option>
            <option value="<?= $uri . '&limit=500' ?>">500</option>
            <option value="<?= $uri . '&limit=1000' ?>">1000</option>
        <?= $this->Paginator->first('<< ' . __('first')) ?>

Maybe there’s a more elegant way to accomplish the same thing. But I got other projects to complete and I want to get paid on this one.

best regards