Problem/Motivation

The pager_query_add_page() function, used by the pager to prepare the querystring of the pager links, fails to reflect changes done programmatically to other querystring elements than the 'page' one (i.e. by setting specific querystring elements via the '#parameters' of the 'pager' theme). The query elements of the current request always prevail.

Proposed resolution

  • Just swap the order of the arrays merged together in the function, to ensure any parameter passed programmatically overrides the one coming from the current request.
  • Add tests.

Remaining tasks

  • Review

User interface changes

None.

API changes

None.

Original report by @mondrake

Hi,

if you invoke theme_pager() passing querystring parameters through $variables['parameters'], AND the current url has already a different value for any of the parameters passed, then the current url's value overrides any value passed programmatically.

Steps to reproduce - Drupal 8:

Run the attached code in a page callback function:

  // #1588138 - theme_pager overrides parameters passed programmatically - example

  // Example query.
  $header_0 = array(
    array('data' => 'wid'),
    array('data' => 'type'),
    array('data' => 'timestamp'),
  );
  $query_0 = db_select('watchdog', 'd')->extend('Drupal\Core\Database\Query\PagerSelectExtender')->element(0);
  $query_0->fields('d', array('wid', 'type', 'timestamp'));
  $result_0 = $query_0
    ->limit(5)
    ->orderBy('d.wid')
    ->execute();
  $rows_0 = array();
  foreach ($result_0 as $row) {
    $rows_0[] = array('data' => (array) $row);
  }
  $build['pager_table_0'] = array(
    '#theme' => 'table',
    '#header' => $header_0,
    '#rows' => $rows_0,
    '#empty' => t("There are no watchdog records found in the db"),
  );
  
  // Counter of clicks to the current pager.
  $query_params = pager_get_query_parameters();
  $pager_clicks = isset($query_params['pager_clicks']) ? ($query_params['pager_clicks'] ? $query_params['pager_clicks'] : 0) : 0;
  $build['l_pager_pager_0'] = array('#markup' => "<b>Pager clicks: $pager_clicks</b>");

  // Pager.
  $build['pager_pager_0'] = array(
    '#theme' => 'pager',
    '#element' => 0,
    '#parameters' => array(
      'pager_clicks' => ++$pager_clicks,
    ),
  );

  return $build;

The first time the page is called, with page '1' being the current, then the counter will be 0.
You would expect that each time a pager link is clicked, the requested page will be loaded, and the counter 'Pager clicks' incremented by one.
But actually the counter is only incremented on first click, and then stays on 1 for any following click.

Proposed fix - Drupal 7:

I believe the issue lays in the following piece of (Drupal 7) code of the theme_page_link() function in pager.inc

  $query = array();
  if (count($parameters)) {
    $query = drupal_get_query_parameters($parameters, array());
  }
  if ($query_pager = pager_get_query_parameters()) {
    $query = array_merge($query, $query_pager);
  }

In fact $query_pager retrieves all the parameters' values (but 'q' and 'page') from current url. PHP's array_merge behaviour is such that "if the input arrays have the same string keys, then the later value for that key will overwrite the previous one". Since $query_pager comes second in the function, the current url values do overwrite whatever passed to the function.

I would suggest to change the order of the variables passed to array_merge like this

-    $query = array_merge($query, $query_pager);
+    $query = array_merge($query_pager, $query);

so that parameters input to the function will prevail on the current url's ones.

Related:

#1778990: Merge theme_pager_link*() theme functions into theme_link()
#1786724: theme_pager does not always respect parameters option while generating pager links.
#1898474: pager.inc - Convert theme_ functions to Twig

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug
Unfrozen changes Unfrozen because it only changes the pager to fix a bug, and the issue also exists in Drupal 7.

Comments

mondrake’s picture

Version: 7.x-dev » 8.x-dev

Hi, do I understand right this has to be flagged against 8.x-dev, and only eventually backported to 7.x?

Thanks

mondrake’s picture

Status: Active » Needs review
StatusFileSize
new474 bytes

Hi,

I propose a patch for the above. I hope I am following the right process :)

Status: Needs review » Needs work

The last submitted patch, fix_theme_pager_link-1588138-2.patch, failed testing.

mondrake’s picture

Status: Needs work » Needs review
mondrake’s picture

Status: Needs review » Needs work

Given that in D8 theme_pager_link() is being removed on behalf of more general usage of theme_link, patch in #2 does not make sense any longer.

A new patch will be needed after #1778990: Merge theme_pager_link*() theme functions into theme_link() lands.

mondrake’s picture

Issue summary: View changes

typo

mondrake’s picture

Issue summary: View changes

new dependency

mondrake’s picture

Issue summary: View changes

added related issue

mondrake’s picture

Status: Needs work » Needs review

Edited issue summary to include steps to reproduce. Set to needs review to check if patch is still applicable.

mondrake’s picture

StatusFileSize
new474 bytes

and the patch

mondrake’s picture

Status: Needs review » Needs work

Still blocked by #1778990: Merge theme_pager_link*() theme functions into theme_link(), which is going to remove theme_pager_link() entirely, and move the code for determining the query parameters in a new pager_query_add_page() function.

mondrake’s picture

Issue summary: View changes

added steps to reproduce

mondrake’s picture

Title: theme_pager_link overrides parameters passed programmatically » theme_pager() overrides parameters passed programmatically

Changed title to reflect the edited issue summary.

mondrake’s picture

Status: Needs work » Needs review
mondrake’s picture

Hi,

#1778990: Merge theme_pager_link*() theme functions into theme_link() has not moved for almost for a month, so I wonder whether this could go in first.

star-szr’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests, +Needs backport to D7

@mondrake - The bug report looks great and the fix makes sense. A couple things worth mentioning to move this issue forward.

  1. This bugfix definitely needs tests (and I've set the status to 'needs work' because tests are a core gate). The good news is that it looks like you (or someone else) already has a pretty good guide for writing an automated test via your steps to reproduce code in the issue summary. Contributor doc on writing automated tests: http://drupal.org/node/1468170

    Adding automated test coverage will prove that this bug exists, and help prevent regressions in the future.

  2. I wouldn't say this is blocked on #1778990: Merge theme_pager_link*() theme functions into theme_link(). It would certainly conflict with that patch (and with #1898474: pager.inc - Convert theme_ functions to Twig and possibly others). As long as the patch here is kept up to date, there's really not much to worry about and the order in which any of these patches gets committed is not that important. If this patch got in first, #1778990: Merge theme_pager_link*() theme functions into theme_link() would need to be rerolled, and vice versa. Same with #1898474: pager.inc - Convert theme_ functions to Twig. Welcome to core contribution :)

In short, this is an independent bug that deserves its own issue, and your best bet at seeing this issue resolved is to add that test coverage and keep the patch here up to date.

Tagging for tests and D7 backport.

mondrake’s picture

Updating summary to reflect input in #12.

I am not sure how a test could be written here: I am pretty sure this bug will not appear in the existing core codebase (otherwise it would have popped up before), as there seems to be no invocation of theme('pager', ...) with additional 'parameters' specified, there.

Rather, this is a matter of providing correct functionality to contrib/custom modules that may want to use the 'parameters' variable when invoking theme('pager', ...), see e.g. Pagerer.

So, I am willing to develop a test coverage for this, but I do not know how to setup the test so that a drupalGet call could retrieve the page callback as specified in the issue summary. Any input appreciated.

mondrake’s picture

Issue summary: View changes

made reference to theme_pager() instead of theme_pager_link()

star-szr’s picture

The fact that this bug has not popped up before is even more reason to add test coverage :)

If the bug depends on having a page callback and cannot be reproduced by just calling theme('pager', …) with additional 'parameters', then you can create a small test module with page callbacks etc. to test against.

See for example block_test.module:
http://drupalcode.org/project/drupal.git/tree/refs/heads/8.x:/core/modul...

If you'd like more help, feel free to stop by Drupal core contribution mentoring:
http://drupal.org/core-office-hours

star-szr’s picture

mondrake’s picture

Thanks :)

I was coming to the same conclusion after some browsing... I will write a pager_test module and see how this moves forward.

Cheers

mondrake’s picture

So.

The test_only patch should prove the bug described in the summary.

The full patch should fix the bug and test should be OK.

(Theoretically :))

Let's see what bot thinks first of all.

mondrake’s picture

Status: Needs work » Needs review

Oh yes, and the status change.

star-szr’s picture

Status: Needs review » Needs work

The test looks pretty reasonable, good work! A few minor nitpicks ahead:

+++ b/core/modules/system/lib/Drupal/system/Tests/Pager/PagerQueryParametersTest.phpundefined
@@ -0,0 +1,72 @@
+ * Definition of Drupal\system\Tests\Pager\PagerParametersTest.

The format for these changed, should be:
Contains \Drupal\system…

+++ b/core/modules/system/lib/Drupal/system/Tests/Pager/PagerQueryParametersTest.phpundefined
@@ -0,0 +1,72 @@
+use SimpleXMLElement;

I don't think this use is needed for your tests, should be removed.

+++ b/core/modules/system/lib/Drupal/system/Tests/Pager/PagerQueryParametersTest.phpundefined
@@ -0,0 +1,72 @@
+  ¶

+++ b/core/modules/system/tests/modules/pager_test/pager_test.moduleundefined
@@ -0,0 +1,67 @@
+  ¶
...
+  ¶

Few bits of trailing whitespace, please remove per http://drupal.org/coding-standards#indenting.

+++ b/core/includes/pager.incundefined
@@ -355,7 +355,7 @@ function theme_pager_link($variables) {
-    $query = array_merge($query, $query_pager);
+    $query = array_merge($query_pager, $query);

The only other thing I would recommend at this time would be to add a comment above this line you're changing to explain the reasoning behind the order of arguments passed to array_merge().

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new5.59 KB

Thanks for review @Cottser!

New patch, with all input in #19 addressed.

star-szr’s picture

For the "Contains \Drupal\system…" the initial slash wasn't a typo, please see http://drupal.org/node/1354#file.

Interdiffs are great, too :)

mondrake’s picture

StatusFileSize
new735 bytes
new5.59 KB

Thanks for you patience, @Cottser!

Here we go.

mondrake’s picture

mondrake’s picture

Issue tags: -Needs tests
StatusFileSize
new5.27 KB
new6.75 KB
jwilson3’s picture

+++ b/core/includes/pager.incundefined
@@ -355,7 +355,12 @@ function theme_pager_link($variables) {
-    $query = array_merge($query, $query_pager);
+    // $query_pager holds the query parameters of the current request; in
+    // case there are colliding parameter values coming from the $variables
+    // array, the latter should prevail. array_merge() overwrites $query_pager
+    // keys with $query keys, and returns the final merged $query array to be
+    // used for building the link.
+    $query = array_merge($query_pager, $query);

Do we really nee 5 lines of comments for this explanation? I think it would be much improved by clearer field names, and a brief plain english comment.

Something like this:

  // Merge the specified pager query parameters with any paramaters from the
  // current request. In case of collision, parameters from the current request
  // take precedence.
  if ($current_request_query = pager_get_query_parameters()) {
    $query = array_merge($current_request_query, $query);
  }

thoughts?

jwilson3’s picture

Status: Needs review » Needs work
mondrake’s picture

Why not, however the precedence is taken by the input values, not the current request's ones (that's the actual reason of this entire issue :))

mondrake’s picture

Status: Needs work » Needs review

.. and status change

jwilson3’s picture

Oops, thanks,... see, it *was* confusing!

mondrake’s picture

Status: Needs review » Needs work
Issue tags: +Needs backport to D7

The last submitted patch, theme_pager_query_parameters-1588138-27.patch, failed testing.

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new6.76 KB
mondrake’s picture

mondrake’s picture

Issue summary: View changes

edit based on feedback in #12

mondrake’s picture

Status: Needs review » Needs work

The last submitted patch, 32: theme_pager_query_parameters-1588138-32.patch, failed testing.

mondrake’s picture

Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new6.1 KB
new7.45 KB

Rerolled / refreshed.

Status: Needs review » Needs work

The last submitted patch, 36: pager_query_parameters-1588138-36.patch, failed testing.

mondrake’s picture

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new6.1 KB

Test was cancelled

The last submitted patch, 36: pager_query_parameters-1588138-36.patch, failed testing.

mondrake’s picture

#40 refers to the failure of test in #36

#39 is green and up for review :)

Status: Needs review » Needs work

The last submitted patch, 42: pager_query_parameters-1588138-42.patch, failed testing.

mondrake’s picture

mondrake’s picture

Status: Needs work » Needs review
mondrake’s picture

jwilson3’s picture

Nothing jumps out at me as wrong with the latest patch.

But because this is such an obscure and confusing problem, do you think you could provide a patch file that contains *just* the test (eg suffix the filename with -test-only.patch) to see what Drupal errors on currently?

Hat tip: https://twitter.com/xjmdrupal/status/195113152759865344

jwilson3’s picture

+++ b/core/includes/pager.inc
@@ -440,8 +440,11 @@ function pager_query_add_page(array $query, $element, $index) {
+  // Merge the pager query parameters specified in variables, with any
+  // parameters coming from the current request. In case of collision,
+  // parameters specified in variables take precedence.

Oops, I take it back.

The comma placement here makes this difficult to read and the terminology "in variables" is abstract enough to be confusing. Which variables are we talking about?

Could we change this comment again to something like:

Merge the query parameters passed to this function with the parameters from the current request. In case of collision, the parameters passed into this function take precedence.

This small change will give you and opportunity for a new patch, and a test-only patch on the same comment.

jhedstrom’s picture

Status: Needs review » Needs work

Needs work based on #48.

mondrake’s picture

Status: Needs work » Needs review
StatusFileSize
new4.88 KB
new5.68 KB

Rerolled and addressed comments in #47 and #48. Sorry no interdiff.

The last submitted patch, 50: pager_query_parameters-1588138-50-test-only.patch, failed testing.

star-szr’s picture

Title: theme_pager() overrides parameters passed programmatically » pager_query_add_page() overrides parameters passed programmatically

Based on the latest patch this title seems more accurate to me. Perhaps the issue summary should be updated a bit. Happy to see this is still moving :)

mondrake’s picture

Issue tags: +Needs issue summary update, +Needs backport to 7.x

I'll try to update the IS. Readded backport to 7 tag which got lost.

mondrake’s picture

Issue summary: View changes
Issue tags: -Needs issue summary update
jhedstrom’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community

I've added a beta phase evaluation to the issue summary. The patch in #50 looks good to me. Marking RTBC.

  • alexpott committed b7a6866 on 8.0.x
    Issue #1588138 by mondrake: pager_query_add_page() overrides parameters...
alexpott’s picture

Version: 8.0.x-dev » 7.x-dev
Status: Reviewed & tested by the community » Patch (to be ported)

Committed b7a6866 and pushed to 8.0.x. Thanks!

Thanks for adding the beta evaluation to the issue summary.

mondrake’s picture

Title: pager_query_add_page() overrides parameters passed programmatically » pager_query_add_page() [in D7, theme_pager_link()] overrides parameters passed programmatically
Status: Patch (to be ported) » Needs review
Issue tags: -Needs backport to 7.x
StatusFileSize
new4.52 KB

D7 ported patch.

mondrake’s picture

StatusFileSize
new4.52 KB
new839 bytes

Whoops

  • alexpott committed b7a6866 on 8.1.x
    Issue #1588138 by mondrake: pager_query_add_page() overrides parameters...

  • alexpott committed b7a6866 on 8.3.x
    Issue #1588138 by mondrake: pager_query_add_page() overrides parameters...

  • alexpott committed b7a6866 on 8.3.x
    Issue #1588138 by mondrake: pager_query_add_page() overrides parameters...
jonmcl’s picture

I believe if this fix is implemented, there is going to be a problem with the Date module's implementation of Views exposed select filters. I'm not sure if it is correct or incorrect, but that module generated a form element name that is like name="field_example_date[value][year]" and this issue's fix will cause a fatal error:

Fatal error: Cannot create references to/from string offsets nor overloaded objects in .../includes/common.inc on line 6754

I created an issue on the Date module: #2821200: Unusual form element name attribute for exposed Views select filter?

So it appears that the array_merge bug, outlined in this issue, was allowing the Date module's three-dimensional form element name to work. Fixing this issue will cause the link generated to trigger a fatal error because of the way the Date module (incorrectly or correctly) constructs the form element's name.

  • alexpott committed b7a6866 on 8.4.x
    Issue #1588138 by mondrake: pager_query_add_page() overrides parameters...

  • alexpott committed b7a6866 on 8.4.x
    Issue #1588138 by mondrake: pager_query_add_page() overrides parameters...
tommyblopes’s picture

Hi,

I am currently facing this issue on Drupal 7.54, and patch #59 works like a charm.

I can see it was merged into 8.x, but why hasn't it been merged into 7.x ? We need this bug resolved in the core.

Thanks

Status: Needs review » Closed (outdated)

Automatically closed because Drupal 7 security and bugfix support has ended as of 5 January 2025. If the issue verifiably applies to later versions, please reopen with details and update the version.