Obtenez les termes par taxonomie ET post_type

17

J'ai 2 types de messages personnalisés «signets» et «extraits» et une «balise» de taxonomie partagée. Je peux générer une liste de tous les termes de la taxonomie avec get_terms (), mais je ne sais pas comment limiter la liste au type de message. Ce que je recherche essentiellement, c'est quelque chose comme ceci:

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

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

Oh, je suis sur WP 3.1.1

Gavin Hewitt
la source

Réponses:

11

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;

}
Braydon
la source
Oui! Cela fait exactement ce que je veux.
Gavin Hewitt
print_r(get_terms_by_post_type(array('category') , array('event') ));spectaclesWarning: Missing argument 2 for wpdb::prepare()
devo
Je peux me tromper, mais du haut de ma tête, je ne pense pas que ces déclarations de «jointure» fonctionneront - c'est-à-dire qu'elles ne fonctionneraient que si des tableaux à valeur unique étaient passés. En effet, la fonction prepare échapperait à toutes les guillemets simples générés et considérerait chaque chaîne entière «join».
Codesmith
14

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 messages d'un type personnalisé, puis je vérifie quels sont les termes réels de ma taxonomie qu'ils utilisent.

Ensuite, j'ai obtenu tous les termes de cette taxonomie en utilisant get_terms(), puis j'ai utilisé uniquement ceux qui figuraient dans les deux listes, je l'ai enveloppé dans une fonction et j'ai terminé.

Mais ensuite, j'avais besoin de plus que des ID: j'avais besoin des noms, j'ai donc ajouté un nouvel argument nommé $fieldspour pouvoir dire à la fonction quoi renvoyer. Ensuite, je me suis dit que cela get_termsacceptait de nombreux arguments et ma fonction se limitait à des termes utilisés par un type de message, j'ai donc ajouté une autre ifdéclaration et c'est parti:

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 n'avez besoin que d'une liste d'ID de termes, alors:

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

Si vous avez seulement besoin 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 de termes, alors:

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

Et si vous avez besoin d'utiliser des arguments supplémentaires de get_terms comme: 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!

Mise à jour:

Pour fixer le nombre de termes à une modification de type de publication 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;
    }
}
Bainternet
la source
ne serait-il pas préférable d'utiliser (array) $argsau lieu d'une liste de 4 $ vars? Cela vous permettrait de ne pas vous soucier de l'ordre dans lequel vous jetez les arguments, donc quelque chose comme get_terms_by_post_type( $args = array( 'taxonomies', 'args', 'post_type', 'fields' => 'all') ), puis les appelez à l'intérieur de la fonction avec $args['taxonomies']. Cela vous aiderait à éviter d'ajouter des valeurs vides et à vous rappeler de l'ordre de vos arguments. Je suggère également d'utiliser des guillemets simples au lieu de doubles. Je les ai vus cinq fois plus rapides.
kaiser
1
@kaiser - Les chaînes entre guillemets doubles doivent être analysées, où les valeurs entre guillemets simples sont toujours traitées comme littérales. Lorsque vous utilisez des variables dans une chaîne, cela a du sens et il est tout à fait correct d'utiliser des guillemets doubles, mais pour les valeurs de chaîne non variables, les guillemets simples sont plus idéaux (car ils n'auront pas besoin d'être analysés) et légèrement plus rapides (nous '' parler de millisecondes dans la plupart des cas).
t31os le
@ t31os - Absolument correct. Je préfère toujours 'this is my mood: '.$valueplus "this is my mood: $value", en raison de la lisibilité. En ce qui concerne la vitesse: ce n'est pas légèrement - j'ai mesuré jusqu'à cinq fois. Et lorsque vous utilisez des guillemets doubles dans l'ensemble de votre thème partout, ils résumeront rapidement lorsque vous aurez beaucoup de demandes. Quoi qu'il en soit, vous l'avez dit clairement.
kaiser
@ t31os Après une discussion, j'ai re-mesuré la vitesse de "vs 'et je me trompais . La différence est bien au-delà de tout ce que quiconque remarquerait.
kaiser
1
+1 fonction sympa! 2 fautes de frappe: $ taxonomies est utilisé dans la fonction $ taxonomy et $ terms [] = $ c; doit être $ terms [] = $ t;
Rob Vermeer
8

J'ai écrit une fonction qui vous permet de passer post_typele $argstableau à la get_terms()fonction:

HT à @braydon pour avoir écrit le 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);
jessica
la source
7

Grande question et réponses solides.

J'ai vraiment aimé l'approche de @jessica en utilisant le filtre terms_clauses, car il é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. Il permet également 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);

Parce que get_terms n'a pas de clause pour GROUPY BY, j'ai dû l'ajouter à la fin de la clause WHERE. Notez que la priorité du filtre est très élevée, dans l'espoir qu'elle durera toujours en dernier.

poignard
la source
3

Je n'ai pas pu faire en sorte que les arguments get_terms fonctionnent avec la version de Gavin du code ci-dessus, mais finalement en changeant

$terms2 = get_terms( $taxonomy );

à

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

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

tzeldin88
la source
1
Correction dans la version actuelle
Gavin Hewitt
0

@Bainternet: Merci! J'ai dû modifier légèrement la fonction car elle 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, donc je ne pense pas que vous puissiez utiliser get_terms () dans ce cas.

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 du ou des correctifs. Mais ça ne marche toujours pas pour moi. Le compte affiche toujours la valeur incorrecte.

Gavin Hewitt
la source
C'est une autre histoire, mais vous pouvez compter en évitant les doublons dans la boucle while.
Bainternet
J'ai mis à jour ma réponse avec une correction du nombre de termes.
Bainternet
1
Veuillez ne pas ajouter de suivis comme réponses, à moins que vous ne répondiez spécifiquement à votre propre question , des ajouts devraient plutôt être apportés à la question d'origine.
t31os
1
@ t31os: Ah oui, je me demandais comment ajouter un ajout. Je n'ai pas pensé à modifier ma question. Merci!
Gavin Hewitt
Comment puis-je appeler cela? print_r(get_terms_by_post_typea(array('event','category','',array()));celui-ci donne Warning: Invalid argument supplied for foreach()pour la ligneforeach ($current_terms as $t){
devo
0

É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;
        }
Kaotiko
la source
1
Pouvez-vous expliquer pourquoi cela résout le problème? Voir Comment répondre .
brasofilo