Получить условия по таксономии И post_type

17

У меня есть 2 пользовательских типа записей «закладки» и «фрагменты» и общий тег таксономии. Я могу создать список всех терминов в таксономии с помощью get_terms (), но я не могу понять, как ограничить список типом записи. В основном я ищу что-то вроде этого:

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

Есть ли способ добиться этого? Идеи очень ценятся!

О, я на WP 3.1.1

Гэвин Хьюитт
источник

Ответы:

11

Вот еще один способ сделать что-то подобное с помощью одного 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;

}
Брейдон
источник
Да! Это именно то, что я хочу.
Гэвин Хьюитт
print_r(get_terms_by_post_type(array('category') , array('event') ));шоуWarning: Missing argument 2 for wpdb::prepare()
devo
Я могу ошибаться, но не думаю, что эти операторы соединения будут работать, т. Е. Они будут работать, только если передаются однозначные массивы. Это связано с тем, что функция prepare будет экранировать все сгенерированные одинарные кавычки и рассматривать каждую целую строку 'join' в одну строку.
Codesmith
14

Так случилось, что мне нужно что-то подобное для проекта, над которым я работаю. Я просто написал запрос, чтобы выбрать все сообщения пользовательского типа, а затем проверяю, какие именно термины моей таксономии они используют.

Затем я получил все условия использования этой таксономии, get_terms()а затем я использовал только те, которые были в обоих списках, обернул их в функцию, и все было готово.

Но тогда мне нужно было больше, чем просто идентификаторы: мне нужны были имена, чтобы я добавил новый аргумент с именем, $fieldsчтобы я мог сказать функции, что возвращать. Затем я решил, что он get_termsпринимает много аргументов, и моя функция была ограничена просто терминами, которые используются типом записи, поэтому я добавил еще одинif утверждение, и все:

Функция:

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

Использование:

Если вам нужен только список идентификаторов терминов, то:

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

Если вам нужен только список имен терминов, то:

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

Если вам нужен только список объектов термина, то:

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

И если вам нужно использовать дополнительные аргументы get_terms, такие как: orderby, order, иерархический ...

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

Наслаждайтесь!

Обновить:

Чтобы привязать количество терминов к конкретному изменению типа сообщения:

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
источник
не лучше ли использовать (array) $argsсписок из 4 $? Это позволит вам не заботиться о порядке, в котором вы добавляете аргументы, что-то вроде, get_terms_by_post_type( $args = array( 'taxonomies', 'args', 'post_type', 'fields' => 'all') )а затем вызывать их внутри функции с помощью $args['taxonomies']. Это поможет вам избежать добавления пустых значений и запоминания порядка ваших аргументов. Я бы также предложил использовать одинарные кавычки вместо двойных. Я видел, как они в пять раз быстрее.
Кайзер
1
@kaiser - строки в двойных кавычках должны быть проанализированы, где значения в одинарных кавычках всегда рассматриваются как литералы. Когда вы используете переменные в строке, это имеет смысл, и совершенно нормально использовать двойные кавычки, но для неизменяемых строковых значений одинарные кавычки являются более идеальными (потому что их не нужно анализировать) и немного быстрее (мы мы говорим о миллисекундах в большинстве случаев).
t31os
@ t31os - Абсолютно верно. Я по- прежнему предпочитаю 'this is my mood: '.$valueболее "this is my mood: $value", из - за удобство чтения. Когда дело доходит до скорости: это не слегка - я измерял до пяти раз. И когда вы везде используете двойные кавычки во всей своей теме, они быстро подведут итог, когда вы получите много запросов. Во всяком случае, хорошо, что вы это ясно дали понять.
Кайзер
@ t31os Вне обсуждения я переоценил скорость "против 'и был неправ. Разница далеко за пределами того, что кто-нибудь заметит.
Кайзер
1
+1 хорошая функция! 2 опечатки: $ таксономии используется в функции $ таксономии и $ term [] = $ c; должно быть $ term [] = $ t;
Роб Вермеер
8

Я написал функцию, которая позволяет вам передать post_typeв$args массив в get_terms()функции:

HT для @ Braydon для написания 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);
Джессика
источник
7

Отличный вопрос и твердые ответы.

Мне действительно понравился подход @jessica, использующий фильтр term_clauses, потому что он расширяет функцию get_terms очень разумным способом.

Мой код является продолжением ее идеи, с некоторыми sql от @braydon, чтобы уменьшить дубликаты. Это также позволяет использовать массив 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);

Поскольку в get_terms нет предложения для GROUPY BY, мне пришлось добавить его в конец предложения WHERE. Обратите внимание, что у меня установлен очень высокий приоритет фильтра, в надежде, что он всегда будет последним.

daggerhart
источник
3

Я не смог заставить аргументы get_terms работать с версией кода Гевина, описанной выше, но в конце концов изменил

$terms2 = get_terms( $taxonomy );

в

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

как это было в оригинальной функции от Bainternet.

tzeldin88
источник
1
Исправлено в текущей версии
Гэвин Хьюитт
0

@Bainternet: Спасибо! Мне пришлось немного изменить функцию, потому что она не работала (некоторые опечатки). Единственная проблема сейчас в том, что срок отсчета выключен. Счет не учитывает тип записи, поэтому я не думаю, что вы можете использовать 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;
    }
}

РЕДАКТИРОВАТЬ: Добавлено исправление (я). Но почему-то это все еще не работает для меня. Счет по-прежнему показывает неправильное значение.

Гэвин Хьюитт
источник
Это другая история, но вы можете рассчитывать, избегая дублирования в цикле while.
Bainternet
Я обновил свой ответ с исправлением подсчета сроков.
Bainternet
1
Пожалуйста, не добавляйте ответы в качестве ответов, если только вы не отвечаете на свой вопрос , вместо этого следует добавить дополнения к исходному вопросу.
t31os
1
@ t31os: Ах да, мне было интересно, как добавить дополнение. Не думал редактировать мой вопрос. Благодарность!
Гэвин Хьюитт
Как я могу это назвать? print_r(get_terms_by_post_typea(array('event','category','',array()));этот дает Warning: Invalid argument supplied for foreach()за линиюforeach ($current_terms as $t){
Devo
0

Избегайте дубликатов:

//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
источник
1
Можете ли вы объяснить, почему это решает проблему? Смотрите как ответить .
brasofilo