Uzyskaj warunki według taksonomii ORAZ typu post_

17

Mam 2 niestandardowe typy „zakładek” i „fragmentów” oraz wspólny „tag” taksonomii. Mogę wygenerować listę wszystkich terminów w taksonomii za pomocą get_terms (), ale nie mogę wymyślić, jak ograniczyć listę do typu postu. W zasadzie szukam czegoś takiego:

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

Czy istnieje sposób na osiągnięcie tego? Pomysły są bardzo mile widziane !!

Och, jestem na WP 3.1.1

Gavin Hewitt
źródło

Odpowiedzi:

11

Oto inny sposób zrobienia czegoś podobnego za pomocą jednego zapytania 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
źródło
Tak! To robi dokładnie to, czego chcę.
Gavin Hewitt,
print_r(get_terms_by_post_type(array('category') , array('event') ));pokazujeWarning: Missing argument 2 for wpdb::prepare()
devo
Mogę się mylić, ale nie sądzę, żeby te stwierdzenia „przyłączyły się” działały - tzn. Działałyby tylko wtedy, gdy przejdą tablice pojedynczej wartości. Wynika to z faktu, że funkcja preparacji unikałaby wszystkich wygenerowanych pojedynczych cudzysłowów i rozważała każde „połączenie” jednego łańcucha.
Codesmith
14

Tak się składa, że ​​potrzebowałem czegoś takiego do projektu, nad którym pracuję. Po prostu napisałem zapytanie, aby wybrać wszystkie posty typu niestandardowego, a następnie sprawdzam, jakie są rzeczywiste warunki mojej taksonomii, z której korzystają.

Potem wykorzystałem wszystkie warunki tej taksonomii, get_terms()a potem użyłem tylko tych, które były na obu listach, zawinąłem je w funkcję i skończyłem.

Ale potem potrzebowałem więcej niż tylko identyfikatorów: potrzebowałem nazw, więc dodałem nowy argument o nazwie, $fieldsaby móc powiedzieć funkcji, co mam zwrócić. Potem pomyślałem, że get_termsakceptuje wiele argumentów, a moja funkcja była ograniczona do po prostu terminów używanych przez typ postu, więc dodałem jeszcze jedenif instrukcję i proszę bardzo:

Funkcja:

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

Stosowanie:

Jeśli potrzebujesz tylko listy terminów id:

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

Jeśli potrzebujesz tylko listy nazw terminów, to:

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

Jeśli potrzebujesz tylko listy obiektów terminowych, to:

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

A jeśli potrzebujesz użyć dodatkowych argumentów get_terms, takich jak: orderby, order, hierarchical ...

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

Cieszyć się!

Aktualizacja:

Aby naprawić liczbę haseł do konkretnej zmiany typu posta:

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

do:

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
źródło
czy nie byłoby lepiej, gdybyś użył (array) $argszamiast listy 4 $ vars? Pozwoliłoby to nie przejmować się kolejnością dodawania argumentów, więc coś w stylu, get_terms_by_post_type( $args = array( 'taxonomies', 'args', 'post_type', 'fields' => 'all') )a następnie wywołaj je w funkcji za pomocą $args['taxonomies']. Pomoże ci to unikać dodawania pustych wartości i pamiętać o kolejności swoich argumentów. Sugeruję również stosowanie pojedynczych cudzysłowów zamiast podwójnych. Widziałem, jak leciały nawet pięć razy szybciej.
kaiser
1
@kaiser - Ciągi znaków cudzysłowu muszą zostać przeanalizowane, a jako wartości pojedynczego cudzysłowu są zawsze traktowane jako dosłowne. Kiedy używasz zmiennych w ciągu, ma to sens i doskonale jest stosować podwójne cudzysłowy, ale w przypadku niezmiennych wartości ciągu pojedyncze cudzysłowy są bardziej idealne (ponieważ nie trzeba ich analizować) i nieco szybciej (my ” w większości przypadków mówią o milisekundach).
t31os
@ t31os - Absolutnie poprawne. Nadal wolę 'this is my mood: '.$valueze "this is my mood: $value"względu na czytelność. Jeśli chodzi o szybkość: nie jest to nieznacznie - mierzyłem do pięciu razy. A kiedy wszędzie użyjesz podwójnych cudzysłowów, szybko podsumują się, gdy otrzymasz wiele próśb. W każdym razie dobrze, że to wyjaśniłeś.
kaiser
@ t31os W dyskusji ponownie zmierzyłem prędkość "kontra 'i się myliłem. Różnica daleko wykracza poza wszystko, co ktoś by zauważył.
kaiser
1
+1 fajna funkcja! 2 literówki: taksonomie $ są używane w funkcji $ taksonomia i $ terminy [] = $ c; musi być $ warunki [] = $ t;
Rob Vermeer
8

Napisałem funkcję, która pozwala przechodzić post_typew $argstablicy do get_terms()funkcji:

HT do @braydon za pisanie 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
źródło
7

Świetne pytanie i solidne odpowiedzi.

Naprawdę podobało mi się podejście @jessica z użyciem filtru terms_clauses, ponieważ rozszerza on funkcję get_terms w bardzo rozsądny sposób.

Mój kod jest kontynuacją jej pomysłu, z częścią sql z @braydon w celu zmniejszenia duplikatów. Pozwala także na tablicę typów post_typów:

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

Ponieważ get_terms nie ma klauzuli GROUPY BY, musiałem dodać ją na końcu klauzuli WHERE. Zauważ, że mam bardzo wysoki priorytet filtra, w nadziei, że zawsze będzie ostatni.

sztylet
źródło
3

Nie mogłem zmusić argumentów get_terms do współpracy z powyższą wersją kodu Gavina, ale w końcu zrobiłem to, zmieniając

$terms2 = get_terms( $taxonomy );

do

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

tak jak w oryginalnej funkcji Bainternet.

tzeldin88
źródło
1
Naprawiono to w aktualnej wersji
Gavin Hewitt
0

@Bainternet: Dzięki! Musiałem nieco zmienić funkcję, ponieważ nie działała (niektóre literówki). Jedynym problemem jest teraz to, że liczenie terminów jest wyłączone. Liczba nie bierze pod uwagę typu postu, więc nie sądzę, że można w tym celu użyć get_terms ().

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

EDYCJA: Dodano poprawkę (poprawki). Ale jakoś nadal nie działa dla mnie. Liczba nadal pokazuje niepoprawną wartość.

Gavin Hewitt
źródło
To inna historia, ale można liczyć, unikając duplikatów w pętli while.
Bainternet,
Zaktualizowałem swoją odpowiedź poprawką zliczania terminów.
Bainternet,
1
Nie dodawaj odpowiedzi jako odpowiedzi, chyba że odpowiadasz na własne pytanie , zamiast tego należy dodać oryginalne pytanie.
t31os 10.0411
1
@ t31os: Ach tak, zastanawiałem się, jak dodać dodatek. Nie myślałem o edycji mojego pytania. Dzięki!
Gavin Hewitt
Jak mogę to nazwać? print_r(get_terms_by_post_typea(array('event','category','',array()));ten daje Warning: Invalid argument supplied for foreach()do liniiforeach ($current_terms as $t){
devo
0

Unikaj duplikatów:

//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
źródło
1
Czy możesz wyjaśnić, dlaczego to rozwiązuje problem? Zobacz jak odpowiedzieć .
brasofilo