Получить все категории и записи в этих категориях

24 февр. 2012 г., 23:23:38
Просмотры: 60K
Голосов: 7

Я ищу решение, которое позволит мне вывести следующее:

Кат 1        Кат 2        Кат 3
 Запись 1     Запись 1     Запись 1
 Запись 2     Запись 2     Запись 2
 Запись 3                  Запись 3
                          Запись 4

РЕДАКТИРОВАНО

Я ищу решение, которое будет требовать только один запрос к базе данных! Поэтому, если в вашем коде есть foreach, за которым следует new WP_Query, то это не то, что мне нужно (я планирую разместить это на главной странице своего сайта).

3
Комментарии

минусую, так как ваше требование превращает этот вопрос о WordPress в проблему сортировки на php.

Michael Michael
25 февр. 2012 г. 13:29:37

Пожалуйста, посмотрите мой ответ. Очень быстро и эффективно. Смотрите статистику, которую я добавил в ответе

Pieter Goosen Pieter Goosen
7 сент. 2014 г. 16:51:40

Как насчет этого? <?php $erer = wp_list_categories('orderby=name&title_li=&show_count=1'); var_dump($erer); ?> Затем вы сможете понять, как использовать HTML и CSS.

Vishwa Vishwa
16 окт. 2018 г. 07:14:33
Все ответы на вопрос 8
3
20

ПЕРЕСМОТР №2

Я никогда не работал с Transient API, пока сегодня не увидел ответ @MikeSchinkel в этом посте. Это вдохновило меня снова пересмотреть данный пост. После некоторых тестов я пришел к следующему:

  • Время выполнения сократилось с ~0.07 секунд до ~0.002 секунд

  • Время запросов к базе данных уменьшилось примерно вдвое

  • С использованием транзиентов выполняется только 2 запроса к БД

Как работает код (обсуждаю только изменения по сравнению с оригинальным кодом из ПЕРЕСМОТРА):

ШАГ 1

Нам нужно сохранить значение $q в транзиент — это значение содержит список категорий с заголовками постов.

ШАГ 2

Сначала проверяем, существует ли транзиент. Если его нет — создаем. Если существует — извлекаем его данные.

ШАГ 3

Эти данные передаются в цикл foreach для вывода списка с названиями категорий и заголовками постов.

ШАГ 4

В текущем виде транзиент будет обновляться каждые 12 часов. Этот интервал можно изменить под свои нужды. Однако транзиент нужно удалять и создавать заново при каждом изменении статуса поста (например, черновик → опубликовано, новый пост или удаление поста). Для этого используется delete_transient, подключенный к хуку transition_post_status, который срабатывает при каждом изменении статуса поста.

Полный код:

В файле functions.php

add_action( 'transition_post_status', 'publish_new_post', 10, 3 );

function publish_new_post() {
   delete_transient( 'category_list' );
}

В шаблоне, где нужно вывести список

<?php
if ( false === ( $q = get_transient( 'category_list' ) ) ) {

    $args = array( 
        'posts_per_page' => -1
    );

    $query = new WP_Query($args); 

    $q = array();

    while ( $query->have_posts() ) { 

        $query->the_post(); 

        $a = '<a href="'. get_permalink() .'">' . get_the_title() .'</a>';

        $categories = get_the_category();

        foreach ( $categories as $key=>$category ) {

            $b = '<a href="' . get_category_link( $category ) . '">' . $category->name . '</a>';    

        }

        $q[$b][] = $a; // Создаем массив с названиями категорий и заголовками постов
    }


    /* Восстанавливаем оригинальные данные поста */
    wp_reset_postdata();

set_transient( 'category_list', $q, 12 * HOUR_IN_SECONDS );
}

foreach ($q as $key=>$values) {
        echo $key;

        echo '<ul>';
            foreach ($values as $value){
                echo '<li>' . $value . '</li>';
            }
        echo '</ul>';
    }


?>

ПЕРЕСМОТР

Недавно я разработал очень легковесное решение, которое работает значительно быстрее других возможных вариантов. На тестовом сайте общее время генерации составляет всего ~0.07 секунд и всего 6 запросов к БД (согласно Query Monitor), тогда как другие методы дают время генерации ~0.35 секунд и 50 дополнительных запросов.

Разберем мой метод:

ШАГ 1

Сначала создаем кастомный запрос с помощью WP_Query для получения всех опубликованных постов

$args = array( 
        'posts_per_page' => -1
    );

    $query = new WP_Query($args);   
    $q = array();

    while ( $query->have_posts() ) { 

    }

    /* Восстанавливаем оригинальные данные поста */
    wp_reset_postdata();

ШАГ 2

Используя get_the_category, получаем список всех категорий поста.

$categories = get_the_category();

        foreach ( $categories as $key=>$category ) {

            $b = '<a href="' . get_category_link( $category ) . '">' . $category->name . '</a>';    

        }

ШАГ 3

Присваиваем переменные заголовку поста и его категориям

$a = '<a href="'. get_permalink() .'">' . get_the_title() .'</a>';

и

$b = '<a href="' . get_category_link( $category ) . '">' . $category->name . '</a>';    

ШАГ 4

Объединяем эти переменные в многомерный массив

$q[$b][] = $a;

Чтобы увидеть содержимое массива, используйте var_dump

?><pre><?php var_dump($q); ?></pre><?php

ШАГ 5

С помощью циклов foreach создаем список постов, отсортированный по категориям

foreach ($q as $key=>$values) {
    echo $key;

    echo '<ul>';
        foreach ($values as $value){
            echo '<li>' . $value . '</li>';
        }
    echo '</ul>';
}

ВСЁ ВМЕСТЕ!

Полный код:

<?php

    $args = array( 
        'posts_per_page' => -1
    );

    $query = new WP_Query($args);   
    $q = array();

    while ( $query->have_posts() ) { 

        $query->the_post(); 

        $a = '<a href="'. get_permalink() .'">' . get_the_title() .'</a>';

        $categories = get_the_category();

        foreach ( $categories as $key=>$category ) {

            $b = '<a href="' . get_category_link( $category ) . '">' . $category->name . '</a>';    

        }

        $q[$b][] = $a; // Создаем массив с названиями категорий и заголовками постов
    }

    /* Восстанавливаем оригинальные данные поста */
    wp_reset_postdata();

    foreach ($q as $key=>$values) {
        echo $key;

        echo '<ul>';
            foreach ($values as $value){
                echo '<li>' . $value . '</li>';
            }
        echo '</ul>';
    }

?>
29 мая 2014 г. 21:09:05
Комментарии

почему другие категории дублируются?

winresh24 winresh24
15 мар. 2020 г. 05:57:51

Как это сделать с пользовательским типом записи?

winresh24 winresh24
15 мар. 2020 г. 06:55:26

@Pieter-Goosen Отличный код! Вместо использования <ul> я хочу использовать шаблон из файла. Я попробовал подключить файл в цикле `foreach ($values as $value) {include(template.php);}`, но тогда в качестве заголовка страницы устанавливаются названия записей, а не их заголовки. Есть идеи как это исправить?

harvey harvey
19 апр. 2020 г. 10:06:55
0

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

// Transients API для всех категорий и всех постов
$query_categories = get_transient('cached_categories');
if ( false === $query_categories){
    $args_cat = array(
        // сортировка по имени категории по возрастанию
        'orderby' => 'name',
        'order' => 'ASC',
        // получаем только родительские категории
        'parent' => 0
    );
    // Вместо кэширования WP_Query кэшируем результат 'get_categories()'.
    $query_categories = get_categories($args_cat);
    // var_dump($query_categories);
    set_transient('cached_categories', $query_categories, DAY_IN_SECONDS );
}

// Полный запрос постов
// если есть категории, содержащие посты
if (!empty ($query_categories) && !is_wp_error( $query_categories )) {

    foreach ($query_categories as $category) {

        // var_dump($category);
        $query_category_posts = get_transient('cached_posts_' . $category->slug );
        if ( false === $query_category_posts ){

            // Запрос всех постов по slug внутри каждой категории
            $args_category_posts = array(
                'post_type' => 'post',
                // slug и имя категории получаем из цикла по всем категориям
                'category_name' => $category->slug
            );

            // Здесь кэшируем WP_Query, это выполняется для всех категорий.
            // Для этого используется '$category->slug', чтобы передавать строку, а не объект.
            $query_category_posts = new WP_Query($args_category_posts);         
            set_transient( 'cached_posts_' . $category->slug , $query_category_posts, DAY_IN_SECONDS );
        }

        if ($query_category_posts->have_posts()) {
            while ($query_category_posts->have_posts()) {
                $query_category_posts->the_post(); ?>
                <article class="<?php echo $category->slug ?>-article">
                    <h2 class="<?php echo $category->slug ?>-article-title">
                        <a href="<?php echo get_permalink() ?>"><?php echo get_the_title() ?></a>
                    </h2>
                    <p class="<?php echo $category->slug ?>-post-info">
                        <?php the_time('d. m. Y') ?>
                    </p>
                    <div <?php post_class() ?> >
                        <?php the_content(); ?>
                    </div>
                </article> <?php
            }
        } // конец цикла
    } // конец foreach
wp_reset_postdata() ;
} // конец условия, если есть категории с постами
25 июн. 2017 г. 00:00:39
0

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

Мой пример основан на пользовательской таксономии категории document_category и пользовательском типе записей documents. Но я уверен, что вы уловите суть.

$result = [];

$categories = get_terms( [
    'taxonomy' => 'document_category'
] );

foreach ( $categories as $index => $category ) {
    $args      = [
        'post_type'      => 'documents',
        'posts_per_page' => - 1,
        'public'         => true,
        'tax_query'      => [
            [
                'taxonomy'         => 'document_category',
                'field'            => 'term_id',
                'terms'            => $category->term_id,
                'include_children' => true
            ]
        ]
    ];
    $documents = get_posts( $args );

    $result[ $index ]['category']  = $category;
    $result[ $index ]['documents'] = $documents;
}

return $result;
7 апр. 2020 г. 15:12:08
0

Попробуйте этот код сейчас

$cat_ids=array();
foreach (get_categories() as $cat)
{
    array_push($cat_ids, $cat->cat_ID);
}
$the_query = new WP_Query(array('post_type'=>'post', array('category__and' => $cat_ids) ) );
if( $the_query->have_posts() ): 
    while ( $the_query->have_posts() ) : $the_query->the_post(); 
        echo the_title();
    endwhile;
endif; 
wp_reset_query();
10 июл. 2018 г. 10:50:31
7
-1

Вы можете использовать этот код... Укажите нужное количество записей...

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

<?php  
    $allcats = get_categories('child_of=0'); 

    foreach ($allcats as $cat) :
        $args = array(
            'posts_per_page' => 3, // укажите количество записей на категорию здесь
            'category__in' => array($cat->term_id)
        );

        $customInCatQuery = new WP_Query($args);

        if ($customInCatQuery->have_posts()) :
            echo '<div>';
            echo '<h3>'.$cat->name.'</h3>';
            echo '<ul>';
            while ($customInCatQuery->have_posts()) : $customInCatQuery->the_post(); ?>

            <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
        <?php
            endwhile; 
            echo '</ul></div>'; 
        ?>

<?php
        else :
            echo 'Нет записей в категории: '.$cat->name;                
        endif; 
        wp_reset_query();
    endforeach; 
?>

Надеюсь, это поможет.

25 февр. 2012 г. 00:30:41
Комментарии

Спасибо, но возьмем, например, 10 категорий. В этом случае ваш код вызывает 11 SQL-запросов. Мне нужен всего 1 SQL-запрос. Лучшим решением, вероятно, было бы получить все записи, отсортированные по категориям, но я не знаю, как это сделать (я не нашел order_by = cat в кодексе) !?

Ben Ben
25 февр. 2012 г. 01:12:55

Не думаю, что есть хоть какая-то разница между 10 или 20 категориями... (пробовал с 20) если только вы не пытаетесь загрузить огромное количество записей, что в любом случае может замедлить загрузку страницы... Попробуйте - увидите, что всё работает очень быстро - попытки перегруппировать после загрузки по категориям потребуют написания громоздкого и в основном бесполезного кода (по моему скромному мнению :))

Sagive Sagive
25 февр. 2012 г. 01:33:35

Нет, всё проще - нужно лишь немного изменить вывод (т.е. закрыть список для категории n и начать список для категории n+1) каждый раз, когда запись имеет категорию, отличную от категории предыдущей записи... Гораздо проще, чем нагружать вашу бедную базу данных 20+ запросами на каждую загрузку страницы... (по моему скромному мнению :)

Ben Ben
25 февр. 2012 г. 02:04:14

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

Sagive Sagive
25 февр. 2012 г. 02:37:18

Разница в количестве запросов к базе данных. Один большой против множества маленьких... ;)

Ben Ben
25 февр. 2012 г. 03:11:51

хм.. не знаю - не разбираюсь в этом настолько глубоко.. я имею в виду, как WordPress обрабатывает этот запрос.. но. я попробовал, и думаю, что это работает действительно быстро. надеюсь, я хотя бы сократил путь к вашему желаемому решению. ;)

Sagive Sagive
25 февр. 2012 г. 03:21:28

O(n) неэффективно

S.. S..
29 апр. 2018 г. 20:47:01
Показать остальные 2 комментариев
1
-1

Я создал кое-что для себя, чем пользуюсь довольно часто, вот код. Вы можете использовать ярлыки (слаги), ID или объекты терминов в массиве $categories. Если вам нужно получить все категории, можно использовать get_terms() и передать массив объектов терминов, но будьте осторожны — этот код не обрабатывает иерархию.

$categories = array( 1, 'slug', 3 );
echo '<ul>';
foreach($categories as $category) {
    $term = ( is_numeric($category) || is_object($category) ? get_term( $category, 'category' ) : get_term_by( 'slug', $category, 'category' ) );
    $args = array(
        'cat' => $term->term_id
        // Добавьте другие аргументы по необходимости
    );
    $q = new WP_Query( $args );
    if( $q->have_posts() ) {
        echo '<li><a href="' . get_term_link( $term->term_id, 'category' ) . '">' . $term->name . '</a><ul>';
        while( $q->have_posts() ) {
            $q->the_post();
            echo '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>';
        }
        echo '</ul></li>';
    } else {

    }
}
echo '</ul>';
25 февр. 2012 г. 00:32:56
Комментарии

O(n2) неэффективно

S.. S..
29 апр. 2018 г. 20:47:57
4
-1

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

<?php
    $category_ids = get_all_category_ids();
    foreach ($category_ids as $values) {
        $args = array('category' => $value);
        $posts_array = get_posts( $args );
        foreach ($posts_array as $post) : setup_postdata($post);
?> 
    <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php
        endforeach;
        wp_reset_query();
    }
?>
25 февр. 2012 г. 00:54:00
Комментарии

Та же проблема, что и в коде Sagive

Ben Ben
25 февр. 2012 г. 01:13:19

Хорошо. Невозможно выполнить одним запросом. Загрузка любой страницы WordPress уже включает множество запросов, даже без добавления пользовательского кода. Все встроенные запросы используют класс WPDB. Единственный способ сделать это одним запросом - использовать свой собственный Transact SQL или сделать WPDB родительским классом для другого класса с этой целью и вызывать его таким образом.

Neil Davidson Neil Davidson
4 мар. 2012 г. 02:14:04

Обратите внимание, что get_all_category_ids(); теперь устарел

Pieter Goosen Pieter Goosen
6 янв. 2015 г. 10:16:03

O(n) неэффективно

S.. S..
29 апр. 2018 г. 20:47:27
2
-2

Если вы ищете плагин, вам может подойти List Category Posts.

Для запросов ознакомьтесь с функцией get_posts

24 февр. 2012 г. 23:48:04
Комментарии

или вот это: http://codex.wordpress.org/Function_Reference/get_all_category_ids

jasonflaherty jasonflaherty
24 февр. 2012 г. 23:48:17

Спасибо, но это не помогло.

Ben Ben
25 февр. 2012 г. 00:09:17