Custom Post Type Query multiple Taxonomy Terms

So on this Custom Post Type list page there is a list of Properties.

I have a custom search feature where they can search for Properties with certain Taxonomy Terms – but they can also search for Multiple Taxonomy Terms.

Question:
If someone selects 2 terms then the only Properties that should display in the list are Properties with term_one AND term_two

So I tried this below:

$required_features_slugs = array();
if( isset ( $_GET['features'] ) ) {
    $features_slugs = $_GET['features'];
}

$feature = get_terms('features');

$tmp_required = $required_features_slugs;

$property = array(
    'post_type'     => 'properties',
    'paged'         => $paged,
    'tax_query'         => array(
        array(
            'taxonomy'  => 'features',
            'field'     => 'slug',
            'terms'     => $tmp_required,
            'operator'  => 'AND',
        )
    ),
);

The URL looks like this:

features%5B%5D=accredited-landlord&features%5B%5D=cellarbasement

Now because these two Features are selected it should only return one Property because there is only one Property with term_one AND term_two together

However I don’t get this result instead I get 4 Properties because there are 4 Properties. 3 with term_one and 1 with term_two.

What I want to happen is for 1 Property to be returned because there is only one Property with term_once AND term_two together.

Is there something wrong that I am doing in the Query?

Edit: Updated PHP file

$required_features_slugs = array();
if( isset ( $_GET['features'] ) ) {
    $required_features_slugs = $_GET['features'];
}

$all_features = get_terms('features');

if( ! empty( $required_features_slugs ) ) {
    foreach ( $all_features as $feature ) {
        $tmp_required = $required_features_slugs;

        if( ! in_array($feature->slug, $required_features_slugs) ) {
            array_push( $tmp_required, $feature->slug );
        }

        $property = array(
            'post_type'     => 'properties',
            'paged'         => $paged,
            'tax_query'         => array(
                'taxonomy'  => 'features',
                'field'     => 'slug',
                'terms'     => $tmp_required,
                'operator'  => 'AND',
            ),
        );
    }
}

Edit #2:
So here is the var_dump to the Query:

array(3) { ["post_type"]=> string(10) "properties" ["paged"]=> int(1) ["tax_query"]=> array(4) { ["taxonomy"]=> string(8) "features" ["field"]=> string(4) "slug" ["terms"]=> array(2) { [0]=> string(19) "accredited-landlord" [1]=> string(14) "cellarbasement" } ["operator"]=> string(3) "AND" } }

And here is the var_dump to the terms:

array(2) { [0]=> string(19) "accredited-landlord" [1]=> string(14) "cellarbasement" }

3 Answers
3

Query Arguments:

If $input_terms is the input array of term slugs, then you should be able to use (if I understand the question correctly):

$property = [
    'post_type'         => 'properties',
    'paged'             => $paged,
    'tax_query'         => [
        [
            'taxonomy'  => 'features',
            'field'     => 'slug',
            'terms'     => $input_terms,
            'operator'  => 'AND',
        ]
    ],
];

where we added a missing array in the tax query.

Validation:

We should validate the input terms first.

Here are some examples:

  • Check the maximum number of allowed terms:

    $valid_input_terms_max_count = count( $input_terms ) <= 4;
    
  • Check the minimum number of allowed terms:

    $valid_input_terms_min_count = count( $input_terms ) >= 1;
    
  • Check if they input terms slugs exists (assumes non-empty input array):

    $valid_input_terms_slugs = array_reduce( 
        (array) $input_terms, 
        function( $carry, $item ) { 
            return $carry && null !== term_exists( $item, 'features' ); 
        }, 
        true 
    );
    

    where we collect term_exists() for all the terms into a single boolean value.

  • We could also match the input slugs against a predefined array of slugs

Generated SQL:

Here’s a generated SQL query for two existing term slugs in $input_terms:

SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID 
FROM wp_posts  
WHERE 1=1  AND ( 
  (
        SELECT COUNT(1)
        FROM wp_term_relationships
        WHERE term_taxonomy_id IN (160,161)
        AND object_id = wp_posts.ID
    ) = 2
) 
AND wp_posts.post_type="properties" 
AND (wp_posts.post_status="publish" OR wp_posts.post_status="private") 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC 
LIMIT 0, 10

Hope you can adjust this to your needs.

Leave a Comment