Dario
Dario

Reputation: 13

Function to filter posts by 2 taxonomies

I have a page of cruises, where I can filter by 2 different taxonomies (sorry the page is in Spanish, the English version is not full). The problem I'm having is that I can't make that the filter works all the time. For example, if I make the double filter works, filter one taxonomy it doesn't. And if the simple filter works, the double filter it doesn't.

This is the current code, which works perfectly to filter by 2 taxonomies, but it doesn't if I filter using only one.

    function belmondo_modify_cruise_archive_query( $query ) {
    if( is_archive() && is_post_type_archive('cruise') && $query->is_main_query() && isset($_GET['ct']) && isset($_GET['co'])) {
        $taxquery = array(
            'relation' => 'AND',
            array(
                'taxonomy' => 'cruise_type',
                'field' => 'slug',
                'terms' => array($_GET['ct']),
                'operator'=> 'AND',
            ),
            array(
                'taxonomy' => 'cruise_country',
                'field' => 'slug',
                'terms' => array($_GET['co']),
                'operator'=> 'AND',
            ),
        );
        $query->set( 'tax_query', $taxquery );
    } 
}
add_filter( 'pre_get_posts', 'belmondo_modify_cruise_archive_query' );

And before this, I had this other code, which it worked perfectly but no the default. By default, the page shows all the posts, and with this code, the 'Show All' showed 0 results, but the resto works perfect.

function belmondo_modify_cruise_archive_query( $query ) {
if( is_archive() && is_post_type_archive('cruise') && $query->is_main_query() && isset($_GET['ct']) && isset($_GET['co'])) {
    $taxquery = array(
        'relation' => 'AND',
        array(
            'taxonomy' => 'cruise_type',
            'field' => 'slug',
            'terms' => array($_GET['ct']),
            'operator'=> 'AND',
        ),
        array(
            'taxonomy' => 'cruise_country',
            'field' => 'slug',
            'terms' => array($_GET['co']),
            'operator'=> 'IN',
        ),
    );
    $query->set( 'tax_query', $taxquery );
} elseif( is_archive() && is_post_type_archive('cruise') && $query->is_main_query() ) {
    $taxquery = array(
        'relation' => 'OR',
        array(
            'taxonomy' => 'cruise_type',
            'field' => 'slug',
            'terms' => array($_GET['ct']),
            'operator'=> 'IN',
        ),
        array(
            'taxonomy' => 'cruise_country',
            'field' => 'slug',
            'terms' => array($_GET['co']),
            'operator'=> 'IN',
        ),
        );
        $query->set( 'tax_query', $taxquery );
    }
}
add_filter( 'pre_get_posts', 'belmondo_modify_cruise_archive_query' );

I researched a lot about these and I tried a lot of different things, like using OR and AND in place of IN, things like that. I think I'm really close to solving it, but there's something I'm not seeing. I'm far away to be an expert on PHP, so I'll try to answer all your questions.

Thanks!

Upvotes: 0

Views: 48

Answers (1)

Xhynk
Xhynk

Reputation: 13840

In your if statement in the top function, it's only running if both $_GET['ct'] and $_GET['co'] are set, because you're checking if both are logically set using the && operator. You should split that up with the logical OR operator: ||. This however would let you pass null values to the tax_query. So without restructuring everything you have, you could just do the following:

1) Logically group the if statement together, making sure the current request is a Cruise Archive's Main-Query, AND at least ct or co is set.

2) Start an empty array and add ct and co to it if they are set.

3) Set the tax_query's relational operator to AND if both co and ct are set.

4) You appear to have the relational operators in the wrong place, it should be set to handle how each query is related outside the array.

5) Instead of handling the $_GET variables constantly, you may want to name them as individual variables so you don't need to keep checking if they're set with isset() and can just text if they return a truthy value.

It would end up looking something like this:

function belmondo_modify_cruise_archive_query( $query ) {
    // Make sure this is a Cruise Archive's Main query - AND - at at least 'ct' or 'co' are passed via GET.
    if( (is_archive() && is_post_type_archive('cruise') && $query->is_main_query()) && (isset($_GET['ct']) || isset($_GET['co'])) ){
        $tax_query = array(); // Tax Query starts empty (it's an array of arrays)

        // Define variables for ease of use
        $cruise_type   = ( isset($_GET['ct']) ) ? $_GET['ct'] : null;
        $cruise_county = ( isset($_GET['co']) ) ? $_GET['co'] : null;

        // If Type is set, add it to the query
        if( $cruise_type ){
            $tax_query[] = array(
                'taxonomy' => 'cruise_type',
                'field'    => 'slug',
                'terms'    => array( $cruise_type ),
            );
        }

        // If Country is set, add it to the query
        if( $cruise_county ){
            $tax_query[] = array(
                'taxonomy' => 'cruise_country',
                'field'    => 'slug',
                'terms'    => array( $cruise_county ),
            );
        }

        // If BOTH are set, set the relation, otherwise it's not needed
        if( $cruise_type && $cruise_county ){
            $tax_query['relation'] = 'AND';
        }

        $query->set( 'tax_query', $tax_query );
    } 
}
add_filter( 'pre_get_posts', 'belmondo_modify_cruise_archive_query' );

edit: For clarity, I've added some grouping parenthesis to your if statement, since it's got 4 required and's, the last of which is a required or.

Upvotes: 1

Related Questions