WordPress.org

Make WordPress Core

Opened 4 years ago

Closed 5 months ago

#18106 closed feature request (wontfix)

get_terms of posts in a specific post_type

Reported by: braydonf Owned by:
Milestone: Priority: normal
Severity: normal Version:
Component: Taxonomy Keywords: close needs-patch
Focuses: Cc:

Description

I have searched most everywhere to find how to use get_terms for a specific post type, and have only found this article: http://wordpress.stackexchange.com/questions/14331/get-terms-by-taxonomy-and-post-type However this is doing a query for posts and getting the terms for those posts rather than doing a straight SQL statement, so it's not as efficient or ideal.

Adding an additional argument for additional where statements or a argument to specific a post_type, are two solutions I can imagine.

Change History (7)

comment:1 @braydonf4 years ago

Here is SQL to achieve something similar:

    static public function get_terms_by_post_type( $taxonomies, $post_types ) {

        global $wpdb;

        $query = $wpdb->prepare( "SELECT t.*, COUNT(*) from $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id INNER JOIN $wpdb->term_relationships AS r ON r.term_taxonomy_id = tt.term_taxonomy_id INNER JOIN $wpdb->posts AS p ON p.ID = r.object_id WHERE p.post_type IN('".join( "', '", $post_types )."') AND tt.taxonomy IN('".join( "', '", $taxonomies )."') GROUP BY t.term_id");

        $results = $wpdb->get_results( $query );

        return $results;

    }

Perhaps better for another function all together.

comment:2 @Bjorn24043 years ago

I was looking to do the same thing - it would be nice if this was a built-in function. I couldn't get your function to work exactly as you have it above. Maybe when you copied it in some quotes were added or changed. The INs in the WHERE clause seemed to be causing issues. I think your back quotes might have turned into regular quotes. I think I have it working correctly.

comment:3 @braydonf3 years ago

I've been working more on the above, and have included it as part of another plugin here: http://code.braydon.com/p/awe/source/tree/master/class-awe.php#L46

I think you're probably right about the quotes.

comment:4 @stephenh19883 years ago

  • Cc contact@… added

Someone asked how to do this on WordPress Stack Exchange http://wordpress.stackexchange.com/a/57449/9364. After implementing the above solution (with validation, cache etc). I realised a better way *might* be to use a wrapper around get_terms and make use of the filter.

This wrapper function takes the same arguments as get_terms, plus a 'post_types' key in the $args array. This takes a string|array of post types. It works for the defaults - but I haven't verified it for all arguments...

Not exactly future proof (neither solutions are) - but would be great to see this feature added at some point. I should image that count will cause problems as this is stored in the db. The workaround below replaces it with an SQL COUNT(*).

function wpse57444_get_terms($taxonomies, $args=array() ){

    if( !empty($args['post_types']) ){
        $args['post_types'] = (array) $args['post_types'];
        add_filter( 'terms_clauses','wpse_filter_terms_by_cpt',10,3);

        function wpse_filter_terms_by_cpt( $pieces, $tax, $args){
            global $wpdb;

            //Don't use db count
            $pieces['fields'] .=", COUNT(*) " ;

            //Join extra tables to restrict by post type.
            $pieces['join'] .=" INNER JOIN $wpdb->term_relationships AS r ON r.term_taxonomy_id = tt.term_taxonomy_id 
                                INNER JOIN $wpdb->posts AS p ON p.ID = r.object_id ";

            //Restrict by post type and Group by term_id for COUNTing.
            $post_types_str = implode(',',$args['post_types']);
            $pieces['where'].= $wpdb->prepare(" AND p.post_type IN(%s) GROUP BY t.term_id", $post_types_str);

            return $pieces;
        }
    }
        $terms = get_terms($taxonomies, $args);
        remove_filter( 'terms_clauses','wpse_filter_terms_by_cpt',10);          
        return $terms;
}

comment:5 @wonderboymusic20 months ago

  • Keywords close needs-patch added

There is probably going to be no unslow way to do this in core

comment:6 @wonderboymusic11 months ago

#27720 was marked as a duplicate.

comment:7 @boonebgorges5 months ago

  • Milestone Awaiting Review deleted
  • Resolution set to wontfix
  • Status changed from new to closed

get_terms() never makes reference to wp_term_relationships, so probably isn't the place for this kind of argument. It belongs somewhere like wp_get_post_terms().

The simplest way to implement this: in wp_get_post_terms(), if $post_id is empty and $args['post_type'] is not, then fetch the IDs of the posts in the post type(s) and pass them to wp_get_object_terms(). This is probably going to perform very slowly on large sites, and if your site is large enough you may even experience the joy of a seg fault. I don't think this is something we'd want to put into core.

It's hard to think of a more robust solution that won't look pretty hackish. wp_get_object_terms() is designed to be agnostic about the object type (post, user, etc). But filtering by post_type is very specific to posts. So, you can imagine passing a 'primary_id_column' and 'primary_table' column to wp_get_object_terms() to build the necessary JOIN clause, but it's hard to imagine the generic logic that would be necessary to build a WHERE clause like p.post_type IN ('post','page'). It'd be out of keeping with the rest of WP to have functions passing chunks of SQL between each other. At the same time, it doesn't make a lot of architectural sense to put logic directly into wp_get_object_terms() that is specific to posts (or, worse, post_type). And even if we did, we'd be looking at a query that'd be pretty slow for large numbers of posts.

On the basis of this, I'm closing as wontfix. Official recommendation for now is to query for post IDs on your own and pass to wp_get_object_terms(). If someone shows up here with a dynamite patch that proves me wrong, I'll eat my foot and dub you the champion of WordPress.

Note: See TracTickets for help on using tickets.