query_posts() — модификация основного цикла

Когда я писал про циклы, я говорил, что если хотите как-то «по-особому» вывести записи, например в случайном порядке, используйте query_posts().

Итак, query_posts() видоизменяет основной цикл WordPress, то есть, если раньше на главной показывались последние 10 записей с блога, то теперь там может показываться что угодно и сколько угодно.

query_posts( $query )
$query
(строка|массив) все параметры такие же, как и у WP_Query()

Обычные примеры

Этот код выведет заголовки всех записей из рубрики с id=22:

query_posts("cat=22");
while (have_posts()) : the_post();
	echo'<p>'; the_title(); echo'</p>';
endwhile;
wp_reset_query();

Как и в случае с wp_list_categories(), параметры можно указывать в скобках, а можно выносить в отдельную переменную-массив (кстати, советую использовать именно двойные кавычки, как в примере).

Выводим записи из всех рубрик кроме id=1 и id=4:

query_posts("cat=-1,-4");

Выводим записи, опубликованные в текущем месяце.

$curr_year = date('Y'); // текущий год
$curr_month = date('m'); // текущий месяц
query_posts("year=$curr_year&monthnum=$curr_month");

Записи из рубрики с id=2, опубликованные в текущем месяце и отсортированные по заголовкам (в алфавитном порядке) по возрастанию:

$args = array(
	'cat'      => 2,
	'year'     => $curr_year,
	'monthnum' => $curr_month,
	'orderby'  => 'title',
	'order'    => 'ASC'
);
query_posts($args);

Примеры с модификацией основного цикла страницы

Честно говоря, все предыдущие примеры вообще как-то не в тему к этой функции, они больше подходят к использованию через WP_Query(). Ну да ладно, прост пост я писал давно, а сейчас жалко все это удалять. Короче я покажу, для чего на самом деле нужна функция query_posts().

Сортируем посты на странице по имени:

global $query_string; // глобальные параметры в виде строки
query_posts( $query_string . '&orderby=name' ); // делаем сортировку постов по имени, объединив параметры

Тот же самый пример, но только через массивы:

global $wp_query; // глобальные параметры, которые уже стоят по умолчанию на странице
$args = array_merge( $wp_query->query_vars, array( 'orderby' => 'name' ) ); // добавляем к ним сортировку по имени
query_posts( $args );

Вы наверное понимаете, что таким образом можно вытворять всё, что угодно, например сортировать товары по цене (и по возрастанию и по убыванию).

P.S. в самой документации WordPress не рекомендуется использовать несколько query_posts() на странице, они говорят, что это приводит к багам, хотя лично я ничего такого не замечал.

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

  • serj16 ноября 2012 в 22:11 #

    Привет Миша! Помоги решить задачу.
    На главную нужно вывести из трех кат.(например с id=123,320,405) по три записи(c id=234,345,677), как осуществить, что удалить может. Спасибо.

    вот код --->

  • Roman4 августа 2014 в 21:08 #

    Здравствуйте.
    Собственно, ситуация следующая.
    На главной странице использую плагин для пагинации — PageNavi, все работает корректно.
    Сейчас создал свою страницу для одной из категорий, и решил подключить стандартную пагинацию, т.к. шаблоны страниц слегка отличаются.

    Использовал код —

    max_num_pages > 2 ) : ?>

    На сайте ограничение стоит на 10 записей, всего в базе — 12.
    Пагинация выводит три номера страницы и дублирует одни и те же записи.

    В чем может заключаться проблема?

    • Миша5 августа 2014 в 07:08 #

      Добрый день!
      Если у вас в настройках сайта стоит 10 записей, а выводится другое количество, это значит, что в цикле параметр posts_per_page переопределяется, вам нужно найти где.

  • Roman5 августа 2014 в 09:08 #

    Спасибо большое! Нашел, где идет перераспределение) Одна лишняя страница исчезла, но осталась ещё одна, которая дублирует записи на предыдущей странице.
    В базе - 12 записей, она дубликатом выдает 20.

    Какой здесь подвох? Безумно буду благодарен за совет!)

  • Roman5 августа 2014 в 09:08 #

    Может как-то циклом задать, что если нет постов, не дублировать предыдущие записи?

  • Roman5 августа 2014 в 17:08 #

    Проблема найдена! И она крылась намного глубже)) Дело даже не в posts_per_page, как оказалось, wp_query был лишним и вызывал сбой в работе.

    Михаил, огромное Вам спасибо за наводку. И огромное спасибо за ресурс и статьи!

  • Роман24 ноября 2015 в 01:11 #

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

    <?php if (is_category('club')): {query_posts('category_name=club&showposts=4&orderby=date');} ?>

    // далее идёт цикл

    сама навигация отображается, но при клике на ссылку второй страницы вижу 404 (
    есть решение?

    • Роман24 ноября 2015 в 03:11 #

      сам спросил и сам отвечу )
      временно вопрос решил через функцию:

      function club_post_queries( $query ) {
        if (!is_admin() && $query->is_main_query()){
          if(is_category('club')){
            $query->set('posts_per_page', 4);
          }
        }
      }
      add_action( 'pre_get_posts', 'club_post_queries' );

      но глобально вопрос остался
      если будет опубликовано больше 4х статей, то система обрежет лишнее (

      появилась мысль сделать выборку по дате.
      т.е. выводить на странице посты за одну дату как в архивах, а пагинация должна перебрасывать на следующую страницу с постами у которых общая дата
      как это возможно?

      • Миша24 ноября 2015 в 08:11 #

        но глобально вопрос остался
        если будет опубликовано больше 4х статей, то система обрежет лишнее (

        Разве?
        По-моему вы уже нашли решение через pre_get_posts ?

        • Роман24 ноября 2015 в 16:11 #

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

          • Миша25 ноября 2015 в 08:11 #

            Разделить по дням? А почему не используете архивы по дням?
            Например http://сайт/2015/02/18 ?

            • Роман25 ноября 2015 в 12:11 #

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

  • Роман23 января 2016 в 20:01 #

    Добрый вечер, я вот в wpmag прочитал что query_posts нежелательно использовать т.к. она заменяет основной цикл и что вроде могут быть ошибки. Могут быть с этой функцией проблемы?

    • Миша24 января 2016 в 11:01 #

      Добрый вечер!
      По сути с любой функцией могут :) Что касается query_posts(), то просто следует знать, где её нужно использовать, а где нет.

      Если вы скажите, для каких целей вы хотите её задействовать, я могу вам подсказать — нужно или нет :)

  • Алексей6 мая 2016 в 16:05 #

    Миша, это снова я.
    Напомню. На вордпрессе создана произвольная таксономия, сделана страница архива этой таксономии, скажем, taxonomy-nane.php.
    Нужно написать код, который сделает так, чтобы на странице архива не выводились ссылки на посты, принадлежащие дочерним таксономиям (подтаксономиям).
    мне вот тут посоветовали вот так код построить:
    сначала вот это:

    <?php $the_query = new WP_Query( $args );  
    $args = array(   
    'child_of'     => 0,   
    'parent'       => ''  
    )   
    ?>
    <?php while ($the_query->have_posts()) : $the_query->the_posts();   ?>

    потом тайтлы, контенты
    а потом вот это

    <?php endwhile; ?>
    <?php wp_reset_postdata();  ?>

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

    • Миша7 мая 2016 в 08:05 #

      Окей, по порядку.

      1. query_posts().
      Нужен именно он. В самом начале файла:

      global $wp_query;
      $taxonomy_slug = get_query_var('term');
      $taxonomy = get_query_var('taxonomy');
       
      $args = array_merge( $wp_query->query_vars, array( 'tax_query', array(array(  'taxonomy' => $taxonomy, 'field' => 'slug', 'terms' => array( $taxonomy_slug ), 'include_children' => false ) ) ) ); 
      query_posts( $args );

      2. get_terms()

      $term = get_term_by( 'slug', $taxonomy_slug, $taxonomy );
      $term_id = $term->term_id;
       
      $children = get_terms($taxonomy, array( 'child_of' => $term_id ) ); 
       
      echo '<ul>';
      foreach ($children as $term){
      	echo "<li>{$term->name} ({$term->count})</li>";
      }
      echo '</ul>';
      • Алексей7 мая 2016 в 12:05 #

        вероятно, я не знаю каких-то основ, но я пишу вот это

        <?php 
        global $wp_query;
        $taxonomy_slug = get_query_var('term');
        $taxonomy = get_query_var('taxonomy');
         
        $args = array_merge( $wp_query->query_vars, array( 'tax_query', array(array(  'taxonomy' => $taxonomy, 'field' => 'slug', 'terms' => array( $taxonomy_slug ), 'include_children' => false ) ) ) ); 
        query_posts( $args );
        ?>

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

        • Миша10 мая 2016 в 14:05 #

          Через обычный while ?

          • Алексей10 мая 2016 в 14:05 #

            Миша, к сожалению, не могу сказать, так как не знаю, что это такое.
            Факт в том, что сделал то, что хотел с помощью одного программиста. Ваши подсказки я ему тоже показывал.

  • Дмитрий27 февраля 2017 в 23:02 #

    Привет, Михаил. Дублирую из скайпа вопрос:

    В купленной мной теме есть файл портфолио, которое выводит все записи из избранной рубрики. Так же в нем организован фильтр, который переключает записи по подрубрикам для удобства. Это портфолио я приспособил под каталог. Изначально было предусмотрен вывод 60 записей в виде плитки по три в ряд, что вполне смотрибельно. Но в моем каталоге около 200 записей, так что страница долго загружается и имеет длинную прокрутку.

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

    Так что сейчас задача такая:

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

    Сам код ниже.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    
    <!--?php $titan = TitanFramework::getInstance( 'gpress' );?-->
    <div id="7" class="gp-home-container gp-light-bg  gp-clearfix">
    <div class="gp-home-container-inner gp-clearfix"><!-- end isotope-list --> <!--?php endif; ?--></div>
    <!-- end home-container-inner --></div>
    <!-- end home-container --> <!--?php $titan = TitanFramework::getInstance( 'gpress' );?-->
    <div id="7" class="gp-home-container gp-light-bg  gp-clearfix">
    <div class="gp-home-container-inner gp-clearfix">
    <div class="item-action-icons"> <?php $titan = TitanFramework::getInstance( 'gpress' );?></div>
    </div>
    </div>
    <div id="7" class="gp-home-container gp-light-bg gp-clearfix">
     
    <div class="gp-home-container-inner gp-clearfix">
     
    <div class="gp-home-container-title">
     
    <h2><?php echo $titan->getOption( 'filter-title' ); ?></h2>
     
    <span><?php echo $titan->getOption( 'filter-subtitle' ); ?></span>
     
    </div><!-- end home-container title -->
    <ul id="filters" class="gp-clearfix">
     
    <li>Фильтр: </li>
     
    <li><a href="#" data-filter="*" class="selected"><?php _e('Все', 'gpress'); ?></a></li>
     
    <?php
     
    $terms = get_terms('category', array('parent' => $titan->getOption( 'filter-cat' )));
     
    $count = count($terms);
     
    if ( $count > 0 ){
     
    foreach ( $terms as $term ) {
     
    echo "<li><a href='#' data-filter='.".$term->slug."'>" . $term->name . "</a></li>\n";
     
    }
     
    }
     
    ?>
     
    </ul>
     
    <?php
     
    $terms_ID_array = array();
     
    foreach ($terms as $term)
     
    {
     
    $terms_ID_array[] = $term->term_id;
     
    }
     
    $terms_ID_string = implode(',', $terms_ID_array);
    $the_query = new WP_Query( 'posts_per_page=30&cat='.$terms_ID_string );
     
    ?>
     
    <?php if ( $the_query->have_posts() ) : ?>
     
    <div id="isotope-list">
     
    <?php while ( $the_query->have_posts() ) : $the_query->the_post();
     
    $termsArray = get_the_terms( $post->ID, "category" );
     
    $termsString = "";
     
    foreach ( $termsArray as $term ) {
     
    $termsString .= $term->slug.' ';
     
    }
    ?>
     
    <div class="<?php echo $termsString; ?> filter-block grid-item">
     
    <div class="item-wrap">
     
    <?php
     
    $thumb = get_post_thumbnail_id();
     
    $img_url = wp_get_attachment_url( $thumb,'full' );
     
    $image = aq_resize( $img_url, 400, 400, true, true, true );
     
    ?>
     
    <?php if($image) : ?><img src="<?php echo $image ?>" alt="<?php the_title(); ?>"><?php endif; ?>
     
    </div> <!-- end item-wrap -->
     
    <div class="item-inner">
     
    <h2><?php the_title(); ?></h2>
     
    </div><!-- end item inner -->
     
    <div class="item-action-icons">
     
    <?php if($image) : ?> <a rel="lightbox" href="<?php echo $img_url ?>"><i class="fa fa-arrows-alt" aria-hidden="true"></i></a><?php endif; ?>
     
    <a href="<?php the_permalink() ?>"><i class="fa fa-link" aria-hidden="true"></i></a>
     
    </div>
     
    </div> <!-- end item -->
     
    <?php endwhile; ?>
    </div> <!-- end isotope-list -->
     
    <?php endif; ?>
     
    </div><!-- end home-container-inner -->
     
    </div><!-- end home-container -->
    • Миша1 марта 2017 в 09:03 #

      Привет!

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

      Ничего страшного, если вы не программер, вам не придётся работать с промисами js или копать глубоко в PHP-классы.

      Итак, пошагово:

      1. Для вывода цикла используйте функцию query_posts() вместо WP_Query — собственно мы как раз находимся в статье, где она описана.
      2. В неё вам также нужно передавать номер текущей страницы при помощи параметра paged и количество постов, которые вы хотите отобразить при помощи posts_per_page. Это второй шаг.
      3. Убедитесь, что на этот момент всё сделали правильно — при переходе на портфолио, когда вы будете добавлять в URL что-то типо /page/2/ — всё должно работать как полагается.
      4. Пагинация — стандартная wp_pagenavi() при таком раскладе вполне хорошо должна работать, если нет, то рекомендую взять мою функцию отсюда — просто скопируйте её в functions.php вашей темы, после чего можете смело использовать.

      Собственно ссылка на подробный урок.

      Если будут какие-то вопросы по существу, оставляйте их прямо тут в ответе на мой комментарий.

      • Дмитрий1 марта 2017 в 12:03 #

        Михаил, вы можете внести изменения в код, который я скинул выше? Я просто скопирую его и проверю. Заранее спасибо

        • Миша1 марта 2017 в 18:03 #

          Я всегда за то, чтобы человек сам во всём разобрался и научился чему-то. Поэтому - нет. С этим вопросом вам на биржу фриланса.

          В уроке, который я вам скинул, и так уже все изменения в коде внесены.

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

        • Миша1 марта 2017 в 18:03 #

          Даже обидно, так старался всё расписал вам, а в ответ "Я просто скопирую его и проверю.".

          • Дмитрий1 марта 2017 в 21:03 #

            Я ни разу не программист. Максимум на что меня хватает - это найти ненужный кусок кода типа "кнопки" и выбросить его или переставить местами блоки. И я не собираюсь изучать php, у меня другие интересы. То, что для вас выглядит как очевидная инструкция - для меня таковой не является. Я честно оплатил доступ к сайту, вы ответили. Возможно мы друг друга не поняли, но неужели для вас, как для профессионала, вставить нужный кусок кода - более затратный процесс, чем написание вышеуказанной инструкции?

        • Миша3 марта 2017 в 09:03 #

          Хорошо, я вам опишу всё настолько подробно, насколько это возможно, если в этом случае у вас ничего не получится, в рекомендую обратиться за помощью к фрилансеру. Без вариантов.

          В первую очередь нам нужно заменить 61-ю строчку вашего кода (смотрите цифры выше) на следующий кусок кода:

          $current_page = ( get_query_var('paged') ) ? get_query_var('paged') : 1;
          $params = array(
          	'posts_per_page' => 30, // количество постов на странице
          	'cat'       => $terms_ID_string,
          	'paged'           => $current_page // текущая страница
          );
          query_posts($params);

          Так как функция query_posts() просто заменяет основной цикл, то нужно сам цикл также переделать под неё, а именно: на строках 65 и 69 просто удаляем это $the_query-> (вместе со стрелкой!) - всего я насчитал их три штуки.

          Последним шагом на 118й строке вам нужно вставить функцию постраничной навигации, это может быть функция из плагина wp_pagenavi(), либо та функция, которую я вам прислал в предыдущем комментарии (перед её использованием её нужно предварительно вставить в functions.php)

          echo misha_pagination( $wp_query );
          • Дмитрий4 марта 2017 в 10:03 #

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

Оставить комментарий / вопрос

phpjsHTMLCSSSQLПросто код
  Для того, чтобы оставить комментарий, пожалуйста, зарегистрируйтесь или авторизуйтесь на сайте.
Получайте новости блога по email или следите за мной в социальных сетях.
  • Миша: Добрый день! В принципе вам только немного нужно доработать код из этого поста - категории уже есть, цена - это и так...

  • eveniy: Добрый день Михаил, подскажите на примере как добавить свои произвольные поля в в вашу форму фильтрации: Я так понимаю...

  • Миша: Отлично! )

  • Дмитрий: сам разобрался: в настройках кастомного типа записи недавно оптимизировал настройки и переоптимизировал: в аргументе 'ta...

  • Дмитрий: У меня по какой-то причине пропало отображение (как в колонке меню слева, так и метабокс в записях ) раздела рубрик для...