Используя wp_query, можно ли заказать таксономию?

49

У меня простой вопрос, я использую WP_Query для извлечения фильтрации сообщений некоторых типов по таксономии с использованием tax_query.

Теперь моя проблема в том, что я хотел бы заказать таксономию, но из документации и поиска в Интернете я не могу найти решение.

Порядок в WP_Query позволяет упорядочивать по множеству полей, даже настраиваемые мета-поля, но кажется, что он не поддерживает таксономию.

Есть ли указатели в правильном направлении?

Спасибо вам всем.

yeope
источник

Ответы:

12

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

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

В такой ситуации вы должны использовать мета-пост. Вы можете заказать по почте мета, и это уникально для каждого сообщения.

Изменить: Тем не менее, вы можете заказать по таксономии, сделав пользовательский запрос SQL с использованием фильтра, вы просто не можете сделать это из неизмененного WP_Query: http://scribu.net/wordpress/sortable-taxonomy-columns.html

Однако, если вам приходится прибегать к подобным вещам, то ваша структура проектирования данных, в первую очередь, неверна. «Термины» в таксономии не являются фактическими «данными». Сами термины не имеют внутреннего значения, они просто обозначают те группы, которые они описывают. Если вы рассматриваете их как значимые данные, то у вас есть основной недостаток дизайна.

Таксономии группируют вещи, назначая им термины. Эта группировка - весь смысл таксономий, термины - это просто симпатичные лица в группе. Если у вас есть значимые метаданные для назначения посту, вам следует вместо этого использовать метаданные поста. И это вы можете упорядочить, потому что post meta использует и ключи, и значения для хранения информации. С таксономией вы на самом деле храните только ключи, а их значения - это посты, сгруппированные по этому термину.

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

эфирное масло
источник
Привет Отто, спасибо за ответ. Я понимаю вашу точку зрения и, возможно, я иду по неверному пути с этим. В моем примере на сайте телешоу у меня есть таксономия для серии 1, серии 2, серии 3 и т. Д. Так что я могу сгруппировать все разные телешоу по номеру серии. Затем я делаю то же самое для эпизодов, эпизодов 01, эпизодов 02 и т. Д. Я хотел бы, чтобы при показе списка всех эпизодов были упорядочены по эпизодам и сериям. Я буду анализировать, а затем опубликовать мета и пользовательские поля. Спасибо, Отто.
yeope
@yeope ваша таксономия должна быть последовательностью, а ваши термины должны быть сериями 1, сериями 2 и т. д. С эпизодами я предполагаю, что серия содержит несколько эпизодов, поэтому она может использовать одну и ту же таксономию, «серию», а если они иерархические, то эпизод 1, эпизод 2 и т. д. будет иметь родительский термин "серия х". Затем вы можете запросить целую серию по порядку с эпизодами, попадающими в очередь, где они должны.
Chris_O
@Chris_O Я вижу, вы можете быть на деньги там! Единственная проблема, которую я вижу, - это необходимость повторять термины «Эпизод 1», «Эпизод 2» для каждой серии. Также не удалось сгруппировать все эпизоды 1 вне зависимости от серии, но я думаю, что есть способ обойти это. Спасибо Chris_O
yeope
2
На самом деле использование таксономии для эпизодов не имеет особого смысла, потому что группирование бесполезно. Подумайте об этом, если у вас есть «эпизод 1» в качестве термина, то вы группируете эпизод 1 с каждым другим эпизодом 1 из любого другого телешоу. Номера эпизодов и серий имеют больше смысла как post_meta, потому что они специфичны для конкретного шоу и бесполезны для группы. Название телешоу было бы полезно как термин в таксономии ТВ-шоу, потому что тогда вы группируете шоу в целом вместе.
Отто
1
Отто последовал интересный пост в блоге: когда (не) использовать пользовательскую таксономию .
Ян Фабри
47

Принятый ответ на этот вопрос неприемлем. Нелогично предполагать, что упорядочение по налогам «не имеет смысла». Ответ, который он дал, не имеет смысла.

Рассмотрите возможность иметь тип сообщения меню. Тогда у вас есть таможенный налог "FoodCategories". Налог FoodCategories имеет условия "Завтрак", "Обед" и "Ужин". Если вы отправляете запрос, используя параметр tax_query, теперь у вас есть набор результатов со всеми терминами, однако они упорядочены по дате публикации.

Чтобы получить из них правильный порядок относительно их терминов, а затем соответствующим образом отобразить их на внешнем интерфейсе, разделив посты по их различным категориям, вы должны пройтись по набору результатов, а затем запросить каждый отдельный пост в набор результатов, чтобы найти его термины и сравнить с текущим термином, отфильтровать в массив и продолжить. Затем вы должны снова перебрать новый массив для отображения. Это не продуктивно.

Было бы неплохо, если бы у WP была опция orderby «tax__in», как и у «post__in», но так как она отсутствует, вам также придется выполнить вышеупомянутый нелепый процесс; самостоятельно настроить запрос с помощью фильтра posts_orderby и posts_join, чтобы настроить метод orderby и добавить термин в результирующий набор соответственно; или вы должны сделать новый запрос для каждого термина, который вы фильтруете в разделах html относительно этих терминов.

Наиболее эффективным было бы изменить строку запроса с помощью фильтров. Самым простым было бы сделать три отдельных запроса. WP API должен обрабатывать упорядочение по налогам или любые ограничивающие параметры запроса. Если вы ограничиваете запрос, основанный на определенных условиях, существует высокая вероятность того, что многим придется заказывать по тем же условиям.

Арий дантли
источник
2
Извините, но вы не правы. Заказ по таксономии также не имеет никакого смысла в вашем случае. Что ты хочешь показать? Сначала все завтраки, потом ужины, потом обеды? Вы должны выбрать то, что вы хотите, и порядок, в котором вы хотите, но таксономия - это просто метка группировки. Это не значимые «данные», которые вы должны упорядочивать. Если это так, тогда это не должен быть термин в таксономии, вместо этого вы должны сделать его пост-мета.
Отто
15
Да ладно, конечно, будут некоторые случаи, когда вы захотите заказать посты по таксономии. Другим примером является тип поста Movie с рейтинговой таксономией. В списке фильмов очень легко представить людей, желающих упорядочить список фильмов по рейтингу, поэтому все фильмы с рейтингом G, затем с рейтингом PG и т. Д. Появляются вверху. (В этом и примере еды они могут быть упорядочены по term_id вместо имени.) Существует большая серая область случаев, когда вам, вероятно, лучше всего подавать таксономию, а не мета, но, вероятно, также полезно, чтобы эта таксономия была упорядоченной -able.
Седьмая Марка
2
Рейтинги PG и G и т. Д. Являются хорошим выбором для таксономии, за исключением того, что они представляют собой данные о конкретных фильмах. Таким образом, они мета. Это данные, а не категории. Наличие ограниченного числа вариантов не делает таксономию. Если это нужно отсортировать по, то либо сделайте его мета, либо форсируйте сортировку по таксономии через специальный код таксономии. Кстати, NC17 идет после PG. Итак, вам нужен код, чтобы сделать это в любом случае.
Отто
Я знаю, что опоздал на вечеринку с этим комментарием, но наткнулся на это. Упорядочение по таксономии может иметь смысл в некоторых ситуациях. У нас есть списки вакансий для одного проекта в виде поста, а затем штат и город, в котором эта работа является таксономией. Мы хотим, чтобы они были легко отображаемыми (показывать все рабочие места в штате или все рабочие места в городе), чтобы таксономия была лучшим решением. В то же время существует общий поиск работы, где мы хотим отсортировать их сначала по названию, затем по штату, а затем по городам.
Деннис Пузак,
Другой вариант использования: у клиента есть группа статей, каждая из которых имеет категорию. Клиент хочет, чтобы была страница со списком всех статей, которые можно отсортировать по алфавиту, по дате или по категории. Категории также могут быть отфильтрованы, но перечисление всех статей по категориям в алфавитном порядке не так уж безумно для варианта использования, и вы видите, что он всплывает довольно часто.
Уилсон Биггс
15

Да, но это довольно сложно ...

Добавьте к functions.php в вашей теме:

function orderby_tax_clauses( $clauses, $wp_query ) {
    global $wpdb;
    $taxonomies = get_taxonomies();
    foreach ($taxonomies as $taxonomy) {
        if ( isset( $wp_query->query['orderby'] ) && $taxonomy == $wp_query->query['orderby'] ) {
            $clauses['join'] .=<<<SQL
LEFT OUTER JOIN {$wpdb->term_relationships} ON {$wpdb->posts}.ID={$wpdb->term_relationships}.object_id
LEFT OUTER JOIN {$wpdb->term_taxonomy} USING (term_taxonomy_id)
LEFT OUTER JOIN {$wpdb->terms} USING (term_id)
SQL;
            $clauses['where'] .= " AND (taxonomy = '{$taxonomy}' OR taxonomy IS NULL)";
            $clauses['groupby'] = "object_id";
            $clauses['orderby'] = "GROUP_CONCAT({$wpdb->terms}.name ORDER BY name ASC) ";
            $clauses['orderby'] .= ( 'ASC' == strtoupper( $wp_query->get('order') ) ) ? 'ASC' : 'DESC';
        }
    }
    return $clauses;
}

    add_filter('posts_clauses', 'orderby_tax_clauses', 10, 2 );

Это Франкенштейн из некоторых найденных вещей и вещей, которые я сделал сам. Объяснить это довольно сложно, но суть этого бега, вы можете поставить? Orderby = (запрос таксономии var) & order = ASC (или DESC), и она сразу начнёт!

Дрю Гурли
источник
Спасибо, Дрю, я попробую и попробую запустить этот SQL, нужно немного отредактировать, но это может сработать. Моя единственная проблема сейчас, это то, что я могу идти в неправильном направлении, как указал Отто. Спасибо, Дрю. РЕДАКТИРОВАТЬ - не нужно редактировать, я вижу, где он нуждается в настройке :) Спасибо
yeope
Если вы схватили его в течение последних двух минут, он не сработает, продолжайте и хватайте сейчас, я исправил это. Он был установлен для двух конкретных таксономий, я улучшил код для работы со всеми зарегистрированными таксономиями.
Дрю Гурли
Спасибо еще раз. На всякий случай, я попробовал ваше решение, и оно вроде как работает. Также, если кто-то еще хочет использовать его, вы должны изменить add_filter('posts_clauses', 'orderby_tax_clauses', 10, 2 );на add_filter('posts_clauses', 'todo_tax_clauses', 10, 2 );Спасибо :)
yeope
Да, это теперь исправлено в блоке кода, я взял это из проекта, над которым я работаю, и забыл изменить имя функции, даже если я изменил ее в хуке.
Дрю Гурли
1
Знаете ли вы, можно ли заказать таксономии по идентификатору вместо названия? Я пытаюсь получить тот же результат, упорядочивая группы таксономии по ID
Хавьер Вильянуева
9

Я опаздываю на игру, но есть более простой способ сделать это на WordPress.

Создайте свой налоговый запрос, как обычно.

$tax_query = array();
$tax_query['relation']="OR";
$tax_query[] = array(
    'taxonomy' => 'product_cat',
    'field'    => 'slug',
    'terms'    => $cat_terms,
);
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;

Настройте свои аргументы для query_posts или WP_Query

$args = array(
    'post_type'=>'post',
    'posts_per_page'=>12,
    'paged'=>$paged,
    'tax_query' => $tax_query,
);

Перед тем, как сделать запрос query_posts / WP_Query, подключитесь к фильтру orderby и переопределите его.

add_filter('posts_orderby', 'edit_posts_orderby');
function edit_posts_orderby($orderby_statement) {
    $orderby_statement = " term_taxonomy_id ASC ";
    return $orderby_statement;
}
query_posts($args);
remove_filter('posts_orderby', 'edit_posts_orderby');

не забудьте потом удалить фильтр ...

это работает, потому что tax_query создает объединения и т.д. для вас, вам просто нужно упорядочить по одному из полей из объединения.

Фрэнсис Якониелло
источник
2
Любая идея о том, как заказать по имени вместо term_taxonomy_id? изменение term_taxonomy_id в orderby_statement выдает ошибки
tehlivi
Это правильный ответ для всех, кто заинтересован!
Майра М
2

Ну, я бы хотел поделиться своим опытом сортировки пользовательских типов постов по категориям / таксономии.

ПАУТИНА

  1. Сайт туристического агентства, работающий на WordPress
  2. Основное содержание в пользовательском типе поста называется 'ruta'
  3. Таксономия с этой структурой Тип путешествия> континент> страна

ДЕЛО

На страницах списка категорий архива клиент хотел сортировать сообщения по

  1. Континент, упорядоченный по количеству маршрутов на каждом.
  2. Страна, упорядоченная по алфавиту.

ШАГИ

Во-первых , я ловлю запрос от неизмененного запроса к странице архива, который оказался следующим:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID 
FROM wp_posts 
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
WHERE 1=1 
AND ( wp_term_relationships.term_taxonomy_id IN (5,6,7,8,9,10,11,12,13,15,16,17,18,19,20,21,22,23,25,26,28,29,31,32,33,35,38,95,101,102,193) )
AND wp_posts.post_type IN ('ruta', 'nav_menu_item') 
AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 45 
AND wp_posts.post_status = 'private') 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC LIMIT 0, 20

Во-вторых , я отредактировал SQL-код в Sequel Pro для базы данных, чтобы он соответствовал моим потребностям. Я выступаю с этим (да, возможно, это можно улучшить: мои знания по MySQL не выдаются):

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID, tt1.parent AS pare,
    (
    SELECT COUNT(*) 
    FROM  wp_posts
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
    INNER JOIN wp_term_taxonomy AS tt1 ON ( tt1.term_taxonomy_id =      wp_term_relationships.term_taxonomy_id )
    INNER JOIN wp_term_taxonomy AS tt2 ON ( tt2.term_taxonomy_id =  tt1.term_taxonomy_id )
    WHERE 1=1  
    AND tt1.parent = pare
    ) AS Total
FROM  wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_term_taxonomy AS tt1 ON ( tt1.term_taxonomy_id =      wp_term_relationships.term_taxonomy_id )
INNER JOIN wp_terms ON ( tt1.term_id = wp_terms.term_id )
WHERE 1=1  
AND ( wp_term_relationships.term_taxonomy_id IN (5,6,7,8,9,10,11,12,13,15,16,17,18,19,20,21,22,23,25,26,28,29,31,32,33,35,38,95,101,102,193) ) 
AND wp_posts.post_type IN ('ruta', 'nav_menu_item') 
AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 45 
AND wp_posts.post_status = 'private') 
GROUP BY wp_posts.ID 
ORDER BY
total DESC,
wp_terms.name  

В-третьих , я подключил запрос к файлу functions.php тремя фильтрами: posts_fields, posts_join и posts_orderby

Код в functions.php:

function xc_query_fields( $fields ) {

   $fields = "wp_posts.ID, wp_posts.post_title, wp_terms.name, tt1.parent AS pare,
    (
    SELECT COUNT(*) 
    FROM  wp_posts
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
    INNER JOIN wp_term_taxonomy AS tt1 ON ( tt1.term_taxonomy_id = wp_term_relationships.term_taxonomy_id )
    INNER JOIN wp_term_taxonomy AS tt2 ON ( tt2.term_taxonomy_id = tt1.term_taxonomy_id )
    WHERE 1=1  
    AND tt1.parent = pare
    )
    AS Total";
     return $fields;
}


function xc_query_joins( $join ) {
$join .= "INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
   INNER JOIN wp_term_taxonomy AS tt1 ON ( tt1.term_taxonomy_id = wp_term_relationships.term_taxonomy_id )
   INNER JOIN wp_terms ON ( tt1.term_id = wp_terms.term_id )";
 return $join;
}


function xc_query_orderby( $join ) {
    $join = "total DESC, wp_terms.name ";
    return $join;
 }

Наконец, я включил фильтры из ловушки pre_get_post в соответствии с некоторыми условиями

function filtra_queries( $query )
{

  if (  is_archive()  && $query->is_main_query() && !is_admin()  ) {

$rutes = array('viajes-privados', 'asia', 'africa', 'oceania', 'america', 'oriente-proximo');

if  ( in_array( $query->get('category_name'), $rutes ) ) 
  {
  add_filter( 'posts_fields', 'xc_query_fields' );
  add_filter( 'posts_join', 'xc_query_joins' );
  add_filter( 'posts_orderby', 'xc_query_orderby' );
}// end if in_array

  }// end if is_archive

}
 add_filter('pre_get_posts', 'filtra_queries');

Надеюсь, что это может помочь кому-то

Ксавье Калис
источник
Хорошая работа, нелепая, это то, что мы взяли это количество кода для сортировки по таксономии. Огромная проблема с WP.
serraosays
2

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

ПРОБЛЕМЫ:

1) Wordpress не позволяет вам заказывать таксономии любым разумным способом.

2) Wordpress просто не позволяет orderbyиспользовать таксономии в пост-типе WP_Query (как изложено Отто).

РЕШЕНИЯ:

1) На данный момент сортировка таксономий лучше всего выполняется с помощью плагина NE пользовательских таксономических заказов. Это позволяет вам заказать таксономию через WYSIWYG, wp-adminчто не совсем так, как я бы это сделал, но я не нашел ничего лучшего.

Когда вы установите плагин, вы получите нечто похожее на то, что я сделал здесь. Запишите эту опцию Auto-sort Queries of this Taxonomy- установите для нее значение Custom Order as Defined Above; это дает вам заказ, который вам нужен. Скриншот:

Пользовательский таксономический заказ NE дисплей

2) Имея отсортированную таксономию, вы можете теперь создавать серию вызовов WP_Query, которые выполняются через каждый термин, эффективно создавая архив, упорядоченный таксономией. Используйте get_terms()для создания массива всех налоговых терминов, а затем выполните foreachнад каждым термином. Это создает WP_Queryдля каждого элемента термина, который будет возвращать все сообщения за данный термин, эффективно создавая архив, упорядоченный по термину таксономии. Код для этого:

  // Get your terms and put them into an array
  $issue_terms = get_terms([
    'taxonomy' => 'issues',
    'hide_empty' => false,
  ]);

  // Run foreach over each term to setup query and display for posts
  foreach ($issue_terms as $issue_term) {
    $the_query = new WP_Query( array(
      'post_type' => 'post',
      'tax_query' => array(
        array(
          'taxonomy' => 'issues',
          'field' => 'slug',
          'terms' => array( $issue_term->slug ),
          'operator' => 'IN'
        )
      )
    ) );

    // Run loop over each query
    while($the_query->have_posts()) :
      $the_query->the_post();

      // YOUR TEMPLATE OUTPUT FOR EACH POST

    endwhile;
  }

Связанное чтение на этом сайте: Показать все сообщения в пользовательском типе сообщения, сгруппированные по пользовательской таксономии

serraosays
источник
2

Я не уверен, почему все решения здесь в значительной степени превосходят его. Хорошо, это было полвека назад, но я сейчас запускаю следующий код, и он работает:

   <?php // Default
    $wheels_args = array(
        'post_type' => 'wheels',
        'posts_per_page' => '96',
        'orderby' => 'taxonomy, name', // Just enter 2 parameters here, seprated by comma
        'order'=>'ASC'
    );
    $loop = new WP_Query($wheels_args);
    ?>

Это позволит сначала отсортировать таксономии вашего CPT по его таксономии в алфавитном порядке, а внутри этих групп таксономий - по алфавиту.

user3135691
источник
@yeope Почему это принятый ответ? слава богу, что прокрутил
Хуан Солано
1

Вот решение, которое я использовал для этой конкретной проблемы. Это решение предназначено для крайних случаев, когда невозможно использовать pre_get_postsфильтр, и в запросе существует разбиение на страницы (например, WooCommerce):

global $wpdb;

$taxonomies = array('my-tax-1', 'my-tax-2', 'my-tax-3');

$orderby = "'".implode("', '", array_keys($taxonomies))."'";
$id_sql = $GLOBALS['wp_query']->request;

$id_sql = preg_replace('/LIMIT\s+\d+\s?,?\s\d*/', '', $id_sql);
$id_sql = str_replace('SQL_CALC_FOUND_ROWS', '', $id_sql);

$term_sql = "SELECT
  tt.taxonomy AS `taxonomy`,
  t.name AS `term_name`,
  t.slug AS `term_slug`,
  count(*) AS `term_count`
FROM ({$id_sql}) p 
JOIN wp_term_relationships tr
  ON p.ID = tr.object_id
JOIN wp_term_taxonomy tt
  ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms t
  ON tt.term_id = t.term_id
WHERE tt.taxonomy IN ({$orderby})
GROUP BY t.slug
ORDER BY
  FIELD(tt.taxonomy, {$orderby})"; // Add further specific ordering here

$results = $wpdb->get_results($term_sql, ARRAY_A);

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

Если вы просто хотите сообщения, измените запрос на SELECT p.*иGROUP BY p.ID

CodeShaman
источник
0

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

function grouped_by_taxonomy_main_query( $query ) {

    if ( $query->is_home() && $query->is_main_query() ) { // Run only on the homepage

        $post_ids = array();

        $terms = get_terms('my_custom_taxonomy');

        foreach ( $terms as $term ) {
            $post_ids = array_merge( $post_ids, get_posts( array( 
                'posts_per_page' => 4, // as you wish...
                'post_type' => 'my_custom_post_type', // If needed... Default is posts
                'fields' => 'ids', // we only want the ids to use later in 'post__in'
                'tax_query' => array( array( 'taxonomy' => $term->taxonomy, 'field' => 'term_id', 'terms' => $term->term_id, )))) // getting posts in the current term
            );
        }

        $query->query_vars['post_type'] = 'my_custom_post_type'; // Again, if needed... Default is posts
        $query->query_vars['posts_per_page'] = 16; // If needed...
        $query->query_vars['post__in'] = $post_ids; // Filtering with the post ids we've obtained above
        $query->query_vars['orderby'] = 'post__in'; // Here we keep the order we generated in the terms loop
        $query->query_vars['ignore_sticky_posts'] = 1; // If you dont want your sticky posts to change the order

    }
}

// Hook my above function to the pre_get_posts action
add_action( 'pre_get_posts', 'grouped_by_taxonomy_main_query' );
Марсело Виана
источник