Упорядочивание записей по (иерархическим пользовательским) терминам таксономии и дочерним терминам


Сценарий

  • пользовательский тип записи wiki
  • (иерархическая) пользовательская таксономия topics
  • шаблон страницы archive-wiki.php

Ситуация

Сообщения отображаются и упорядочиваются по post_date (по умолчанию).

Соответствующий основной запрос:

SELECT SQL_CALC_FOUND_ROWS {$wpdb->prefix}posts.ID 
    FROM {$wpdb->prefix}posts WHERE 1=1 
    AND {$wpdb->prefix}posts.post_type = 'wiki' 
    AND ({$wpdb->prefix}posts.post_status = 'publish' OR {$wpdb->prefix}posts.post_status = 'private') 
ORDER BY {$wpdb->prefix}posts.post_date 
DESC 
LIMIT 0, {$setting->posts_per_page}

Да, я вошел в систему при получении этого запроса - следовательно, статус += private.

Задача

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

Где это должно произойти?

Внутри pre_get_posts или posts_pieces (и аналогичных фильтров). Я не хочу добавлять дополнительный запрос для этого.

Спасибо.

Author: kaiser, 2012-10-15

1 answers

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

Что это делает, так это возвращает записи в правильном дочернем порядке в родительском порядке. Чтобы создать соответствующие заголовки родительского уровня, вам придется сравнить родительский таксон текущей публикации с таксоном предыдущей публикации. Выведите заголовок таксона в верхней части страницы и еще один уровень в каждой точке, где изменяется значение родительского таксона. Вы будете нужно вызывать функцию для каждой записи, чтобы получить таксон родительского уровня, так как я не знаю никакого способа передать значение из запроса в объект post.

 SELECT *
   from wp_posts 
  LEFT JOIN wp_term_relationships ON wp_posts.ID = wp_term_relationships.object_id
    LEFT JOIN wp_term_taxonomy ON (wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id)
    LEFT JOIN wp_terms ON (wp_term_taxonomy.term_id = wp_terms.term_id)
    LEFT JOIN wp_term_taxonomy AS parent ON (wp_term_taxonomy.parent = parent.term_taxonomy_id)
    LEFT JOIN wp_terms AS parent_terms ON (parent.term_id = parent_terms.term_id) 
  WHERE wp_posts.post_type = 'post'
  AND wp_term_taxonomy.taxonomy = 'category'
  and not exists(select 1 
                   from wp_posts AS subposts
                        LEFT JOIN wp_term_relationships as subtr ON subposts.ID = subtr.object_id
                        LEFT JOIN wp_term_taxonomy as subtt ON (subtr.term_taxonomy_id = subtt.term_taxonomy_id)
                        LEFT JOIN wp_terms as subt ON (subtt.term_id = subt.term_id)
                        LEFT JOIN wp_term_taxonomy AS subparent ON (subtt.parent = subparent.term_taxonomy_id)
                  where subtt.parent > wp_term_taxonomy.parent
                    and subposts.ID = wp_posts.ID 
                    AND subtt.taxonomy = wp_term_taxonomy.taxonomy)
  ORDER BY IFNULL(  parent_terms.slug ,  wp_terms.slug) ASC, wp_terms.slug  ASC;

Учитывая, что это правильный SQL для вашей страницы архива, давайте рассмотрим, как реализовать использование фильтров, таких как pre_get_posts и т.д.

ОБНОВЛЕНИЕ Приведенный выше SQL-запрос был протестирован и изменен для получения правильных результатов. Следующее, аналогичное моему исходному запросу, возвращает две строки для любой записи, которая имеет родитель.

   SELECT wp_posts.ID,  parent_terms.slug parent_slug, wp_terms.slug, wp_term_taxonomy.taxonomy, wp_term_taxonomy.parent
         , IFNULL(  parent_terms.slug ,  wp_terms.slug) sort_col2
   from wp_posts 
   LEFT JOIN wp_term_relationships ON wp_posts.ID = wp_term_relationships.object_id
    LEFT JOIN wp_term_taxonomy ON (wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id)
    LEFT JOIN wp_terms ON (wp_term_taxonomy.term_id = wp_terms.term_id)
    LEFT JOIN wp_term_taxonomy AS parent ON (wp_term_taxonomy.parent = parent.term_taxonomy_id)
    LEFT JOIN wp_terms AS parent_terms ON (parent.term_id = parent_terms.term_id) 
  WHERE wp_posts.post_type = 'post'
  AND wp_term_taxonomy.taxonomy = 'category'
  ORDER BY IFNULL(  parent_terms.slug ,  wp_terms.slug) ASC, wp_terms.slug  ASC;

Обратите внимание, что post.id =629 появляется дважды в результатах:

  +-----+-------------+---------------+----------+--------+-----------+
  | ID  | parent_slug | slug          | taxonomy | parent | sort_col2 |
  +-----+-------------+---------------+----------+--------+-----------+
  | 629 |             | business      | category | 0      | business  |
  | 629 | business    | press-release | category | 3      | business  |
  | 618 |             | media         | category | 0      | media     |
  | 608 |             | media         | category | 0      | media     |
  | 624 |             | startups      | category | 0      | startups  |
  | 621 |             | startups      | category | 0      | startups  |
  +-----+-------------+---------------+----------+--------+-----------+
  6 rows in set (0.00 sec)

Повторяющиеся строки отфильтровываются путем добавления условия NOT EXISTS(...):

  SELECT wp_posts.ID,  parent_terms.slug parent_slug, wp_terms.slug, wp_term_taxonomy.taxonomy, wp_term_taxonomy.parent
         , IFNULL(  parent_terms.slug ,  wp_terms.slug) sort_col2
   from wp_posts 
  LEFT JOIN wp_term_relationships ON wp_posts.ID = wp_term_relationships.object_id
    LEFT JOIN wp_term_taxonomy ON (wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id)
    LEFT JOIN wp_terms ON (wp_term_taxonomy.term_id = wp_terms.term_id)
    LEFT JOIN wp_term_taxonomy AS parent ON (wp_term_taxonomy.parent = parent.term_taxonomy_id)
    LEFT JOIN wp_terms AS parent_terms ON (parent.term_id = parent_terms.term_id) 
  WHERE wp_posts.post_type = 'post'
  AND wp_term_taxonomy.taxonomy = 'category'
  and not exists(select 1 
                   from wp_posts AS subposts
                        LEFT JOIN wp_term_relationships as subtr ON subposts.ID = subtr.object_id
                        LEFT JOIN wp_term_taxonomy as subtt ON (subtr.term_taxonomy_id = subtt.term_taxonomy_id)
                        LEFT JOIN wp_terms as subt ON (subtt.term_id = subt.term_id)
                        LEFT JOIN wp_term_taxonomy AS subparent ON (subtt.parent = subparent.term_taxonomy_id)
                  where subtt.parent > wp_term_taxonomy.parent
                    and subposts.ID = wp_posts.ID )
  ORDER BY IFNULL(  parent_terms.slug ,  wp_terms.slug) ASC, wp_terms.slug  ASC;

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

  +-----+-------------+---------------+----------+--------+-----------+
  | ID  | parent_slug | slug          | taxonomy | parent | sort_col2 |
  +-----+-------------+---------------+----------+--------+-----------+
  | 629 | business    | press-release | category | 3      | business  |
  | 618 |             | media         | category | 0      | media     |
  | 608 |             | media         | category | 0      | media     |
  | 624 |             | startups      | category | 0      | startups  |
  | 621 |             | startups      | category | 0      | startups  |
  +-----+-------------+---------------+----------+--------+-----------+
  5 rows in set (0.00 sec)

ОБНОВЛЕНИЕ: Функция НЕ соответствует последней версии SQL выше

function wpse69290_query( $pieces, $obj )
{
    global $wpdb;

    #$pieces['fields'] = "* ";

    $pieces['join'] .= " LEFT JOIN `$wpdb->term_relationships` AS trs ON ($wpdb->posts.ID = trs.object_id)";
    $pieces['join'] .= " LEFT JOIN `$wpdb->term_taxonomy` AS tt ON (trs.term_taxonomy_id = tt.term_taxonomy_id)";
    $pieces['join'] .= " LEFT JOIN `$wpdb->terms` AS t ON (tt.term_id = t.term_id)";
    $pieces['join'] .= " LEFT JOIN `$wpdb->term_taxonomy` AS parent ON (parent.parent = trs.term_taxonomy_id)";
    $pieces['join'] .= " LEFT JOIN `$wpdb->terms` AS parent_terms ON (parent.term_id = parent_terms.term_id)";

    $pieces['where'] .= " AND (tt.taxonomy = 'topics')";

    $pieces['orderby'] = "IFNULL(parent_terms.slug, t.slug) ASC";

    $pieces['limits'] = "LIMIT 0, 999";

    return $pieces;
}
add_filter( 'posts_clauses', 'wpse69290_query', 10, 2 );

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

 3
Author: marfarma, 2012-10-16 18:39:42