web-dev-qa-db-fra.com

Obtenir les termes par taxonomie ET post_type

J'ai 2 types de signets personnalisés 'bookmarks' et 'snippets' ainsi qu'un 'tag' de taxonomie partagée. Je peux générer une liste de tous les termes de la taxonomie avec get_terms (), mais je ne vois pas comment limiter la liste au type de publication. Ce que je recherche fondamentalement est quelque chose comme ceci:

get_terms(array('taxonomy' => 'tag', 'post_type' => 'snippet'));

Y a-t-il un moyen d'y parvenir? Les idées sont grandement appréciées !!

Oh, je suis sur WP 3.1.1

17
Gavin Hewitt

Voici une autre façon de faire quelque chose de similaire, avec une requête SQL:

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('%s') AND tt.taxonomy IN('%s')
        GROUP BY t.term_id",
        join( "', '", $post_types ),
        join( "', '", $taxonomies )
    );

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

    return $results;

}
11
Braydon

Il se trouve donc que j’avais besoin de quelque chose comme ça pour un projet sur lequel je travaille. J'ai simplement écrit une requête pour sélectionner tous les articles d'un type personnalisé, puis je vérifie quels sont les termes réels de ma taxonomie qu'ils utilisent.

Ensuite, j'ai eu tous les termes de cette taxonomie en utilisant get_terms() et ensuite, je n'ai utilisé que ceux qui étaient dans les deux listes, je l'ai résumé dans une fonction et j'ai terminé.

Mais ensuite, il me fallait plus que les identifiants: j'avais besoin des noms, j'ai donc ajouté un nouvel argument nommé $fields afin de pouvoir indiquer à la fonction ce qu'elle retournait. Ensuite, je me suis dit que get_terms acceptait de nombreux arguments et que ma fonction était limitée à de simples termes utilisés par un type de message. J'ai donc ajouté une autre instruction if et voilà:

La fonction:

/* get terms limited to post type 
 @ $taxonomies - (string|array) (required) The taxonomies to retrieve terms from. 
 @ $args  -  (string|array) all Possible Arguments of get_terms http://codex.wordpress.org/Function_Reference/get_terms
 @ $post_type - (string|array) of post types to limit the terms to
 @ $fields - (string) What to return (default all) accepts ID,name,all,get_terms. 
 if you want to use get_terms arguments then $fields must be set to 'get_terms'
*/
function get_terms_by_post_type($taxonomies,$args,$post_type,$fields = 'all'){
    $args = array(
        'post_type' => (array)$post_type,
        'posts_per_page' => -1
    );
    $the_query = new WP_Query( $args );
    $terms = array();
    while ($the_query->have_posts()){
        $the_query->the_post();
        $curent_terms = wp_get_object_terms( $post->ID, $taxonomy);
        foreach ($curent_terms as $t){
          //avoid duplicates
            if (!in_array($t,$terms)){
                $terms[] = $c;
            }
        }
    }
    wp_reset_query();
    //return array of term objects
    if ($fields == "all")
        return $terms;
    //return array of term ID's
    if ($fields == "ID"){
        foreach ($terms as $t){
            $re[] = $t->term_id;
        }
        return $re;
    }
    //return array of term names
    if ($fields == "name"){
        foreach ($terms as $t){
            $re[] = $t->name;
        }
        return $re;
    }
    // get terms with get_terms arguments
    if ($fields == "get_terms"){
        $terms2 = get_terms( $taxonomies, $args );
        foreach ($terms as $t){
            if (in_array($t,$terms2)){
                $re[] = $t;
            }
        }
        return $re;
    }
}

Usage:

Si vous avez seulement besoin d'une liste d'identifiants de termes, alors:

$terms = get_terms_by_post_type('tag','','snippet','ID');

Si vous n'avez besoin que d'une liste de noms de termes, alors:

$terms = get_terms_by_post_type('tag','','snippet','name');

Si vous n'avez besoin que d'une liste d'objets termes, alors:

$terms = get_terms_by_post_type('tag','','snippet');

Et si vous devez utiliser des arguments supplémentaires de get_terms tels que: orderby, order, hierarchical ...

$args = array('orderby' => 'count', 'order' => 'DESC',  'hide_empty' => 1);
$terms = get_terms_by_post_type('tag',$args,'snippet','get_terms');

Prendre plaisir!

Mettre à jour:

Pour fixer le nombre de termes à un changement de type de message spécifique:

foreach ($current_terms as $t){
          //avoid duplicates
            if (!in_array($t,$terms)){
                $terms[] = $t;
            }
        }

à:

foreach ($current_terms as $t){
    //avoid duplicates
    if (!in_array($t,$terms)){
        $t->count = 1;
        $terms[] = $t;
    }else{
        $key = array_search($t, $terms);
        $terms[$key]->count = $terms[$key]->count + 1;
    }
}
14
Bainternet

J'ai écrit une fonction qui vous permet de passer post_type dans le tableau $args à la fonction get_terms():

HT à @braydon pour l'écriture du code SQL.

 /**
 * terms_clauses
 *
 * filter the terms clauses
 *
 * @param $clauses array
 * @param $taxonomy string
 * @param $args array
 * @return array
**/
function terms_clauses($clauses, $taxonomy, $args)
{
    global $wpdb;

    if ($args['post_type'])
    {
        $clauses['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";
        $clauses['where'] .= " AND p.post_type='{$args['post_type']}'"; 
    }
    return $clauses;
}
add_filter('terms_clauses', 'terms_clauses', 10, 3);
8
jessica

Excellente question et réponses solides.

J'ai vraiment aimé l'approche de @ jessica utilisant le filtre terms_clauses, car elle étend la fonction get_terms de manière très raisonnable.

Mon code est une continuation de son idée, avec quelques SQL de @braydon pour réduire les doublons. Cela permet aussi un tableau de post_types:

/**
 * my_terms_clauses
 *
 * filter the terms clauses
 *
 * @param $clauses array
 * @param $taxonomy string
 * @param $args array
 * @return array
 **/
function my_terms_clauses($clauses, $taxonomy, $args)
{
  global $wpdb;

  if ($args['post_types'])
  {
    $post_types = $args['post_types'];

    // allow for arrays
    if ( is_array($args['post_types']) ) {
      $post_types = implode("','", $args['post_types']);
    }
    $clauses['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";
    $clauses['where'] .= " AND p.post_type IN ('". esc_sql( $post_types ). "') GROUP BY t.term_id";
  }
  return $clauses;
}
add_filter('terms_clauses', 'my_terms_clauses', 99999, 3);

Comme get_terms n'a pas de clause pour GROUPY BY, je devais l'ajouter à la fin de la clause WHERE. Remarquez que la priorité du filtre est très haute, dans l’espoir que cela ira toujours en dernier.

7
daggerhart

Je ne pouvais pas utiliser les arguments get_terms pour travailler avec la version de Gavin du code ci-dessus, mais finalement je l'ai fait

$terms2 = get_terms( $taxonomy );

à

$terms2 = get_terms( $taxonomy, $args );

comme c'était dans la fonction originale de Bainternet.

3
tzeldin88

@Bainternet: Merci! J'ai dû modifier légèrement la fonction car cela ne fonctionnait pas (quelques fautes de frappe). Le seul problème maintenant est que le nombre de termes est désactivé. Le nombre ne prend pas en compte le type de message, je ne pense donc pas que vous puissiez utiliser get_terms () pour cela.

function get_terms_by_post_type($post_type,$taxonomy,$fields='all',$args){
    $q_args = array(
        'post_type' => (array)$post_type,
        'posts_per_page' => -1
    );
    $the_query = new WP_Query( $q_args );

    $terms = array();

    while ($the_query->have_posts()) { $the_query->the_post();

        global $post;

        $current_terms = get_the_terms( $post->ID, $taxonomy);

        foreach ($current_terms as $t){
            //avoid duplicates
            if (!in_array($t,$terms)){
                $t->count = 1;
                $terms[] = $t;
            }else{
                $key = array_search($t, $terms);
                $terms[$key]->count = $terms[$key]->count + 1;
            }
        }
    }
    wp_reset_query();

    //return array of term objects
    if ($fields == "all")
        return $terms;
    //return array of term ID's
    if ($fields == "ID"){
        foreach ($terms as $t){
            $re[] = $t->term_id;
        }
        return $re;
    }
    //return array of term names
    if ($fields == "name"){
        foreach ($terms as $t){
            $re[] = $t->name;
        }
        return $re;
    }
    // get terms with get_terms arguments
    if ($fields == "get_terms"){
        $terms2 = get_terms( $taxonomy, $args );

        foreach ($terms as $t){
            if (in_array($t,$terms2)){
                $re[] = $t;
            }
        }
        return $re;
    }
}

EDIT: Ajouté le (s) correctif (s). Mais de toute façon cela ne fonctionne toujours pas pour moi. Le compte montre toujours la valeur incorrecte.

0
Gavin Hewitt

Évitez les doublons:

//avoid duplicates
    $mivalor=$t->term_id;
    $arr=array_filter($terms, function ($item) use ($mivalor) {return isset($item->term_id) && $item->term_id == $mivalor;});

    if (empty($arr)){
    $t->count=1;
            $terms[] = $t;
        }else{
            $key = array_search($t, $terms);
            $terms[$key]->count = $terms[$key]->count + 1;
        }
0
Kaotiko