Skip Navigation

[Resolved] Custom wpv-control Filter Not Resetting URL parameters & Not taking Defaults

This is the technical support forum for Toolset - a suite of plugins for developing WordPress sites without writing PHP.

Everyone can read this forum, but only Toolset clients can post in it. Toolset support works 6 days per week, 19 hours per day.

Sun Mon Tue Wed Thu Fri Sat
- 10:00 – 13:00 10:00 – 13:00 10:00 – 13:00 10:00 – 13:00 10:00 – 13:00 -
- 14:00 – 18:00 14:00 – 18:00 14:00 – 18:00 14:00 – 18:00 14:00 – 18:00 -

Supporter timezone: Asia/Kolkata (GMT+05:30)

This topic contains 21 replies, has 2 voices.

Last updated by Minesh 11 months, 2 weeks ago.

Assisted by: Minesh.

Author
Posts
#2669965
Screenshot (101).png

Tell us what you are trying to do?
Add custom filter to a View that will filter the contents to only show those that are related to a specific CPT select from the filter dropdown. The issue arises because the View is by default only showing items related to a different CPT.

Essentially, I have three CPT's in use here, Class Types, Classes, and Terms.

The View is on a Class Type page, and it's basic state is to show all Classes related to that Class Type.

I want to be able to filter the classes shown by the Terms they are related to as well. So you will be able to see all the Classes related to the current Class Type, within whichever Term you select from the filter.

The problem is Views only allows one Relationship filter, and trying to manually add another just overrides the first one.

So I've been trying to add a manual wpv-control filter as mentioned here: https://toolset.com/forums/topic/frontend-search-filter-by-post-types/#post-2153705

The problem is that while I can pull the query and filter the posts, every time I submit the filter the URL parameters just keep getting added to, and the reset buttons don't clear them or reset the filter (see address bar in the screenshot).

I thought this is might be due to the lack of a default value, although I'd still expect it to clear the URL parameters out. I've tried adding the default_label and auto_fill_default parameters as indicated by the documentation: https://toolset.com/documentation/programmer-reference/views/views-shortcodes/#wpv-control, but it doesn't seem to do anything.

I've also tried a few different methods of resetting the page, but the parameters are always added back in at the last step of the reloading process, which I'm assuming is the intended behaviour.

Here's the filter:

[wpv-filter-start hide="false"]
[wpv-filter-controls]

[wpv-control url_param="term" type="select" default_label="All" auto_fill_default="All" values="[term_filter_params parameter='values']" display_values="[term_filter_params parameter='display_values']"]

[wpv-filter-submit type="button" output="bootstrap" class="btn-secondary"][wpv-filter-reset output="bootstrap"]
[/wpv-filter-controls]
[wpv-filter-end]

All the term_filter_params shortcodes are doing is outputting an ordered list of terms with their ID's as the values (see screenshot).

Although it's not being used to cause these issues, and is still incomplete, here is the PHP that I've been working, but even with the add_action hook disabled, the issues are unchanged:

function test_filter(  $query, $view_settings, $view_id ){
  //If this is the Class Type Classes View (ID: 1137)
  if( $view_id = 1137 ){
    if( isset( $_GET['term'] ) ){

      $classes = $query->posts;

      $class_ids = [];

      foreach( $classes as $class ){
        $class_ids[] = $class->ID;
      }

      $filtered_classes = [];

      foreach( $class_ids as $class_id ){
        if( toolset_get_related_post( $class_id, 'term-class' ) == $_GET['term'][0] ){
          $filtered_classes[] = $class_id;
        }
      }
    }
  }
  return $query;
}
add_filter( 'wpv_filter_query_post_process', 'test_filter', 101, 3 );

I don't think this code is relevant, but I thought I'd include it for so it's clear the direction this is heading in. But I'll reiterate, the issues appear to be with the front-end filter controls.

So, to summarise:
1) Is there a better way of achieving filtering by a secondary relationship?
2) Why is the wpv-control filter constantly adding to the url parameters?
3) Why won't wpv-control accept default parameters?
4) Why won't the reset work?

Is there any documentation that you are following?
https://toolset.com/forums/topic/frontend-search-filter-by-post-types/#post-2153705
https://toolset.com/documentation/programmer-reference/views/views-shortcodes/#wpv-control

Thanks in advance.

#2670581

Minesh
Supporter

Languages: English (English )

Timezone: Asia/Kolkata (GMT+05:30)

Hello. Thank you for contacting the Toolset support.

Do you know that you can add the post relationship filter as well. Please check the following doc:
- https://toolset.com/documentation/legacy-features/views-plugin/how-to-filter-by-related-posts/

Will the above doc help in your case?

#2670655
Screenshot (106).png
Screenshot (105).png
Screenshot (104).png
Screenshot (103).png

Hi Minesh,

I do know about this kind of relationship filter, this was what I assumed would work when I wanted to implement this, but I don't think that does help as Toolset seems to not allow the two types of relationship to be filtered together.

As I said, the View needs to be displaying Classes that belong to the current page's Class Type in a Class Type -> Classes one to many relationship, and then I want to be able to give the user the ability to additionally filter the displayed classes by the Term they are associated with in the Terms -> Classes one to many relationship. They are not ancestors of each other, rather the classes belong to several different parents, so I guess that would make them siblings not ancestors I think?

When I have the main query filter in place (screenshot 103) and then try adding a new relationship filter via the search and pagination section, the relationship option is greyed out (screenshot 104). If I have no query filter in place and try to add a search and pagination filter, I can select the relationship option, but if I select the essential Class Types Class relationship (that was in the query filter originally), I can't select the Terms Class relationship as they're not ancestors (screenshot 105), and if I select the Terms Class relationship the same is true (screenshot 106).

As I said in my original question, Toolset doesn't seem to let them coexist, so I was trying to write a work around, but the wpv-control filter doesn't seem to be behaving in a way that makes sense, so I need help understanding what it is doing and why because the documentation doesn't go into any depth on it.

Oh, it's also an issue that if I do use the built in ancestors filter to filter the Term -> Class relationship, then I can't use my custom ordering for the values, which is very important as they need to be in a specific order.

Writing the actual filter code isn't the problem, it's how to get it to interact with the View, which controls to use, or how to get a dropdown to interact that I'm struggling to understand as it's the front end stuff that's giving me trouble here.

I hope that makes it clearer where I'm at.

#2670741

Minesh
Supporter

Languages: English (English )

Timezone: Asia/Kolkata (GMT+05:30)

if you can share problem URL with me and share admin access details. Let me review your structure and see what we can do. I believe we can hook in the current post to filter the classes without adding the query filter from the view's settings.

*** Please make a FULL BACKUP of your database and website.***
I would also eventually need to request temporary access (WP-Admin and FTP) to your site. Preferably to a test site where the problem has been replicated if possible in order to be of better help and check if some configurations might need to be changed.

I have set the next reply to private which means only you and I have access to it.

#2670743

Minesh
Supporter

Languages: English (English )

Timezone: Asia/Kolkata (GMT+05:30)

if you can share problem URL with me and share admin access details. Let me review your structure and see what we can do. I believe we can hook in the current post to filter the classes without adding the query filter from the view's settings.

*** Please make a FULL BACKUP of your database and website.***
I would also eventually need to request temporary access (WP-Admin and FTP) to your site. Preferably to a test site where the problem has been replicated if possible in order to be of better help and check if some configurations might need to be changed.

I have set the next reply to private which means only you and I have access to it.

#2671009

Minesh
Supporter

Languages: English (English )

Timezone: Asia/Kolkata (GMT+05:30)

Can you please check now: hidden link

Can you please confirm that with the term filter you are getting the expected results? If yes then I will try to explain you the changes I've made.

#2671315

Hi Minesh,

Thank you for taking a look at this. I've had a look over what you've done, and I understand the jist of it but not all of it, and unfortunately, it still doesn't solve things completely.

I can see that you've used the wpv_view_settings hook to manually intercept the $view_settings and add the relationship keys to the array if the $view_id is the specific View we're working on with this function:

add_filter( 'wpv_view_settings', 'func_filter_by_current_post_relation', 10, 2 );
  function func_filter_by_current_post_relation( $view_settings, $view_id ) {
  global $post;
    if ( $view_id == 1137 ) { 
   
      
      $view_settings['post_relationship_slug'] = 'class-type-class';
      $view_settings['post_relationship_role'] = 'child';
      $view_settings['post_relationship_mode'] = array('0' => 'this_page');
      $view_settings['post_relationship_id'] = $post->ID;
      
      
            
    }
    return $view_settings;
}

I think that is working in harmony with the URL parameter provided by the [wpv-control-post-relationship] shortcode in the Search and Pagination editor on the View to enact the select field's values.

This bit works correctly, but I cannot figure out how you're populating the select field though as there's no sign of the [term_filter_params] shortcode anywhere on the View or any new implementations in the custom code files. So if you could enlighten me on how you've achieved that I'd be very interested!

However. This is great, but it overlooks the initial problem of having the View filtering by two relationships as I need the View's Classes to be <u>always</u> filtered by the Class Type and then allow the user to filter by the Terms manually as you've implemented here.

Could this further be augmented with some of the other hooks?

Unfortunately, the hook documentation is very vague, and the examples are not really helpful and the arrays, such as the $view_settings array, aren't documented so it makes these things very hard to dig into properly. I don't need to let the user affect this filter at all, I simply need all front-end results to be filtered by the Class Type regardless of the Terms select field. What stage of the View's query process could I hook into to achieve this?

#2671319

Minesh
Supporter

Languages: English (English )

Timezone: Asia/Kolkata (GMT+05:30)

The select term field is populated using the following relationship filter added to your view's "Search and Pagination " section:
=> hidden link

[wpv-control-post-relationship ancestors="academic-term@term-class.parent" url_param="wpv-relationship-filter"]
<div class="form-group">
	<label for="wpv-relationship-filter">[wpml-string context="wpv-views"]Terms[/wpml-string]</label>
	[wpv-control-post-ancestor type="select" ancestor_type="academic-term@term-class.parent"]
</div>
[/wpv-control-post-relationship]

Let me tell you what we did. You want to filter the view result by current class types and displays the related classes. But as you know we can not filter view result using multiple post relationship.

So, I've added the hook "wpv_view_settings" where instead of view's Query filter section where you were trying to filter view result with current post based on the post relationship, I removed that filter and added the hook "wpv_view_settings" where we are providing the arguments explicitly to filter the view result based on current post and display the related classes.

As we removed the post relationship query filter to filter view result based on current post based on the post relationship, it offers us luxury to add post relationship filter to display the related terms and you can filter the view result by selecting the term.

If this is not the result you want then I'm afraid I do not have any other solution as of now.

#2671323

Ok, maybe I didn't see that correctly, but I could have sworn that the select field had the terms in the custom order I required as output by my [term_filter_params] shortcode, but I just looked at it again and it's just the default ordering, which isn't that useful. That's what was confusing me with the select field. As I said, I can see where it is, but the values being passed to it were the mystery. However, I may have been mistaken on that. Is there a way to pass the sorted values to the [wpv-control-post-relationship] field? Or override the content of it?

Everything else I understood, and it's useful extra information definitely, so thank you for that.

Can I ask that you address my other last question please? What hook(s) could I use to take this filtering and override the final results before they're displayed? Would the wpv_filter_query or the wpv_filter_query_post_process hooks enable me to do this?

#2671329

I'm still refining it (jQuery is not my strong point), but it is possible to control the select field values by overriding them with jQuery, something like this:

jQuery(document).ready(function($) {
  var newOptions = {"Spring 2023": "555",
                    "Summer 2023": "1906",
                    "Autumn 2023": "427"
                   };

  var $el = $("#wpv_control_select_wpv-relationship-filter");
  $('#wpv_control_select_wpv-relationship-filter option:gt(0)').remove(); // remove all options, but not the first 
  $.each(newOptions, function(key,value) {
    $el.append($("<option></option>")
               .attr("value", value).text(key));
  });
});

I'd need to get the values from the PHP shortcode to build a jQuery array to make this work, but because it changes it at runtime, it doesn't break the field, so this is one possible solution to one of the problems, I just need to add the second layer of filtering programmatically.

#2671371

Ok, I've got the select updating code it's working, I've even managed to get it to persist over AJAX reloads with the Toolset js_event_wpv_parametric_search_results_updated hook.

Here's the code that works:

jQuery(document).on('js_event_wpv_parametric_search_results_updated', function( event, data ) {

  var commaSeparatedValues = $( "#values" ).val();
  var keys = commaSeparatedValues.split( ',' );
  var commaSeparatedDisplayValues = $( "#display_values" ).val();
  var values = commaSeparatedDisplayValues.split( ',' );
 
  // Get the select element
  var selectElement = $( '#wpv_control_select_wpv-relationship-filter' );

  // Remove all options except the first
  selectElement.children( 'option:not(:first-child)' ).remove();

  // Create and append new options
  for( var i = 1; i < keys.length; i++ ){
    var optionElement = $('<option>');
    optionElement.attr('value', keys[i]);
    optionElement.text(values[i]);
    selectElement.append(optionElement);
  }
});

And based on this support thread:
https://toolset.com/forums/topic/jquery-function-not-running-after-updating-results-via-ajax/

This makes it persist over AJAX search reloads:

(function ($) {
  // 1. Define a function that does "the thing"
  // Function to update relationship filter options
  function updateRelationshipFilterOptions() {
    // Retrieve comma-separated values from input fields
    var commaSeparatedKeys = $('#values').val();
    var commaSeparatedValues = $('#display_values').val();

    // Split comma-separated values into arrays
    var keys = commaSeparatedKeys.split(',');
    var values = commaSeparatedValues.split(',');

    // Get the select element
    var selectElement = $('#wpv_control_select_wpv-relationship-filter');

    // Remove all options except the first
    selectElement.find('option:not(:first-child)').remove();

    // Create and append new options
    for (var i = 1; i < keys.length; i++) {
      var optionElement = $('<option>');
      optionElement.attr('value', keys[i]);
      optionElement.text(values[i]);
      selectElement.append(optionElement);
    }
  }

  // 2. Do the thing on initial pageload (DOM ready)
  $(document).ready(function () {

    updateRelationshipFilterOptions();
  });

  // 3. Do the thing after ajax custom search
  $(document).on('js_event_wpv_parametric_search_results_updated', function (event, data) {

    updateRelationshipFilterOptions();
  });
})(jQuery);

The only weird part is that after it loads with the js_event_wpv_parametric_search_results_updated hook, it always resets the select field to the default option. Fortunately, it doesn't reset the parameters of the filter, just the visual option selected in the select field. Any ideas why that's happening? As it seems to be related to the Toolset filter.

#2671569

Minesh
Supporter

Languages: English (English )

Timezone: Asia/Kolkata (GMT+05:30)

I see you are calling the function "updateRelationshipFilterOptions" using the AJAX callback "js_event_wpv_parametric_search_results_updated".

Can you please make sure that the function "updateRelationshipFilterOptions" returns the correct options you want to display when you filter the results rather than the default options and the custom JS code you added is actually removing the option you want to remove.

#2671635

Ah, you're right! I was overthinking it. Here's the working code:

(function( $ ) {
  //Function to update relationship filter options
  function updateRelationshipFilterOptions() {
    //Retrieve comma-separated values from hidden input fields
    var commaSeparatedKeys = $('#values').val();
    var commaSeparatedValues = $('#display_values').val();

    //Split comma-separated values into arrays
    var keys = commaSeparatedKeys.split(',');
    var values = commaSeparatedValues.split(',');

    //Get the select element
    var selectElement = $('#wpv_control_select_wpv-relationship-filter');
    
    //Store the select elements selected value
    var selectedValue = selectElement.val();

    //Remove all options except the first
    selectElement.find('option:not(:first-child)').remove();

    //Create and append new options
    for( var i = 1; i < keys.length; i++ ){
      var optionElement = $( '<option>' );
      optionElement.attr( 'value', keys[i] );
      optionElement.text( values[i] );
      selectElement.append( optionElement );
    }
    
    //Reinstate the selected value after the options rebuild
    $('#wpv_control_select_wpv-relationship-filter').val(selectedValue);
  }

  //Call the function on inital load
  $(document).ready(function () {

    updateRelationshipFilterOptions();
  });

  //Call the function after Toolset search AJAX refresh
  $(document).on('js_event_wpv_parametric_search_results_updated', function (event, data) {

    updateRelationshipFilterOptions();
  });
})( jQuery );

Now it's just the matter of the secondary relationship filtering. Can I use the wpv_filter_query or the wpv_filter_query_post_process hooks to do this by getting the query results after the select Term filter has been applied and manually removing any Class not associated with the Class Type of the current page?

#2672057

Ok, I've figured it out. I'm not sure if this is the "best" way of doing this and I dare say it's not the most elegant solution, but this works:

View Search and Pagination Controls

[wpv-filter-start hide="false"]
[wpv-filter-controls]

<span class="hide">
  <input type="text" id="values" value="[term_filter_params parameter='values']">
  <input type="text" id="display_values" value="[term_filter_params parameter='display_values']">
</span>

[wpv-control-post-relationship ancestors="academic-term@term-class.parent" url_param="wpv-relationship-filter"]
<div class="form-group">
  <label for="wpv-relationship-filter">[wpml-string context="wpv-views"]Term Filter[/wpml-string]</label>
  [wpv-control-post-ancestor type="select" ancestor_type="academic-term@term-class.parent"]
</div>
[/wpv-control-post-relationship]

[wpv-filter-submit output="bootstrap" class="btn-secondary"][wpv-filter-reset output="bootstrap"]
[/wpv-filter-controls]
[wpv-filter-end]

View Search and Pagination jQuery

(function( $ ) {
  //Function to update relationship filter options
  function updateRelationshipFilterOptions() {
    //Retrieve comma-separated values from hidden input fields
    var commaSeparatedKeys = $('#values').val();
    var commaSeparatedValues = $('#display_values').val();

    //Split comma-separated values into arrays
    var keys = commaSeparatedKeys.split(',');
    var values = commaSeparatedValues.split(',');

    //Get the select element
    var selectElement = $('#wpv_control_select_wpv-relationship-filter');
    
    //Store the select elements selected value
    var selectedValue = selectElement.val();

    //Remove all options except the first
    selectElement.find('option:not(:first-child)').remove();

    //Create and append new options
    for( var i = 1; i < keys.length; i++ ){
      var optionElement = $( '<option>' );
      optionElement.attr( 'value', keys[i] );
      optionElement.text( values[i] );
      selectElement.append( optionElement );
    }
    
    //Reinstate the selected value after the options rebuild
    $('#wpv_control_select_wpv-relationship-filter').val(selectedValue);
  }

  //Call the function on inital load
  $(document).ready(function () {

    updateRelationshipFilterOptions();
  });

  //Call the function after Toolset search AJAX refresh
  $(document).on('js_event_wpv_parametric_search_results_updated', function (event, data) {

    updateRelationshipFilterOptions();
  });
})( jQuery );

PHP

//Function to force the Class Type View to always filter their results by the Class Type -> Classes relationship
function forced_class_type_filter( $query, $view_settings, $view_id ){
  //If this is the Class Type Classes View (ID: 1137)
  if( $view_id == 1137 ){
    //Set the posts to a variable
    $classes = $query->posts;

    //Initialise an array to store class ID's
    $class_ids = [];

    //Loop through the class objects
    foreach( $classes as $class ){
      //Store the class post ID's
      $class_ids[] = $class->ID;
    }

    //Initialise an array to store filtered results
    $filtered_classes = [];

    //Loop through the class ID's
    foreach( $class_ids as $class_id ){   
      //If this class is related to the class type being viewed
      if( toolset_get_related_post( $class_id, 'class-type-class' ) == get_the_ID() ){
        //Store the class ID
        $filtered_classes[] = $class_id;
      }
    }

    //If the filtered classes array is empty
    if( empty( $filtered_classes ) ){
      //Set the post count and found posts to zero and unset any left over posts
      $query->post_count = 0;
      $query->found_posts = 0;
      unset( $query->post );
    }

    //Initialise a position counter for the loop
    $position_counter = 0;
    
    //Loop through the query posts
    foreach( $query->posts as $post ){
      //If the current post ID is not in the filtered classes array
      if( !in_array( $post->ID, $filtered_classes ) ){
        //Remove the current post from the array
        array_splice( $query->posts, $position_counter, 1 );
        //Decresae the position counter to compensate for array_splice reindexing the array
        $position_counter--;
      }
      //Increase the position counter
      $position_counter++;
    }
  }
  return $query;
}
add_filter( 'wpv_filter_query_post_process', 'forced_class_type_filter', 101, 3 );



//Function to apply the custom Term filter to Class Types via the View Settings
function term_filter_by_current_post_relation( $view_settings, $view_id ){
  //Load the global post variable
  global $post;

  //If this is the Class Type View
  if( $view_id == 1137 ){

    //Apply the post relationship elements to the view settings array
    $view_settings['post_relationship_slug'] = 'class-type-class';
    $view_settings['post_relationship_role'] = 'child';
    $view_settings['post_relationship_mode'] = array( '0' => 'this_page' );
    $view_settings['post_relationship_id'] = $post->ID;
  }
  return $view_settings;
}
add_filter( 'wpv_view_settings', 'term_filter_by_current_post_relation', 10, 2 );

This works by enhancing the filter that you provided by using two hidden HTML fields to store the keys and values of the select field respectively. The jQuery then combines these into the select field, while preserving any preselected option, and uses the js_event_wpv_parametric_search_results_updated hook to make sure it works over AJAX reloads.

I've then used the wpv_filter_query_post_process to check if any classes have a relationship with the class type the user is currently looking at. If it finds any it then removes them from the array using the array_splice function. If there is no match for the class type relationship it unsets all posts (as I was finding one left over even though it wasn't being displayed) and then sets the found_posts to zero as these control if the View loads the "No posts found" message or not, and also the post_count for good measure.

This all works perfectly, although I found almost none of this documented in any meaningful way. So I hope this write-up helps someone, and before I close the ticket I just wanted to double-check that there is no part of this that you can foresee to the best of your knowledge that could be a problem with any part of this solution.

#2672527

Minesh
Supporter

Languages: English (English )

Timezone: Asia/Kolkata (GMT+05:30)

Good to know that you are able to enhance the filter as per your requirement and this is what I mentioned with my previous reply that you have to write such addon code that will serve you as required.

Yes, even I have to follow the same path what you did already. You're welcome to mark resolve this ticket and thank you for sharing this code that will help other users in future.