I'm working on a website with two post types sharing several taxonomies & custom fields. I've developed a filtering functionality with pretty urls, and urls for now works this way:

1) I've registered my query vars

function return_vars( $vars ) {
$vars[] = 'tax_filters';
$vars[] = 'field_filters';
return $vars;
}
add_filter( 'query_vars', 'return_vars' );

2) I've setup these rewrite rules using add_rewrite_rule() in this way:

add_rewrite_rule( 'my-base/filters/([^\/]*)/([^&]*)/fields/([^&]*)/?', 'index.php?post_type=$matches[1]&tax_filters=$matches[2]&field_filters=$matches[3]', 'top');
add_rewrite_rule( 'my-base/filters/([^\/]*)/([^&]*)/?', 'index.php?post_type=$matches[1]&tax_filters=$matches[2]', 'top');

so that:

  1. $matches[1] represents the post type
  2. $matches[2] filters taxonomies. It's a string, divided by slashes, in the form of /tax1_name/term/tax2_name/term/tax3_name/term.....
  3. $matches[3] in the first rule stops matches[2] recording and starts matching the stuff after the /fields/ placeholder, which follows as /field1_name/value/field2_name/ etcetera
  4. Lately, I filter WP_Query using pre_get_posts, according to the contents of tax_filters[] & field_filters[]

So my URLs can be:

(according to rule 1)
my-base/my-cpt/filters/tax1/term/tax2/term/../../fields/field1/value/fields2/value/../../

or filter just taxes:

(according to rule 2)
my-base/my-cpt/filters/tax1_name/term/tax2_name/term/../../

Now, since I need even:

  1. filtering for fields only like: my-cpt/fields/field1/value/fields2/value/
  2. pagination for each possible filtering combination: a) only tax, b) only fields c) tax and fields

This would result in 6 different rules, which have to be written in a specific order, from the longest to the shortest, otherwise the latter will overwrite the others.

I was wondering: is it possible to write a regex which would eventually combine all these rules in one?

I've tried with several regexes conditional statements, but I'm not proficient in it, so I've got only parse errors.

Thanks to anyone who could help!

up vote 2 down vote accepted

I've managed to workaround the problem. He're the solution for further reference.

1) I have only 1 query var now:

function return_vars( $vars ) {
  $vars[] = 'filters';
  return $vars;
}

2) My rewrite rules changed to:

add_rewrite_rule( 'my-base/filters/([^\/]*)/([^&]*)/page/([0-9]{1,})/?', 'index.php?post_type=$matches[1]&filters=$matches[2]&paged=$matches[3]', 'top');
add_rewrite_rule( 'my-base/filters/([^\/]*)/([^&]*)/?', 'index.php?post_type=$matches[1]&filters=$matches[2]', 'top');

Rule 1: handles pagination Rule 2: handles main page only

They have to be in this order.

3) Then, I filter the filters string more or less in this way

function my_pre_get_posts( $query ) {
  $filters = get_query_var('filters');

  // if no filter or if filters are odd, then return
  if(!$filters || count(explode("/", $k4w_filters)) % 2 != 0) return;

  // this is a global array with my 2 tax slugs in it
  global $CUSTOM_TAXES_S;

  $chunks = array_chunk(explode('/', $filters), 2);
  $filter_data = array_combine(array_column($chunks, 0), array_column($chunks, 1));

  // filter data, populate tax_data and field_data for queries
  foreach($filter_data as $key => $value) {
    if(in_array($key, $CUSTOM_TAXES_S)) {
        $tax_data[$key] = $value;
    } else {
        $field_data[$key] = $value;
    }
    $query->set('debug_tax_data', $tax_data);
    $query->set('debug_field_data', $field_data);
  }

  // if tax_data exist set tax_query
  if($tax_data) {
    foreach( $tax_data as $taxonomy => $slugs ) {
        // append tax query
        $tax_query[] = array(
            'taxonomy'  => get_tax_name($taxonomy), // that's a simple function that retrieves the taxonomy name from its slug
            'field'     => 'slug',
            'terms'     => explode(',', $slugs),
        );
    }
    $query->set('tax_query', $tax_query);
  }

  // if field_data exist set meta_query
  if($field_data) {
    foreach( $field_data as $key => $value ) {
        // append tax query
        $meta_query[] = array(
            'key'   => $key,
            'value'     => explode(',',$value),
            'compare'       => 'IN',
        );
    }
    $query->set('meta_query', $meta_query);
  }
}
add_action('pre_get_posts', 'my_pre_get_posts', 10, 1);

In this way I can even provide multiple terms for taxes, or values for fields, such as:

/my-base/filters/my-cpt/tax1/term1,term2/tax2/term1,term2/field1/val1,val2/goingon...

And it'll work! With pagination too.

Obviously the code above works, but there has too be several checks to avoid issues and hacks (such as checking if a provided field name exists, or not, or if it's in a filtrable-field list etcetera).

Hope this will help someone :)

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.