When a exposed filter parameter is given a value in a query string that is interpreted as an array, the resulting pager URLs contain incorrect and unneeded parameter values. The situation is such that continuing to clicking through the pager URLs will cause the resultant URLs to contain additional parameter values, particularly if those values are integers.
Example scenarios
- checkboxes in exposed filter
- complex filters from contrib modules such as Geofield proximity filter
This appears to be a direct result of work done for https://www.drupal.org/node/2651102.
Steps to reproduce (standard profile + devel generate)
This can most easily be seen on a vanilla standard profile install by making a small modification to the Content overview view and generating a sufficient amount of example content.
1. Generate 10 Tag vocabulary terms.
2. Generate 500 Article nodes (these should be assigned some of the generated tags).
3. Edit the Content overview view (/admin/structure/views/view/content
). Add 'Tags (field_tags)' as an filter, expose it, and allow multiple selections. Save.
4. Visit the Content overview (/admin/content
). You should see 'Tags (field_tags)', and a multiple select widget with your randomly generated tags.
5. Select one tag and filter the content overview. See if you have selected enough content to generate a pager (i.e. you have filtered more than 50 items). If you do not see a pager, select two tag items and filter. Repeat until you see the pager.
6. Note the URL. It should look something like
/admin/content?title=&status=All&langcode=All&field_tags_target_id%5B%5D=16&field_tags_target_id%5B%5D=18
where 16 and 18 correspond to the tags you have selected for the filter. The numbers themselves are not important, but the fact that they are numeric values is important.
7. Click a pager link to the second page. Your URL will look like the following:
/admin/content?title=&status=All&langcode=All&field_tags_target_id%5B0%5D=16&field_tags_target_id%5B1%5D=18&0=16&1=18&page=1
note the portion of the query string &0=16&1=18
8. Click any pager link, including returning to the first page. Your URL will now look like:
/admin/content?title=&status=All&langcode=All&field_tags_target_id%5B0%5D=16&field_tags_target_id%5B1%5D=18&0=16&1=18&2=16&3=18&page=0
note &0=16&1=18&2=16&3=18
9. Continue to click pager links. You will see the URL grow with each successive link load.
Cause
The root of this appears to be work done for https://www.drupal.org/node/2651102, specifically the work done to handle checkboxes at the end of ViewsExposedForm: http://cgit.drupalcode.org/drupal/diff/core/modules/views/src/Form/Views...
This code takes any exposed form values and, if the value is an array, breaks them out to individual values, flattened, in $view->exposed_raw_input.
This code might actually work correctly for checkboxes - I haven't checked that - but it is also applying to any views exposed filter form value that is an array, which can include selects with multiple values allowed. The code block sees that the value is say of the form ['field_tags_target_id' => [16, 18]], and flattens it out as $view->exposed_raw_input = [16 => 16, 18 => 18]. This means that a query string of the form field_tags_target_id%5B0%5D=16&field_tags_target_id%5B1%5D=18
results in $view->exposed_raw_input = [16 => 16, 18 => 18]
This becomes problematic when the pager builds its links. When the view element is being built, when rendering the pager, $exposed_input is passed to the pager render function (see DisplayPluginBase.php: http://cgit.drupalcode.org/drupal/tree/core/modules/views/src/Plugin/vie...), and $exposed_input is generated from $view->exposed_raw_input.
These parameter values get passed to pager_query_add_page(). The real query parameters are also included here, from the URL in question. These get merged together.
This means that the 'real' query values ['field_tags_target_id' => [16, 18]] get merged with the flattened values [16 => 16, 18 => 18] when generating the pager link. Numeric keys get renumbered when doing an array merge, so this becomes ['field_tags_target_id' => [16, 18], 0 => 16, 1 => 18]. This in turn is used to generate the pager link, resulting in field_tags_target_id%5B0%5D=16&field_tags_target_id%5B1%5D=18&0=16&1=18
This happens on every subsequent generation of the pager links. The numeric values remain in the query string, because they were there to begin with, and ViewsExposedForm incorrectly flattens values on every pass, which means new instances of these parameters get added to the query string every time.
Implications
Geofield proximity filter - pager is not usable.
Generally: apart from this being messy, this poses an issue for these views when they are crawled, say by a search engine spider. The spider finds a pager link and follows it, as it should; the resulting URL is a new URL with additional query parameters. Because it is a new URL, the spider follows its pager links, generating yet more new URLs. This can continue indefinitely unless the spider is programmed to recognize unending loops like this (taking into account a new URL is generated each time) and bail out.
This has both SEO implications and performance implications. As each one of these URLs is uncached, a spider crawling a site where this situation presents can experience considerable performance issues as it attempts to generate these view displays.
Comments
Comment #4
gooddev CreditAttribution: gooddev as a volunteer commentedI was facing the same issue for a custom view in D8 and wrote a patch which i hope solved the problem
Comment #5
gooddev CreditAttribution: gooddev as a volunteer commentedSorry, wrong path in patch, this one works:
Comment #6
andypostComment #8
chanderbhushan CreditAttribution: chanderbhushan as a volunteer and at gai Technologies Pvt Ltd for gai Technologies Pvt Ltd commented@dev.tim may be drupal version but i am not sure, i have created patch, thanks
Comment #9
LendudeThis still needs a test.
Also this seems to change the output URL in such a way that empty parameters are lost, which is good probably, but its why the test fails in #5
-'?status=All&type=All&langcode=All&items_per_page=5&order=changed&sort=asc&title=&page=2'
+'?status=All&type=All&langcode=All&items_per_page=5&order=changed&sort=asc&page=2
Comment #11
adminMN2023 CreditAttribution: adminMN2023 commented@dev.tim -
do you think the patch for Drupal 8 in your comment above (#5) will work for my D8 issue: https://imgur.com/O0saE4i (apologies if this is not the proper place to ask this - but if the patch might be viable - I'm more than willing to test it on a dev server.)The patch (@dev.tim #5 above) did work for my site: Drupal 8.5.5 | PHP 7.1.19 | 5.5.5-10.1.34-MariaDBComment #13
brayfe CreditAttribution: brayfe as a volunteer commentedUse the patch in #5 against Drupal 8.5.6 and it applied cleanly, and fixed the issue with my checkboxes not persisting across pager links. I also am using an ajax form and Better Exposed Filters 8.x-3.0-alpha4.
Comment #14
adminMN2023 CreditAttribution: adminMN2023 commentedThis seems like an easy fix (says the non-programmer). Any reason why this has not been implemented - or is it more complicated than that?
Comment #15
jagermonster CreditAttribution: jagermonster commentedWhat is required for this to be merged into core for the next release? Views pagination is currently broken because of this so it really needs to be resolved.
As Lendude mentioned in #9 the reason this patch failed was because it removed the empty get paramaters from the request url. Should this patch be updated to include the empty paramaters or should the test be updated to not care about the empty paramaters? I can get this fixed and a new patch uploaded but I need clarification on the above question.
Comment #16
samuel.mortenson@jagermonster From my reading of the issue - #9 needs to be addressed and a new test needs to be added to cover the bug this issue fixes before this can be put back into review and eventually marked as RTBC.
Comment #17
jagermonster CreditAttribution: jagermonster commentedI have found the actual problem that cause the issue in the first place.
In core/modules/views/src/Form/ViewsExposedForm.php line 178 there is some code that looks like it resolves another issue with checkboxes
This code changes the array keys. e.g. for a date exposed filter created[min]=21/01/2018&created[max]=22/01/2018 gets changed to created[0]=21/01/2018&created[1]=22/01/2018
So the index min and max are changed to 0 and 1
It seems that the issue described in https://www.drupal.org/node/342316 has been resoved on Drupal 8.7-dev
This bug can be fixed by removing that checkbox code so it becomes
Comment #18
mpolishchuck CreditAttribution: mpolishchuck at Alpha Web Group for Dazzle commentedI've found the patch posted to already closed issue - 2943163#16.
This works for me with Drupal 8.6.7.
URLs look consistent for filters and for filters+pager links.
I'm wondering why it's not posted to some open issue and not considered to merge. Maybe I've missed something?
Comment #19
mpolishchuck CreditAttribution: mpolishchuck at Alpha Web Group for Dazzle commentedSeems like a patch which I've mentioned in #18 just fixes small inconsistency. But globally we still this trouble. It does not work properly if we have checkboxes inside of subvalue of exposed filter. So I propose to make views exposed raw input handling recursive. Attaching the patch. At least it works for me.
Comment #20
AdamPS CreditAttribution: AdamPS at AlbanyWeb commentedThis issue also applies to the proximity filter from the Geofield contrib module. Currently the pager is not usable with that filter, and the patches here fix that.
The patch in #5 works well for me (and #8 seems to be identical). It's simple, easy to review, and reuses an existing method.
The patch in #19 mostly worked. However I got an unwanted extra query parameter from a field that was not actually exposed. The changes from this patch are complex.
@mpolishchuck Please can you explain why you posted patch #19? Do the earlier patches not work for your scenario? If so, please can you upload an export of the view config (simplified as much as possible) where #5 fails, but #19 works?
The next step for this issue is to add a test. A good starting point would be a view that causes this bug - using core only and simplified as much as possible. If anyone has an example, please upload a file with an export of the view config.
Comment #21
vtkachenko CreditAttribution: vtkachenko commentedAn workaround for me for Date range is using 2 filters "From" and "To" instead of 1 with "Between".
Comment #22
andrewbelcher CreditAttribution: andrewbelcher at FreelyGive commentedSo the patch in #5 uses
ViewExecutable::getExposedInput
which pulls directly from the request and could contain other non-filter related query parameters (it simply excludespage
andq
). It also pulls data from the session if nothing is given locally.#19 is handling recursively processing the values, but explicitly uses the processed raw input (yes, a bit of an oxymoron). I'm not sure which should be the 'right' approach...
Comment #24
joep.hendrix CreditAttribution: joep.hendrix at CompuBase Internet Solutions commented#5 works nicely in my case where I am using the geofield proximity filter.
Comment #25
adminMN2023 CreditAttribution: adminMN2023 commentedIs there a chance this has been fixed in Drupal 8.7.0?
I saw no reference to it in the release note - but the problem did not recur after updating to D 8.7.0. I did not have to go back in and apply the patch, #5 above, to the site after updating.
I did confirm that the un-patched code does still exist with the new Drupal version, so the thought seems to be that a change was made elsewhere that made the un-patched code function properly? I'm guessing, but was hoping someone else would take a look at it.
Comment #26
Lendude@CB1_Dru1 maybe #2987598: View's pagination doesn't carry the exposed filter value had an effect on this? If you can't reproduce this anymore, please feel free to close this issue.
Comment #27
AdamPS CreditAttribution: AdamPS at AlbanyWeb commented@CB1_Dru1 Confirmed - now works for me without the patch for geofield proximity filter.
Looks like this has been fixed by #2987598: View's pagination doesn't carry the exposed filter value
Comment #28
adminMN2023 CreditAttribution: adminMN2023 commented@lendude - I see it's been marked as fixed. Awesome!