get_terms по пользовательскому типу записи

19

У меня есть два пользовательских типа постов 'страна' и 'город' и общий флаг таксономии.

Если я использую:

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

Я получаю список всех терминов в таксономии, но я хочу ограничить список типом поста "страна".

Как мне это сделать?


Используя новое решение

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

Я не могу повторить $ childTerm-> name. Почему?

user1443216
источник
Не могли бы вы быть немного яснее?
TheDeadMedic

Ответы:

16

Боюсь, это не возможно изначально (пока?). Смотрите этот трек: http://core.trac.wordpress.org/ticket/18106

Аналогично на странице администратора таксономии количество сообщений отражает все типы сообщений. ( Я почти уверен, что для этого тоже есть трейк-билет ) http://core.trac.wordpress.org/ticket/14084

Смотрите также этот пост .


Новое решение

Написав приведенный ниже, я выпустил гораздо лучший способ (хотя и в том смысле, что вы можете сделать больше) - использовать фильтры, предоставляемые в get_terms()вызове. Вы можете создать функцию-оболочку, которая использует get_termsи (условно) добавляет фильтр для управления запросом SQL (для ограничения по типу записи).

Функция принимает те же аргументы, что и get_terms($taxonomies, $args). $argsпринимает дополнительный аргумент post_typesкоторого принимает массив | строку типов записей.

Но я не могу гарантировать, что все работает "как положено" (я думаю, что набиваю счет). Похоже, что он работает, используя просто по умолчанию $argsдля 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);           
}

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

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

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

Оригинальное решение

Вдохновленный вышеупомянутым билетом trac, (проверено, и это работает для меня)

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

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

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

или

 $terms = wpse57444_filter_terms_by_cpt('flag','country');
Стивен Харрис
источник
Это работает, но что я могу сделать со своими $ args? Я имею в виду ... parent = 0 & orderby = name & hide_empty = 0
user1443216
нет - это должно быть массивом: $args = array('parent'=>0,'orderby'=>'name','hide_empty'=>0);. Я отредактирую это, чтобы разрешить строки запроса ...
Стивен Харрис
Где я могу положить мои $ args в этом примере $terms = wpse57444_filter_terms_by_cpt('flag',array('country','city'));:?
user1443216
Вы не можете в этом, только в новом решении:wpse57444_get_terms()
Стивен Харрис
@ user1443216 $args- второй аргумент. Там вы просто положилиwpse57444_get_terms( 'flag', array( 'country', 'city' ) );
Кайзер
2

Ответ Стивена-Харриса выше только работал для меня частично. Если я попытался использовать его дважды на странице, это не сработало. Кроме того, меня беспокоит мысль о том, чтобы скрывать подобные запросы mysql - я думаю, что лучше использовать основные методы для достижения решения, чтобы избежать конфликтов с будущими обновлениями WP. Вот мое решение, основанное на некотором комментарии № 7 о билете Trac, на который он ссылается

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

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

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

Это работает только для одного типа записи и одной таксономии, потому что это то, что мне было нужно, но было бы не сложно изменить это так, чтобы оно принимало несколько значений.

На этой ветке Trac было упомянуто, что это может плохо масштабироваться, но я работаю в довольно небольшом масштабе и у меня не было проблем со скоростью.

Марк Прус
источник
это решение выглядит более «родным» для меня - в любом случае -> вы должны вызывать «wp_reset_postdata ()» сразу после «конца» цикла: wordpress.stackexchange.com/questions/144343/…
Томас Феллингер
2

Два пользовательских типа постов «страна» и «город» и общая таксономия «флаг». Вы хотите ограничить список типом поста "страна".

Вот более простое решение:

$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
источник
1

[править] Это комментарий к отличному ответу Стивена Харриса.

Он не возвращает никаких терминов, если используется с несколькими типами записей, как это $flags = wpse57444_get_terms('flags', array('post_types' => array('country','city')));. Это связано с тем, что $ wpdb-> prepare очищает строку $ post_types_str до тех p.post_type IN('country,city')пор, пока она должна быть p.post_type IN('country','city'). Смотрите этот билет: 11102 . Используйте решение из этой темы, чтобы обойти это: /programming//a/10634225

keesiemeijer
источник
1

Я также пытался использовать ответ @Stephen Harris, но запрос, который мне был нужен, было довольно сложно написать в виде отдельного запроса с использованием частей фильтра.

Кроме того, мне также нужно было использовать эту функцию несколько раз на одной странице, и я решил проблему с объявлением wpse_filter_terms_by_cptфункции вне функции-оболочки.

В любом случае, ответ @Mark Pruce, по моему мнению, подходит лучше по тем же причинам, которые он сказал, даже если вам нужно сделать еще один запрос (и связанный цикл), чтобы подготовить аргументы для wp_get_object_termsфункции.

Sgaddo
источник