get_terms după tip de postare personalizat

4 iul. 2012, 19:29:23
Vizualizări: 33.6K
Voturi: 24

Am două tipuri de postări personalizate 'country' (țară) și 'city' (oraș) și o taxonomie comună 'flag' (steag).

Dacă folosesc:

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

Obțin o listă cu toți termenii din taxonomie, dar vreau să limitez lista la tipul de postare 'country'.

Cum pot face asta?


Folosind noua soluție

<?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 />';
    }
}
?>

Nu pot afișa $childTerm->name. De ce?

1
Comentarii

Puteți fi puțin mai explicit?

TheDeadMedic TheDeadMedic
4 iul. 2012 19:42:36
Toate răspunsurile la întrebare 5
10
20

Din păcate, acest lucru nu este posibil în mod nativ (încă?). Consultați acest trac: http://core.trac.wordpress.org/ticket/18106

Similar, pe pagina de administrare a taxonomiei, numărul de articole reflectă toate tipurile de postări. (Sunt destul de sigur că există și un ticket trac pentru asta) http://core.trac.wordpress.org/ticket/14084

Consultați de asemenea, acest articol conex.


Soluție nouă

După ce am scris cea de mai jos, am lansat o metodă mult mai bună (cel puțin în sensul că poți face mai multe) și anume utilizarea filtrelor disponibile în apelul get_terms(). Puteți crea o funcție wrapper care utilizează get_terms și (condițional) adaugă un filtru pentru a manipula interogarea SQL (pentru a restricționa după tipul de postare).

Funcția acceptă aceleași argumente ca get_terms($taxonomies, $args). $args acceptă argumentul suplimentar post_types, care primește un array|string de tipuri de postări.

Dar nu pot garanta că totul funcționează 'așa cum este de așteptat' (mă gândesc la umplerea numărării). Se pare că funcționează folosind doar argumentele implicite $args pentru get_terms.

function wpse57444_get_terms( $taxonomies, $args=array() ){
    //Parsează $args în caz că este un șir de interogare.
    $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;

            // Nu utiliza numărarea din baza de date
            $pieces['fields'] .=", COUNT(*) " ;

            //Alătură tabele suplimentare pentru a restricționa după tipul de postare.
            $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 ";

            // Restricționează după tipul de postare și Grupează după term_id pentru numărare.
            $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 setat

    return get_terms($taxonomies, $args);           
}

Utilizare

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

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

Soluție originală temporară

Inspirat din ticket-ul trac menționat mai sus, (testat și funcționează pentru mine)

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

Utilizare

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

sau

 $terms = wpse57444_filter_terms_by_cpt('flag','country');
4 iul. 2012 20:00:56
Comentarii

Funcționează, dar ce pot face cu $args? Adică... parent=0&orderby=name&hide_empty=0

user1443216 user1443216
4 iul. 2012 22:11:24

nu - trebuie să fie un array: $args = array('parent'=>0,'orderby'=>'name','hide_empty'=>0);. Voi edita acest lucru pentru a permite șiruri de interogare...

Stephen Harris Stephen Harris
4 iul. 2012 22:44:51

Unde pot pune $args în acest exemplu: $terms = wpse57444_filter_terms_by_cpt('flag',array('country','city'));?

user1443216 user1443216
4 iul. 2012 22:59:45

Nu poți în acesta, doar în noua soluție:wpse57444_get_terms()

Stephen Harris Stephen Harris
4 iul. 2012 23:21:09

@user1443216 $args este al doilea argument. Acolo pur și simplu pui wpse57444_get_terms( 'flag', array( 'country', 'city' ) );

kaiser kaiser
4 iul. 2012 23:21:12

@StephenHarris Chiar și nucleul încearcă să elimine tot ce seamănă cu un șir de interogare. Doar adaugă balast. Am eliminat deja acest tip de funcționalitate din toate lucrurile mele după o discuție cu unul dintre dezvoltatorii nucleului.

kaiser kaiser
4 iul. 2012 23:22:25

Dacă folosesc asta: $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 />'; } } nu pot afișa $childTerm->name

user1443216 user1443216
7 iul. 2012 17:03:36

pentru a obține numărătoarea corectă a trebuit să actualizez această linie la: $pieces['fields'] .= ", COUNT(*) as count ";

locomo locomo
9 mar. 2018 23:34:12

Încă nu există o metodă mai bună aproape în 2019? Asta e trist :)

trainoasis trainoasis
14 dec. 2018 00:20:26

Soluția de mai sus funcționează foarte bine cu un singur tip de post. Cu mai multe tipuri de postări, am întâmpinat o problemă cu pregătirea. Se pare că nu sanitizează $post_types_str, care va afișa IN( 'whitepaper, blog' ) în loc de IN( 'whitepaper', 'blog' ). Vă rugăm să consultați acest articol pentru o soluție link

Redox Redox
4 nov. 2019 10:51:21
Arată celelalte 5 comentarii
1

Două tipuri personalizate de postări 'țară' și 'oraș' și o taxonomie partajată 'steag'. Doriți să limitați lista la tipul de postare 'țară'.

Iată o soluție mai simplă:

$posts_in_post_type = get_posts( array(
    'fields' => 'ids',
    'post_type' => 'țară',
    'posts_per_page' => -1,
) );
$terms = wp_get_object_terms( $posts_in_post_type, 'steag', array( 'ids' ) ); ?>
7 nov. 2017 14:44:29
Comentarii

Aceasta pare a fi metoda recomandată conform acestui ticket Trac: https://core.trac.wordpress.org/ticket/18106#comment:7. Rețineți că această abordare poate fi foarte lentă, așa că este posibil să fie nevoie să implementați un mecanism de caching dacă site-ul dvs. are o mulțime de articole.

Jules Jules
11 nov. 2020 16:25:33
1

Răspunsul lui @stephen-harris de mai sus a funcționat pentru mine doar parțial. Dacă încercam să-l folosesc de două ori pe aceeași pagină, nu a funcționat. De asemenea, ideea de a ascunde interogări MySQL în acest mod mă îngrijorează - cred că este mai bine să folosești metodele de bază pentru a obține o soluție, pentru a evita conflictele cu viitoarele actualizări ale WordPress. Iată soluția mea, bazată pe comentariul #7 din ticketul Trac pe care l-a menționat

function get_terms_by_custom_post_type( $post_type, $taxonomy ){
  $args = array( 'post_type' => $post_type);
  $loop = new WP_Query( $args );
  $postids = array();
  // construiește un array cu ID-urile postărilor
  while ( $loop->have_posts() ) : $loop->the_post();
    array_push($postids, get_the_ID());
  endwhile;
  // obține valorile taxonomiei bazate pe array-ul de ID-uri
  $regions = wp_get_object_terms( $postids,  $taxonomy );
  return $regions;
}

Utilizare:

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

Această funcție funcționează doar pentru un singur tip de postare și o singură taxonomie, deoarece asta aveam nevoie, dar nu ar fi prea greu să o modifici pentru a accepta mai multe valori.

Pe acel thread Trac s-a menționat că această abordare poate avea probleme de scalabilitate, dar eu lucrez la o scară destul de mică și nu am întâmpinat probleme de viteză.

7 mar. 2017 18:52:27
Comentarii

această soluție pare mai "nativă" pentru mine - oricum ->

ar trebui să apelezi "wp_reset_postdata()" imediat după "endwhile" al buclei: https://wordpress.stackexchange.com/questions/144343/wp-reset-postdata-or-wp-reset-query-after-a-custom-loop

Thomas Fellinger Thomas Fellinger
22 nov. 2017 12:04:47
0

[editează] Acesta este un comentariu la răspunsul excelent al lui Stephen Harris.

Nu returnează niciun termen dacă este folosit cu mai multe tipuri de postări, astfel: $flags = wpse57444_get_terms('flags', array('post_types' => array('country','city')));. Acest lucru se întâmplă deoarece $wpdb->prepare sanitizează șirul $post_types_str la p.post_type IN('country,city'), în timp ce ar trebui să fie p.post_type IN('country','city'). Vezi acest tichet: 11102. Folosește soluția din acest subiect pentru a rezolva problema: https://stackoverflow.com/a/10634225

13 feb. 2013 16:42:10
0

Am încercat și eu să folosesc răspunsul lui @Stephen Harris, dar interogarea de care aveam nevoie era destul de greu de scris ca o singură interogare și folosind fragmentele de filtru.

În plus, aveam nevoie să folosesc acea funcție de mai multe ori pe aceeași pagină și am rezolvat problema declarând funcția wpse_filter_terms_by_cpt în afara funcției wrapper.

Oricum, răspunsul lui @Mark Pruce, în opinia mea, se potrivește mai bine, din aceleași motive pe care le-a menționat, chiar dacă necesită să faci încă o interogare (și bucla asociată) pentru a pregăti argumentele pentru funcția wp_get_object_terms.

3 aug. 2017 12:31:57