get_terms według niestandardowego typu postu

19

Mam dwa niestandardowe typy postów „kraj” i „miasto” oraz wspólną flagę taksonomii.

Jeśli używam:

<?php $flags = get_terms('flag', 'orderby=name&hide_empty=0');

Otrzymuję listę wszystkich terminów w taksonomii, ale chcę ograniczyć listę do typu „kraj”.

Jak mogę to zrobić?


Korzystanie z nowego rozwiązania

<?php 
$flags = wpse57444_get_terms('flags',array('parent' => 0,'hide_empty' => 1,'post_types' =>array('country')));
foreach ($flags as $flag) {
    $childTerms = wpse57444_get_terms('flags',array('parent' => $flag->term_id,'hide_empty' => 1,'post_types' =>array('country')));
    foreach ($childTerms as $childTerm) {
        echo $childTerm->name.'<br />';
    }
}
?>

Nie mogę powtórzyć nazwy $ childTerm-> name. Dlaczego?

użytkownik1443216
źródło
Czy możesz być trochę jaśniejszy?
TheDeadMedic

Odpowiedzi:

16

Obawiam się, że nie jest to możliwe natywnie (jeszcze?). Zobacz ten trac: http://core.trac.wordpress.org/ticket/18106

Podobnie na stronie administratora systematyki liczba postów odzwierciedla wszystkie typy postów. ( Jestem prawie pewien, że istnieje również bilet trac ). Http://core.trac.wordpress.org/ticket/14084

Zobacz także ten powiązany post .


Nowe rozwiązanie

Po napisaniu poniższego, opublikowałem znacznie lepszy sposób (przynajmniej w tym sensie, że możesz zrobić więcej), używając filtrów zawartych w get_terms()wywołaniu. Możesz utworzyć funkcję opakowania, która używa get_termsi (warunkowo) dodaje filtr do manipulowania zapytaniem SQL (w celu ograniczenia według typu postu).

Funkcja przyjmuje takie same argumenty jak get_terms($taxonomies, $args). $argsbierze dodatkowy argument, post_typesktórego bierze tablicę | ciąg typów postów.

Ale nie mogę zagwarantować, że wszystko działa „zgodnie z oczekiwaniami” (myślę, że uzupełnię liczbę). Wygląda na to, że działa przy użyciu domyślnej opcji $argsfor get_terms.

function wpse57444_get_terms( $taxonomies, $args=array() ){
    //Parse $args in case its a query string.
    $args = wp_parse_args($args);

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

            remove_filter( current_filter(), __FUNCTION__ );
            return $pieces;
        }
    } // endif post_types set

    return get_terms($taxonomies, $args);           
}

Stosowanie

$args =array(
    'hide_empty' => 0,
    'post_types' =>array('country','city'),
);

$terms = wpse57444_get_terms('flag',$args);

Oryginalne obejście

Zainspirowany powyższym biletem trac (przetestowany i działa dla mnie)

function wpse57444_filter_terms_by_cpt($taxonomy, $post_types=array() ){
    global $wpdb;

    $post_types=(array) $post_types;
    $key = 'wpse_terms'.md5($taxonomy.serialize($post_types));
    $results = wp_cache_get($key);

    if ( false === $results ) {
       $where =" WHERE 1=1";
       if( !empty($post_types) ){
            $post_types_str = implode(',',$post_types);
            $where.= $wpdb->prepare(" AND p.post_type IN(%s)", $post_types_str);
       }

       $where .= $wpdb->prepare(" AND tt.taxonomy = %s",$taxonomy);

       $query = "
          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
          GROUP BY t.term_id";

       $results = $wpdb->get_results( $query );
       wp_cache_set( $key, $results );
    }        

    return $results;
}

Stosowanie

 $terms = wpse57444_filter_terms_by_cpt('flag',array('country','city'));

lub

 $terms = wpse57444_filter_terms_by_cpt('flag','country');
Stephen Harris
źródło
Działa, ale co mogę zrobić z moimi $ args? Mam na myśli ... rodzic = 0 i orderby = name & hide_empty = 0
user1443216
Nie - to musi być tablicą: $args = array('parent'=>0,'orderby'=>'name','hide_empty'=>0);. Zmienię to, aby zezwolić na ciągi zapytań ...
Stephen Harris
Gdzie mogę umieścić moje $ args w tym przykładzie: $terms = wpse57444_filter_terms_by_cpt('flag',array('country','city'));?
user1443216
Nie możesz w tym, tylko w nowym rozwiązaniu:wpse57444_get_terms()
Stephen Harris
@ użytkownik1443216 $argsto drugi argument. Po prostu umieściłeśwpse57444_get_terms( 'flag', array( 'country', 'city' ) );
Kaiser
2

@ stephen-harris powyższa odpowiedź działała dla mnie tylko częściowo. Jeśli spróbowałem użyć go dwa razy na stronie, to nie działało. Martwi mnie też pomysł zakopywania takich zapytań mysql - myślę, że lepszą praktyką jest stosowanie podstawowych metod w celu uzyskania rozwiązania, aby uniknąć konfliktów z przyszłymi aktualizacjami WP. Oto moje rozwiązanie, oparte na komentarzu nr 7 do biletu Trac, do którego się odwołuje

function get_terms_by_custom_post_type( $post_type, $taxonomy ){
  $args = array( 'post_type' => $post_type);
  $loop = new WP_Query( $args );
  $postids = array();
  // build an array of post IDs
  while ( $loop->have_posts() ) : $loop->the_post();
    array_push($postids, get_the_ID());
  endwhile;
  // get taxonomy values based on array of IDs
  $regions = wp_get_object_terms( $postids,  $taxonomy );
  return $regions;
}

Stosowanie:

$terms = get_terms_by_custom_post_type('country','flag');

Działa to tylko z jednym typem postu i jedną taksonomią, ponieważ tego właśnie potrzebowałem, ale nie byłoby trudno zmodyfikować to, aby zaakceptować wiele wartości.

W wątku Trac wspomniano, że może to nie być dobrze skalowane, ale pracuję na dość małej skali i nie miałem problemów z prędkością.

Mark Pruce
źródło
to rozwiązanie wygląda dla mnie bardziej „natywnie” - w każdym razie - należy wywołać „wp_reset_postdata ()” zaraz po „endwhile” pętli: wordpress.stackexchange.com/questions/144343/…
Thomas Fellinger
2

Dwa niestandardowe typy postów „kraj” i „miasto” oraz wspólna „taksonomia” taksonomii. Chcesz ograniczyć listę do typu posta „kraj”.

Oto prostsze rozwiązanie:

$posts_in_post_type = get_posts( array(
    'fields' => 'ids',
    'post_type' => 'country',
    'posts_per_page' => -1,
) );
$terms = wp_get_object_terms( $posts_in_post_type, 'flag', array( 'ids' ) ); ?>
Alex
źródło
1

[edytuj] To jest komentarz do doskonałej odpowiedzi Stephena Harrisa.

Nie zwraca żadnych terminów, jeśli jest używany z wieloma typami postów jak ten $flags = wpse57444_get_terms('flags', array('post_types' => array('country','city')));. Wynika to z faktu, że $ wpdb-> preparat dezynfekuje ciąg $ post_types_str, na p.post_type IN('country,city')jaki powinien być p.post_type IN('country','city'). Zobacz ten bilet: 11102 . Skorzystaj z rozwiązania z tego tematu, aby obejść ten problem: /programming//a/10634225

keesiemeijer
źródło
1

Próbowałem także użyć odpowiedzi @ Stephen Harris, ale zapytania, którego potrzebowałem, było dość trudne do napisania jako jedno zapytanie i przy użyciu elementów filtrujących.

Ponadto musiałem używać tej funkcji wiele razy na tej samej stronie i rozwiązałem problem z zadeklarowaniem wpse_filter_terms_by_cptfunkcji poza funkcją otoki.

W każdym razie odpowiedź @Marka Pruce'a, moim zdaniem, pasuje lepiej, z tych samych powodów, które powiedział, nawet jeśli trzeba zrobić jeszcze jedno zapytanie (i powiązaną pętlę), aby przygotować argumenty dla wp_get_object_termsfunkcji.

Sgaddo
źródło