Skip Navigation

[Resolved] Date filter by min max year only

This thread is resolved. Here is a description of the problem and solution.

Problem:

I need a year-only filter for a car archive so users can choose a min/max year to filter a date field (fecha de matriculación), but the default Toolset date picker is too granular.

Solution:

Add a “between two values” date filter using URL params (e.g., matri_from/matri_to), insert two hidden Toolset date controls bound to those params, then use visible year selects plus a small JS snippet to map years to YYYY-01-01 and YYYY-12-31, sync the hidden inputs, and trigger the View update.

Relevant Documentation:

https://toolset.com/course-lesson/creating-a-custom-search/

https://toolset.com/forums/topic/date-filter-by-min-max-year-only/#post-2831003

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 6 replies, has 1 voice.

Last updated by javierP-10 5 days, 4 hours ago.

Assisted by: Minesh.

Author
Posts
#2830992

Hi! I'm creating some filters for my car showroom in hidden link. My "coche" CPT has several custom fields, among them I have "fecha de matriculación" (date of registration) of the vehicle in a date field. I need to create a filter so the user can choose between a min and a max date to filter the cars listed, but I need only that they can specify the year. I tried to use de default toolset date picker but it's too complicated, I would like to use single textfilelds of selects for each year field. Which is the best approach to do this?

Is there a similar example that we can see? hidden link

hidden link

#2831003

Christopher Amirian
Supporter

Languages: English (English )

Hi,

Welcome to Toolset support. This will need custom development. I will try to give the starting point but it will be you to expand upon and fix things.

keep two visible year controls (Desde / Hasta),
keep two hidden date controls that Toolset actually reads,
a tiny JS maps the chosen years → proper dates (YYYY-01-01 / YYYY-12-31) and triggers the search.

1) Add the real date filter (range)

Edit your cars View (for CPT coche) → Query Filter → Add a filter by custom field → choose your date field
(e.g., fecha-de-matriculacion). Set it to “between two values” and give the two URL parameter names:

From (min) URL param: matri_from

To (max) URL param: matri_to

These two params are what the View will read to filter results.

2) Insert hidden Toolset date inputs (they feed the filter)

In the Search and Pagination section (or Loop editor → Filter area), add:

<!-- Hidden Toolset date controls -->
[wpv-control field="fecha-de-matriculacion" url_param="matri_from" type="date" class="hidden-date"]
[wpv-control field="fecha-de-matriculacion" url_param="matri_to"   type="date" class="hidden-date"]

Hide them with a tiny CSS rule (View’s CSS editor):

.hidden-date { display: none !important; }

3) Add the visible year-only UI

Right above your Search button, add a Fields and Text cell (or HTML in the Search area):

<label for="year_min">Desde (año)</label>
<select id="year_min" name="year_min"></select>

<label for="year_max" style="margin-left:.5rem;">Hasta (año)</label>
<select id="year_max" name="year_max"></select>

(If you prefer text boxes, use <input type="number" …> with the same names.)

4) JavaScript: generate years, sync hidden dates, trigger search

Open the View’s JS editor (Legacy Views has a dedicated “JavaScript” panel) and paste:

(function(){
  // Config
  var earliest = 1990;                       // first year in the dropdowns
  var nowY = new Date().getFullYear();       // current year

  // Visible selects (year-only)
  var selFrom = document.querySelector('select[name="year_min"]');
  var selTo   = document.querySelector('select[name="year_max"]');

  // Hidden Toolset date inputs (the ones tied to your URL params)
  var inFrom  = document.querySelector('input[name="matri_from"]');
  var inTo    = document.querySelector('input[name="matri_to"]');

  if(!selFrom || !selTo || !inFrom || !inTo) return;

  // Build year options (newest → oldest)
  function fillYears(sel){
    var opt = document.createElement('option');
    opt.value = ''; opt.textContent = sel === selFrom ? 'Desde (año)' : 'Hasta (año)';
    sel.appendChild(opt);
    for(var y = nowY; y >= earliest; y--){
      var o = document.createElement('option');
      o.value = String(y);
      o.textContent = String(y);
      sel.appendChild(o);
    }
  }
  fillYears(selFrom); fillYears(selTo);

  // Read URL params if present (keeps state on reload/pagination)
  var params = new URLSearchParams(window.location.search);
  var ym = params.get('year_min') || '';
  var yM = params.get('year_max') || '';
  if(ym) selFrom.value = ym;
  if(yM) selTo.value   = yM;

  // Helper to format YYYY-MM-DD
  function fmt(y, m, d){ return y + '-' + ('0'+m).slice(-2) + '-' + ('0'+d).slice(-2); }

  // Sync hidden date inputs based on selected years
  function syncDates(){
    var yFrom = parseInt(selFrom.value, 10);
    var yTo   = parseInt(selTo.value,   10);

    // If user picked only one bound, respect it
    inFrom.value = isFinite(yFrom) ? fmt(yFrom, 1, 1)     : '';
    inTo.value   = isFinite(yTo)   ? fmt(yTo,   12, 31)   : '';

    // If both picked but reversed, auto-fix
    if(isFinite(yFrom) && isFinite(yTo) && yFrom > yTo){
      var tmp = yFrom; yFrom = yTo; yTo = tmp;
      selFrom.value = String(yFrom);
      selTo.value   = String(yTo);
      inFrom.value  = fmt(yFrom, 1, 1);
      inTo.value    = fmt(yTo, 12, 31);
    }

    // Update URL params for back/forward & bookmarking
    var p = new URLSearchParams(window.location.search);
    (inFrom.value ? p.set('year_min', selFrom.value) : p.delete('year_min'));
    (inTo.value   ? p.set('year_max', selTo.value)   : p.delete('year_max'));
    history.replaceState(null, '', window.location.pathname + (p.toString()?('?'+p.toString()):''));

    // Trigger View update:
    // If your View is "update results instantly", changing the hidden inputs is enough,
    // but we explicitly dispatch change to be safe.
    inFrom.dispatchEvent(new Event('change', {bubbles:true}));
    inTo.dispatchEvent(new Event('change', {bubbles:true}));
  }

  // Initial sync (when coming from URL params)
  syncDates();

  // Wire events
  selFrom.addEventListener('change', syncDates);
  selTo.addEventListener('change',   syncDates);
})();

If your View updates on submit (not instantly), remove the last two dispatchEvent lines and let the user click Search.

5) Clear caches and test

Toolset → Settings → Front-end content → Clear cache

Load the page and test:

Pick Desde: 2019 / Hasta: 2023 → results should only include cars registered within those years.

Bookmark the URL: ?year_min=2019&year_max=2023 and confirm it restores the selections.

Thanks.

#2832882

Thanks Christopher and sorry for the delay answering, I got it

#2832912

Sorry Christopher, I still need assistance, the date filter works the first time the user uses it, but once filtered the view, the date selects doesn't populate with any year and shows blank in the page, you can see it in hidden link I guess it has something to be with AJAX pagination, I'm using it. Thanks in advance

#2833159

Minesh
Supporter

Languages: English (English )

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

Christopher is on vacation. This is Minesh here and I'll take care of this ticket. HOpe this is OK.

Can you please send me admin access details and let me check what's going wrong with your setup.

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

#2834549

Hi Minesh, any update? My customer is waiting and asking.

Thanks in advance

#2834673

Minesh
Supporter

Languages: English (English )

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

I checked and I'm afraid that using ajax filters and ajax pagination with such custom code is really cumbersome task.

If you drop the idea of using ajax, I may have better solution for you. If you confirm you are ready to use non-ajax I can guilde you.

However I also setup a different view wheter I used year filter - here is a view for that:
- hidden link

And the above view is added to the following page:
- hidden link

The year dropdown select options are populated using shortcode:

<div class="form-group marginr-s grupo-filtro-mobile">
  			<!-- <label for="year_min" class="sr-only">Desde</label>-->
  			
            [show_select_option from="1990" show_dropdown_select="from" ]
              
          </div>
  
		<div class="form-group grupo-filtro-mobile">
  			<!-- <label for="year_max" class="sr-only">Hasta</label> -->
  			[show_select_option from="1990" show_dropdown_select="to"  ]
	  	  </div>

And year filter is handled using the query filter and few other addon functions: The following code is added to "Custom Code" section offered by Toolset:
- hidden link


add_filter('wpv_filter_query', 'func_filter_tribe_events_by_year', 10, 3);  
function func_filter_tribe_events_by_year($query_args, $view_settings, $view_id) {
   
  global $WP_Views;
  global $wpdb;
        
           
if($view_id == 74920)  {
   
    
   	   $all_query_args = $query_args['meta_query']; 
     	$exclude_index = '';
     	foreach($all_query_args as $k=>$v):
     
     		if($v['key']==='wpcf-fecha-de-matriculacion'){
                      
               $exclude_index = $k;
               break;
            }
     
     	endforeach;
     	unset($all_query_args[$exclude_index]);
     	$query_args['meta_query'] = $all_query_args;
     
    
   if(defined( 'DOING_AJAX') and DOING_AJAX ) {
     
     	 $min_value = $max_value = '';
      	foreach($_POST['search']['dps_general'] as $k=>$v):
            if($v['name']=='wpv-wpcf-fecha-de-matriculacion_min' and !empty($v['value'])){
               		$min_value = $v['value'];
              }
        endforeach;
     
     	 foreach($_POST['search']['dps_general'] as $k=>$v):
            if($v['name']=='wpv-wpcf-fecha-de-matriculacion_max' and !empty($v['value'])){
               		$max_value = $v['value'];
              }
        endforeach;
          
                if($min_value!='' and $max_value!=''){
                  	
                  			$from = get_unix_timestamp($min_value,'start');
                  			$to = get_unix_timestamp($max_value,'end');
                  
                  			$year_meta_args = array('relation'=>'AND',
                                       array( 'key'=>'wpcf-fecha-de-matriculacion',
                                              'value'=>$from,
                                              'type'=>'NUMERIC',
                                               'compare'=>'>='
                                         ),
                                       	array( 'key'=>'wpcf-fecha-de-matriculacion',
                                              'value'=>$to,
                                              'type'=>'NUMERIC',
                                               'compare'=>'<='
                                         ),
                                       
                                      );
        					$query_args['meta_query'][] = $year_meta_args;

                }
    
     
        }
   
   
   }
return $query_args;
}


function get_unix_timestamp($year, $type = 'start') {
    // Validate input
    $year = (int) $year;
    $type = strtolower($type);

    if ($type === 'end') {
        // December 31, 23:59:59 of the given year
        $datetime = new DateTime("{$year}-12-31 23:59:59", new DateTimeZone('UTC'));
    } else {
        // January 1, 00:00:00 of the given year
        $datetime = new DateTime("{$year}-01-01 00:00:00", new DateTimeZone('UTC'));
    }

    return $datetime->getTimestamp();
}

function get_selected_option($type='from'){
  
if(defined( 'DOING_AJAX') and DOING_AJAX ) {
     
     	
  		if($type=='from'){
          	$target_field = 'wpv-wpcf-fecha-de-matriculacion_min';
        }else if($type=='to'){
          	$target_field = 'wpv-wpcf-fecha-de-matriculacion_max';
        }
     	
     	 foreach($_POST['search']['dps_general'] as $k=>$v):
            if($v['name']==$target_field and !empty($v['value'])){
               		return $v['value'];
              }
        endforeach;
}else{
  return '';
}
  
        
}
  
  
function show_select_option_shortcode($atts) {
    $atts = shortcode_atts([
        'from' => 1990,       // default start year
        'to'   => date('Y'),  // default end year
      	'show_dropdown_select'=>'',
        
      
    ], $atts);

    $start_year = (int) $atts['from'];
    $end_year = (int) $atts['to'];
    $selected_option =  get_selected_option($atts['show_dropdown_select']);
  	
  
 
	 // Build option HTML
    $options = $selected = $min_value = $max_value = $select = '';
  if($atts['show_dropdown_select']=='from'){
  	$select = '<select id="wpv_control_select_wpcf-fecha-de-matriculacion" name="wpv-wpcf-fecha-de-matriculacion_min" class="js-wpv-filter-trigger form-control"><option value="">Desde</option>';
  }
  
  if($atts['show_dropdown_select']=='to'){
  	$select='<select id="wpv_control_select_wpcf-fecha-de-matriculacion" name="wpv-wpcf-fecha-de-matriculacion_max" class="js-wpv-filter-trigger form-control"><option value="">Hasta</option>';
  }
  
  
      
   
   $options = '';
    
    for ($year = $end_year; $year >= $start_year; $year--) {
      	$selected = '';
      	if($selected_option==$year) {
          	$selected = "selected=selected";
        }
        $options .= sprintf('<option %s value="%d">%d</option>' . "\n",$selected , $year, $year);
    }
  
  	$end_select = "</select>";
  
  	return $select.$options.$end_select;
}
add_shortcode('show_select_option', 'show_select_option_shortcode');

The above view is added on the following page:
- hidden link

If you agree to use non-ajax filter and pagination then I can go ahead and adjust the code accordingly.

#2834746

Hi Minesh! Thanks for your answer, uf let me see if I understood everything, please let me know if not.

Ok, if the custom code you refer to is the JS code in Divi -> General options > integration, let me tell you that the only script handling the date select is the last one. That code (a bit modified) was given by your partner Christopher to replace the toolset default date controls by two custom date selects and pass the values to the filter. If you found a way to do this (and in your example, you did) there's no problem to replace that code with yours.

If you are refering to the view's code and it's not possible, I have checked a while ago that just disabling AJAX filtering and AJAX pagination, the date filter works doing nothing else. It's not the perfect scenario... but I'll try to persuade my customer.

I checked the test page you created with the test view and it works fine with AJAX filtering. If it's possible to integrate that approach in my view (the rest of filters are regular Toolset filters, all done with the filter's view options, no custom code except html) it would be great.

Thank you so much

#2834914

Hi again Minesh, I took yesterday your php code as basis and made made my own code to finish the date filter in my production view. It's working fine now with AJAX filtering and pagination, you can have a look if you have time in hidden link
Thanks for your help and your time!