Begriffe nach Taxonomie UND post_type abrufen

17

Ich habe 2 benutzerdefinierte Beitragstypen "Lesezeichen" und "Snippets" und ein gemeinsames Taxonomie "Tag". Ich kann mit get_terms () eine Liste aller Begriffe in der Taxonomie erstellen, aber ich kann nicht herausfinden, wie ich die Liste auf den Beitragstyp beschränken kann. Was ich im Grunde suche, ist ungefähr so:

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

Gibt es einen Weg, dies zu erreichen? Ideen werden sehr geschätzt !!

Oh, ich bin in WP 3.1.1

Gavin Hewitt
quelle

Antworten:

11

Hier ist eine andere Möglichkeit, mit einer SQL-Abfrage etwas Ähnliches zu tun:

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
quelle
Ja! Das macht genau das, was ich will.
Gavin Hewitt
print_r(get_terms_by_post_type(array('category') , array('event') ));ShowsWarning: Missing argument 2 for wpdb::prepare()
Devo
Ich könnte mich irren, aber auf den ersten Blick glaube ich nicht, dass diese Join-Anweisungen funktionieren werden - dh, sie funktionieren nur, wenn ein Einzelwert-Array übergeben wird. Dies liegt daran, dass die Vorbereitungsfunktion alle generierten einfachen Anführungszeichen überschreibt und jede ganze Zeichenfolge als eine Zeichenfolge 'verbindet'.
Codesmith
14

So etwas brauchte ich einfach für ein Projekt, an dem ich arbeite. Ich habe einfach eine Abfrage geschrieben, um alle Posts eines benutzerdefinierten Typs auszuwählen, und dann überprüft, welche Begriffe in meiner Taxonomie tatsächlich verwendet werden.

Dann habe ich alle Begriffe dieser Taxonomie verwendet get_terms()und dann habe ich nur die verwendet, die in beiden Listen enthalten waren, habe sie in eine Funktion eingepackt und war fertig.

Aber dann brauchte ich mehr als nur die IDs: Ich brauchte die Namen, also fügte ich ein neues Argument mit dem Namen hinzu, $fieldsdamit ich der Funktion mitteilen konnte, was zurückgegeben werden soll. Dann stellte ich fest, dass das get_termsviele Argumente akzeptiert und meine Funktion auf einfache Begriffe beschränkt war, die von einem Post-Typ verwendet werden. Also fügte ich eine weitere ifAnweisung hinzu und los geht's:

Die Funktion:

/* 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;
    }
}

Verwendung:

Wenn Sie nur eine Liste mit Term-IDs benötigen, gehen Sie wie folgt vor:

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

Wenn Sie nur eine Liste mit Begriffsnamen benötigen, gehen Sie wie folgt vor:

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

Wenn Sie nur eine Liste von Termobjekten benötigen, dann:

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

Und wenn Sie zusätzliche Argumente von get_terms verwenden müssen, wie: orderby, order, hierarchical ...

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

Genießen!

Aktualisieren:

So korrigieren Sie die Anzahl der Begriffe für eine bestimmte Änderung des Beitragstyps:

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

zu:

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
quelle
Wäre es nicht besser, wenn Sie (array) $argsanstelle einer Liste von 4 $ Vars verwenden? Dies würde es Ihnen ermöglichen, sich nicht um die Reihenfolge zu kümmern, in der Sie die Argumente einwerfen, also so etwas wie get_terms_by_post_type( $args = array( 'taxonomies', 'args', 'post_type', 'fields' => 'all') )und sie dann innerhalb der Funktion mit aufrufen $args['taxonomies']. Dies würde Ihnen helfen, keine leeren Werte hinzuzufügen und sich nicht die Reihenfolge Ihrer Argumente merken zu müssen. Ich würde auch vorschlagen, einfache Anführungszeichen anstelle von doppelten zu verwenden. Ich habe gesehen, wie sie bis zu fünfmal schneller waren.
Kaiser
1
@kaiser - Strings in doppelten Anführungszeichen müssen analysiert werden, wobei Werte in einfachen Anführungszeichen immer als Literal behandelt werden. Wenn Sie Variablen in einer Zeichenfolge verwenden, ist die Verwendung von doppelten Anführungszeichen sinnvoll und in Ordnung. Für nicht variable Zeichenfolgenwerte sind einfache Anführungszeichen jedoch idealer (da sie nicht analysiert werden müssen) und etwas schneller (wir '). in den meisten Fällen handelt es sich um Millisekunden).
T31OS
@ t31os - Absolut richtig. Ich bevorzuge 'this is my mood: '.$valueimmer "this is my mood: $value"noch wegen der Lesbarkeit. Wenn es um Geschwindigkeit geht: Es ist nicht leicht - ich habe bis zu fünf Mal gemessen. Und wenn Sie in Ihrem gesamten Thema überall doppelte Anführungszeichen verwenden, werden diese schnell zusammengefasst, wenn Sie viele Anfragen haben. Wie dem auch sei, Sie haben das deutlich gemacht.
Kaiser
@ t31os Nach einer Diskussion habe ich die Geschwindigkeit von "vs. neu gemessen 'und mich geirrt. Der Unterschied liegt weit außerhalb dessen, was jemand bemerken würde.
Kaiser
1
+1 schöne Funktion! 2 Tippfehler: $ taxonomies wird in der Funktion $ taxonomy und $ terms [] = $ c verwendet; muss $ terms [] = $ t sein;
Rob Vermeer
8

Ich habe eine Funktion geschrieben, mit der Sie post_typedas $argsArray an die get_terms()Funktion übergeben können:

HT an @braydon zum Schreiben der 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
quelle
7

Tolle Frage und solide Antworten.

Der Ansatz von @jessica, den Filter terms_clauses zu verwenden, hat mir sehr gut gefallen, da er die Funktion get_terms auf sehr vernünftige Weise erweitert.

Mein Code ist eine Fortsetzung ihrer Idee, mit etwas SQL von @braydon, um Duplikate zu reduzieren. Es erlaubt auch ein Array von 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);

Da get_terms keine Klausel für GROUPY BY hat, musste ich sie am Ende der WHERE-Klausel einfügen. Beachten Sie, dass ich die Filterpriorität sehr hoch eingestellt habe, in der Hoffnung, dass sie immer die letzte ist.

Dolchhart
quelle
3

Ich war nicht in der Lage, die Argumente get_terms für Gavins Version des obigen Codes zu verwenden, habe es aber schließlich durch Ändern getan

$terms2 = get_terms( $taxonomy );

zu

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

wie es in der ursprünglichen Funktion von Bainternet war.

tzeldin88
quelle
1
In der aktuellen Version wurde das
Gavin Hewitt
0

@Bainternet: Danke! Ich musste die Funktion leicht ändern, da sie nicht funktionierte (einige Tippfehler). Das einzige Problem ist jetzt, dass die Termanzahl deaktiviert ist. Die Anzahl berücksichtigt nicht den Post-Typ, daher kann get_terms () hier nicht verwendet werden.

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;
    }
}

BEARBEITEN: Die Korrektur (en) wurden hinzugefügt. Aber irgendwie funktioniert es immer noch nicht für mich. Der Zähler zeigt immer noch den falschen Wert an.

Gavin Hewitt
quelle
Das ist eine andere Geschichte, aber Sie können zählen, wenn Sie in der while-Schleife Duplikate vermeiden.
Bainternet
Ich habe meine Antwort mit einem Term Count Fix aktualisiert.
Bainternet
1
Bitte fügen Sie keine Follow-ups als Antworten hinzu, es sei denn, Sie beantworten speziell Ihre eigene Frage . Stattdessen sollten Ergänzungen zur ursprünglichen Frage vorgenommen werden.
T31OS
1
@ t31os: Ah ja, ich habe mich gefragt, wie ich einen Zusatz hinzufügen soll. Ich habe nicht daran gedacht, meine Frage zu bearbeiten. Vielen Dank!
Gavin Hewitt
Wie kann ich das nennen? print_r(get_terms_by_post_typea(array('event','category','',array()));Dieser gibt Warning: Invalid argument supplied for foreach()für die Linieforeach ($current_terms as $t){
devo
0

Vermeiden Sie Duplikate:

//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
quelle
1
Können Sie erklären, warum dies das Problem löst? Siehe Antworten .
Brasofilo