Problem/Motivation

The exposed sort identifiers are not configurable in the same way as the exposed filters identifiers. This leaks some internal IDs in the URL and that we can consider part of the UI, even is in the browser address bar, not in the page. For example, with Search API, the user might see such parameter/value in the query string &sort_by=search_api_relevance, which is not elegant at all.

Proposed resolution

Allow the exposed sort identifiers to be configured.

Remaining tasks

None.

User interface changes

New, textfield element in the exposed sort configuration subform.

API changes

New parameter string $handler_type in \Drupal\views\Plugin\views\display\DisplayPluginBase::isIdentifierUnique() signature.

Data model changes

None.

Release notes snippet

When exposing a sort in the exposed form, the site builder is able to configure the identifiers that are exposed in the URL as query parameter values.

Original report

While working on a Rest API via Views, I noticed that exposed Sorts could not have custom identifier likes Filters could. As such, I have created a (probably terrible) patch to add in this functionality.

Please comment on items that need to be changed or fixed and I will be happy to do so!

CommentFileSizeAuthor
#50 2897638-50.patch38.27 KBKapilV
#48 2897638-48.patch38.27 KBGiuseppe87
#43 2897638-43.patch39.01 KBclaudiu.cristea
#42 2897638-42.patch39.1 KBclaudiu.cristea
#42 2897638-42.interdiff.txt9.31 KBclaudiu.cristea
#40 2897638-40.patch41.47 KBclaudiu.cristea
#40 2897638-40.interdiff.txt6.5 KBclaudiu.cristea
#35 2897638-35.patch35.88 KBclaudiu.cristea
#35 2897638-35.interdiff.txt562 bytesclaudiu.cristea
#33 2897638-33-8.9.x.patch35.15 KBclaudiu.cristea
#33 2897638-33.patch35.87 KBclaudiu.cristea
#33 2897638-33.interdiff.txt2.95 KBclaudiu.cristea
#32 2897638-32.patch18.74 KBclaudiu.cristea
#32 2897638-32.interdiff.txt34.26 KBclaudiu.cristea
#31 2897638-31.patch29.36 KBclaudiu.cristea
#31 2897638-31.interdiff.txt2.14 KBclaudiu.cristea
#28 2897638-26-8.9.x.patch28.59 KBclaudiu.cristea
#25 2897638-25.patch28.52 KBclaudiu.cristea
#25 2897638-25.interdiff.txt655 bytesclaudiu.cristea
#24 2897638-24.patch28.41 KBclaudiu.cristea
#24 2897638-24.interdiff.txt25.83 KBclaudiu.cristea
#21 2897638-21.patch13.18 KBclaudiu.cristea
#21 2897638-21.interdiff.txt2.17 KBclaudiu.cristea
#20 2897638-19.patch12.1 KBclaudiu.cristea
#20 2897638-19.interdiff.txt5.95 KBclaudiu.cristea
#18 2897638-18.patch6.65 KBclaudiu.cristea
#18 2897638-18.interdiff.txt501 bytesclaudiu.cristea
#15 modify_views_exposed_sort_identifiers-2897638-15.patch6.15 KBdrclaw
#14 modify_views_exposed_sort_identifiers-2897638-14.patch6.17 KBistavros
#13 modify_views_exposed_sort_identifiers-2897638-13.patch6.18 KBistavros
#12 modify_views_exposed_sort_identifiers-2897638-12.patch6.18 KBistavros
#4 modify_views_exposed_sort_identifiers-2897638-4.patch6.21 KBPaulDinelle
#2 modify_views_exposed_sort_identifiers-2897638-1.patch6.21 KBPaulDinelle

Issue fork drupal-2897638

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

PaulDinelle created an issue. See original summary.

PaulDinelle’s picture

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.

PaulDinelle’s picture

Re-rolled patch for 8.5.x-dev.

drclaw’s picture

Assigned: PaulDinelle » Unassigned
Status: Active » Reviewed & tested by the community

This is great! Code looks good and works as advertised!

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 4: modify_views_exposed_sort_identifiers-2897638-4.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Lendude’s picture

Adding this to get this in line with exposed filters makes sense.

+++ b/core/modules/views/src/Plugin/views/sort/SortPluginBase.php
@@ -49,6 +49,7 @@ protected function defineOptions() {
+        'identifier' => ['default' => ''],

the additional option needs to be added to the config schema, hence the fails. This also means that this would need an upgrade path.

Also, this needs tests.

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.

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.

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.

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

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

istavros’s picture

Changed the patch to apply to core 8.8.2

istavros’s picture

istavros’s picture

drclaw’s picture

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

claudiu.cristea’s picture

Assigned: Unassigned » claudiu.cristea
Issue tags: +Needs change record
+++ b/core/modules/views/src/Plugin/views/sort/SortPluginBase.php
@@ -225,6 +278,7 @@ public static function trustedCallbacks() {
+      'identifier' => $this->options['id'],

This is a new config key and needs a schema. It might be the source of so many failures.

Assigning to try a fix.

claudiu.cristea’s picture

Status: Needs review » Needs work

The last submitted patch, 18: 2897638-18.patch, failed testing. View results

claudiu.cristea’s picture

Added a tested upgrade path.

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
2.17 KB
13.18 KB

This is weird, I cannot replicate locally some of the failures. Still fixed one.

Status: Needs review » Needs work

The last submitted patch, 21: 2897638-21.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

claudiu.cristea’s picture

  1. We need to fix also the default configuration.
  2. +++ b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
    @@ -157,19 +157,17 @@ public function query() {
    +        elseif (!empty($sort->options['expose']['identifier']) && $sort->options['expose']['identifier'] == $sort_by) {
    +          if (isset($exposed_data['sort_order']) && in_array($exposed_data['sort_order'], ['ASC', 'DESC'])) {
    

    Let's be strict.

  3. +++ b/core/modules/views/src/Plugin/views/sort/SortPluginBase.php
    @@ -207,7 +208,59 @@ public function buildExposeForm(&$form, FormStateInterface $form_state) {
    +  public function validateExposeForm($form, FormStateInterface $form_state) {
    +    $identifier = $form_state->getValue(['options', 'expose', 'identifier']);
    +    $this->validateIdentifier($identifier, $form_state, $form['expose']['identifier']);
    +  }
    ...
    +  protected function validateIdentifier($identifier, FormStateInterface $form_state = NULL, &$form_group = []) {
    +    $error = '';
    +    if (empty($identifier)) {
    +      $error = $this->t('The identifier is required if the sort is exposed.');
    +    }
    +    elseif ($identifier == 'value') {
    +      $error = $this->t('This identifier is not allowed.');
    +    }
    +    elseif (preg_match('/[^a-zA-z0-9_~\.\-]/', $identifier)) {
    +      $error = $this->t('This identifier has illegal characters.');
    +    }
    +
    +    if ($form_state && !$this->view->display_handler->isIdentifierUnique($form_state->get('id'), $identifier)) {
    +      $error = $this->t('This identifier is used by another handler.');
    +    }
    +
    +    if (!empty($form_state) && !empty($error)) {
    +      $form_state->setError($form_group, $error);
    +    }
    +    return $error;
       }
    

    This code is almost the same with the one from filter. Let's avoid code duplication.

  4. +++ b/core/modules/views/src/ViewsConfigUpdater.php
    @@ -477,4 +480,44 @@ protected function mapOperatorFromSingleToMultiple($single_operator) {
    +    if ($handler_type === 'sort' && !empty($handler['exposed'])) {
    
    +++ b/core/modules/views/tests/fixtures/update/update_2897638_sort_identifier.php
    @@ -0,0 +1,23 @@
    +$config['display']['default']['display_options']['sorts']['created']['exposed'] = TRUE;
    
    +++ b/core/modules/views/tests/src/Functional/Update/ViewsSortIdentifiersUpdate.php
    @@ -0,0 +1,42 @@
    +    $this->assertTrue($view->get("{$trail}.exposed"));
    ...
    +    $this->assertTrue($sort['exposed']);
    

    In fact we need to add the default value also for non-exposed sorts.

Keep assigned to fix the above.

claudiu.cristea’s picture

Assigned: claudiu.cristea » Unassigned
Status: Needs work » Needs review
FileSize
25.83 KB
28.41 KB

Fixed #23, still needs tests.

claudiu.cristea’s picture

Forgot the @group tag for the new test.

claudiu.cristea’s picture

Title: Modify Views exposed sort identifiers » Views exposed sort identifiers are not configurable
Issue summary: View changes

Fixing title, IS.

claudiu.cristea’s picture

Issue tags: -Needs release note
claudiu.cristea’s picture

A patch for 8.9.x for whom may concern.

The last submitted patch, 25: 2897638-25.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 28: 2897638-26-8.9.x.patch, failed testing. View results

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
2.14 KB
29.36 KB

More default config to be fixed...

claudiu.cristea’s picture

FileSize
34.26 KB
18.74 KB
  • Reverted the identifier validation code reusability. The sort identifier is different.
  • Fixing views config updater.
  • [CSS] The new textfield should have the same indent as the label.
  • Add tests for for the admin UI part. Still needs tests with exposed form.
claudiu.cristea’s picture

Adding tests for custom sort identifier functionality.

claudiu.cristea’s picture

Issue summary: View changes
Issue tags: -Needs change record
claudiu.cristea’s picture

Issue summary: View changes
FileSize
562 bytes
35.88 KB
  • Fixing the release note snippet.
  • Small docblock fix.

Status: Needs review » Needs work

The last submitted patch, 35: 2897638-35.patch, failed testing. View results

claudiu.cristea’s picture

Status: Needs work » Needs review

It was a random failure. Back to NR.

Lendude’s picture

Status: Needs review » Needs work

This looks great.

The one thing I would like to see added to the test coverage is that the 'uniqueness' validation also works cross handler, so we are sure that any collisions between exposed filter identifiers and sort identifiers is also detected. Looking at the code in \Drupal\views\Plugin\views\display\DisplayPluginBase::isIdentifierUnique, it should work as it stands now, but since currently this code only needs to work for filters I think it would be good to add test coverage for that.

claudiu.cristea’s picture

Related to #8, @Lendude and me, we had a discussion on Slack #contribute channel:

clau
I wonder why a filter identifier should not be the same as a sort identifier. Isn’t that safe? Why?

clau
I think a filter and a sort identifier could be the same (edited)

clau
I would add a new parameter to method:

public function isIdentifierUnique($id, $identifier, $handler_type = NULL) {
  if (!$handler_type) {
    $handler_type = 'filter';
    @trigger_error(...deprecation message);
  }
  ... search only in the scope of $handler_type
}

clau
And, course, fix all the calls to isIdentifierUnique() to pass the type

lendude
If they are the same would that give you a weird query string? since then they would be using the same query parameter?

lendude
ah no, because order always uses the ‘order’ get parameter

clau
actually. sort_by=

lendude
Ah ok, was looking at click sorting :)

lendude
Ok, then your scenario makes sense. You should be able to use the same exposed identifier between sorts and filters, and right now, that will not pass validation

claudiu.cristea’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
6.5 KB
41.47 KB

Fixed #39. Updated IS & change record.

Status: Needs review » Needs work

The last submitted patch, 40: 2897638-40.patch, failed testing. View results

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
9.31 KB
39.1 KB

Thinking more on #40. This change is a little risky if a module created a handler, other than filter which adds its query string params. Also it changes the behaviour of the method, We cannot anticipate if the method is used for other reasons in contrib/custom code. So, maybe is safer to keep it as it is and use a custom code in SortPluginBasethat only checks for sort handlers.

claudiu.cristea’s picture

FileSize
39.01 KB

Reroll

Lendude’s picture

+++ b/core/modules/views/tests/src/Functional/Update/ViewsSortIdentifiersUpdateTest.php
@@ -0,0 +1,44 @@
+declare(strict_types = 1);

Don't think we do this in core?

+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -2553,6 +2553,13 @@ public function newDisplay() {
   public function isIdentifierUnique($id, $identifier) {
     foreach (ViewExecutable::getHandlerTypes() as $type => $info) {
+      if ($type === 'sort') {
+        // The exposed sort identifier is the value of 'sort_by' query string
+        // parameter and cannot collide with $identifier as the later is used as
+        // query string parameter key.
+        continue;
+      }

As pinged on Slack, should we consider not naming the sort identifier 'identifier' in config, so we prevent the logic in isIdentifierUnique() from triggering?

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

claudiu.cristea’s picture

I think #44 is a great idea as long as exposed sort identifier are a different kind of fish than the filter identifiers. I've renamed identifier to field_identifier as it refers to the field being sorted on.

Giuseppe87’s picture

Re-rolled the patch for 9.1.6 - first time I do for a so long patch, I hope I've done right but wouldn't hurt if someone could check it.

Status: Needs review » Needs work

The last submitted patch, 48: 2897638-48.patch, failed testing. View results

KapilV’s picture

Status: Needs work » Needs review
FileSize
38.27 KB

Reroll patch for 9.2.x.

Status: Needs review » Needs work

The last submitted patch, 50: 2897638-50.patch, failed testing. View results

claudiu.cristea’s picture

claudiu.cristea’s picture

@KapilV, the 9.2 (official) change is in https://git.drupalcode.org/project/drupal/-/merge_requests/54 merge request

Lendude’s picture

Status: Needs review » Reviewed & tested by the community

Hard one to review with all the views getting changed, but tried to focus on the non-config changes, and I think this looks good now. Nice work!

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Spokje’s picture

Status: Reviewed & tested by the community » Needs work

MR !54 needs a rebase against 9.3.x. Only the original creator of the MR can do so.

claudiu.cristea’s picture

Status: Needs work » Reviewed & tested by the community

Rebased.

Giuseppe87’s picture

How can I apply the latest version to my project?

I know how to apply a patch with composer, but I don't understand how to do with this merge request.

catch’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll

This looks good to me, but it needs another rebase.

@Giuseppe87 you can use https://git.drupalcode.org/project/drupal/-/merge_requests/54.diff - although to get a stable patch to apply, it's best to download this to a /patches directory in your codebase, and then add the local patch path to composer.

Spokje’s picture

Status: Needs work » Reviewed & tested by the community
Issue tags: -Needs reroll

Rerolled against latest 9.3.x.
Back to RTBC per #54.

  • catch committed d3dabc4 on 9.3.x
    Issue #2897638 by Spokje, claudiu.cristea, istavros, PaulDinelle, drclaw...
catch’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: +9.2.0 release highlights

Committed/pushed to 9.3.x, thanks!

Tagging for highlights since this is a UX improvement.

Spokje’s picture

Now with even more accurate tags! 😇

Status: Fixed » Closed (fixed)

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

quietone’s picture

Publish the CR