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

  1. checkboxes in exposed filter
  2. 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.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

timcosgrove created an issue. See original summary.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

gooddev’s picture

I was facing the same issue for a custom view in D8 and wrote a patch which i hope solved the problem

gooddev’s picture

andypost’s picture

Status: Active » Needs review

Status: Needs review » Needs work
chanderbhushan’s picture

@dev.tim may be drupal version but i am not sure, i have created patch, thanks

Lendude’s picture

Issue tags: +Needs tests

This 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

Status: Needs review » Needs work
adminMN2023’s picture

@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-MariaDB

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

brayfe’s picture

Use 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.

adminMN2023’s picture

This seems like an easy fix (says the non-programmer). Any reason why this has not been implemented - or is it more complicated than that?

jagermonster’s picture

What 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.

samuel.mortenson’s picture

@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.

jagermonster’s picture

I 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

    foreach ($values as $key => $value) {
      if (!empty($key) && !in_array($key, $exclude)) {
        if (is_array($value)) {
          // Handle checkboxes, we only want to include the checked options.
          // @todo: revisit the need for this when
          //   https://www.drupal.org/node/342316 is resolved.
          $checked = Checkboxes::getCheckedCheckboxes($value);
          foreach ($checked as $option_id) {
            $view->exposed_raw_input[$key][] = $value[$option_id];
          }
        }
        else {
          $view->exposed_raw_input[$key] = $value;
        }
      }
    }

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

 foreach ($values as $key => $value) {
      if (!empty($key) && !in_array($key, $exclude)) {
          $view->exposed_raw_input[$key] = $value;
      }
  }
mpolishchuck’s picture

I'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?

mpolishchuck’s picture

Seems 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.

AdamPS’s picture

Title: Views pager links incorrectly handle exposed filters that allow multiple numeric values » Views pager links incorrectly handle complex exposed filters
Issue summary: View changes

This 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.

vtkachenko’s picture

An workaround for me for Date range is using 2 filters "From" and "To" instead of 1 with "Between".

andrewbelcher’s picture

So the patch in #5 uses ViewExecutable::getExposedInput which pulls directly from the request and could contain other non-filter related query parameters (it simply excludes page and q). It also pulls data from the session if nothing is given locally.

  1. It will preserve non filter related query parameters which is probably a good thing
  2. It will only use session data if there are no additional query parameters which is probably OK

#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...

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

joep.hendrix’s picture

#5 works nicely in my case where I am using the geofield proximity filter.

adminMN2023’s picture

Is 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.

Lendude’s picture

@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.

AdamPS’s picture

Status: Needs work » Fixed
Issue tags: -Needs tests
Related issues: +#2987598: View's pagination doesn't carry the exposed filter value

Is there a chance this has been fixed in Drupal 8.7.0?

@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

adminMN2023’s picture

@lendude - I see it's been marked as fixed. Awesome!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.