Как исправить разбивку на страницы для пользовательских циклов?


Я добавил пользовательский/дополнительный запрос в файл шаблона/шаблон пользовательской страницы; как я могу заставить WordPress использовать мой пользовательский запрос для разбиения на страницы вместо использования разбиения на страницы основного цикла запроса?

Добавление

Я изменил запрос основного цикла с помощью query_posts(). Почему не работает разбивка на страницы и как это исправить?

Author: jgraup, 2013-10-28

5 answers

Проблема

По умолчанию в любом данном контексте WordPress использует основной запрос для определения разбиения на страницы. Основной объект запроса хранится в глобальном $wp_query, который также используется для вывода основного цикла запроса:

if ( have_posts() ) : while ( have_posts() ) : the_post();

Когда вы используете пользовательский запрос , вы создаете совершенно отдельный объект запроса:

$custom_query = new WP_Query( $custom_query_args );

И этот запрос выводится через совершенно отдельный цикл:

if ( $custom_query->have_posts() ) : 
    while ( $custom_query->have_posts() ) : 
        $custom_query->the_post();

Но теги шаблонов разбиения на страницы, включая previous_posts_link(), next_posts_link(), posts_nav_link(), и paginate_links(), основывают свои выходные данные на основном объекте запроса , $wp_query. Этот основной запрос может быть разбит на страницы, а может и не быть. Если текущий контекст является пользовательским шаблоном страницы, например, основной объект $wp_query будет состоять только из одной записи - идентификатора страницы, которой назначен пользовательский шаблон страницы.

Если текущий контекст является своего рода архивным индексом, основной $wp_query может состоять из достаточного количества записей чтобы вызвать разбиение на страницы, что приводит к следующей части проблемы: для основного объекта $wp_query WordPress передаст в запрос параметр paged на основе переменной запроса URL paged. Когда запрос будет извлечен, этот параметр paged будет использоваться для определения того, какой набор записей с разбиением на страницы следует возвращать. Если щелкнуть отображаемую ссылку на разбивку на страницы и загрузить следующую страницу, ваш пользовательский запрос не сможет узнать, что разбивка на страницы изменилась.

В Решение

Передача правильного выгружаемого параметра в Пользовательский запрос

Предполагая, что пользовательский запрос использует массив args:

$custom_query_args = array(
    // Custom query parameters go here
);

Вам нужно будет передать правильный параметр paged в массив. Вы можете сделать это, выбрав переменную запроса URL, используемую для определения текущей страницы, с помощью get_query_var():

get_query_var( 'paged' );

Затем вы можете добавить этот параметр в массив аргументов пользовательского запроса:

$custom_query_args['paged'] = get_query_var( 'paged' ) 
    ? get_query_var( 'paged' ) 
    : 1;

Примечание: Если ваша страница является статической первая страница, обязательно используйте page вместо paged, поскольку статическая первая страница использует page, а не paged. Это то, что у вас должно быть для статической главной страницы

$custom_query_args['paged'] = get_query_var( 'page' ) 
    ? get_query_var( 'page' ) 
    : 1;

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

Использование пользовательского объекта запроса для функций разбиения на страницы

Для того, чтобы функции разбиения на страницы выдавали правильные выходные данные, т.Е. ссылки на предыдущую/следующую/страницу относительно пользовательского запроса, WordPress необходимо быть вынужденным распознавать пользовательский запрос. Для этого требуется немного "взломать": заменить основной объект $wp_query объектом пользовательского запроса, $custom_query:

Взломать основной объект запроса

  1. Резервное копирование основного объекта запроса: $temp_query = $wp_query
  2. Обнуляет основной объект запроса: $wp_query = NULL;
  3. Замените пользовательский запрос на основной объект запроса: $wp_query = $custom_query;

    $temp_query = $wp_query;
    $wp_query   = NULL;
    $wp_query   = $custom_query;
    

Этот "взлом" должен быть выполнен перед вызовом любой разбивки на страницы функции

Сброс основного объекта запроса

После вывода функций разбиения на страницы сбросьте основной объект запроса:

$wp_query = NULL;
$wp_query = $temp_query;

Исправлена функция разбиения на страницы

В previous_posts_link() функция будет работать нормально, независимо от разбиения на страницы. Он просто определяет текущую страницу, а затем выводит ссылку для page - 1. Тем не менее, требуется исправление для next_posts_link() для правильного вывода. Это потому, что next_posts_link() использует max_num_pages параметр:

<?php next_posts_link( $label , $max_pages ); ?>

Как и в случае с другими параметрами запроса, по умолчанию функция будет использовать max_num_pages для основного объекта $wp_query. Для того, чтобы заставить next_posts_link() чтобы учесть объект $custom_query, вам нужно будет передать max_num_pages функции. Вы можете извлечь это значение из объекта $custom_query: $custom_query->max_num_pages:

<?php next_posts_link( 'Older Posts' , $custom_query->max_num_pages ); ?>

Собирая все это вместе

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

// Define custom query parameters
$custom_query_args = array( /* Parameters go here */ );

// Get current page and append to custom query parameters array
$custom_query_args['paged'] = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1;

// Instantiate custom query
$custom_query = new WP_Query( $custom_query_args );

// Pagination fix
$temp_query = $wp_query;
$wp_query   = NULL;
$wp_query   = $custom_query;

// Output custom query loop
if ( $custom_query->have_posts() ) :
    while ( $custom_query->have_posts() ) :
        $custom_query->the_post();
        // Loop output goes here
    endwhile;
endif;
// Reset postdata
wp_reset_postdata();

// Custom query loop pagination
previous_posts_link( 'Older Posts' );
next_posts_link( 'Newer Posts', $custom_query->max_num_pages );

// Reset main query object
$wp_query = NULL;
$wp_query = $temp_query;

Добавление: Как насчет query_posts()?

query_posts() для вторичных циклов

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

Использование query_posts() для изменения основного цикла

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

Решение состоит в том, чтобы фильтровать основной запрос до получения сообщений с помощью крючка pre_get_posts.

Вместо добавления этого в файл шаблона категории (category.php):

query_posts( array(
    'posts_per_page' => 5
) );

Добавьте следующее в functions.php:

function wpse120407_pre_get_posts( $query ) {
    // Test for category archive index
    // and ensure that the query is the main query
    // and not a secondary query (such as a nav menu
    // or recent posts widget output, etc.
    if ( is_category() && $query->is_main_query() ) {
        // Modify posts per page
        $query->set( 'posts_per_page', 5 ); 
    }
}
add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' );

Вместо добавления этого в файл шаблона индекса записей в блоге (home.php):

query_posts( array(
    'cat' => '-5'
) );

Добавьте следующее в functions.php:

function wpse120407_pre_get_posts( $query ) {
    // Test for main blog posts index
    // and ensure that the query is the main query
    // and not a secondary query (such as a nav menu
    // or recent posts widget output, etc.
    if ( is_home() && $query->is_main_query() ) {
        // Exclude category ID 5
        $query->set( 'category__not_in', array( 5 ) ); 
    }
}
add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' );

Что таким образом, WordPress будет использовать уже измененный объект $wp_query при определении разбиения на страницы, без необходимости изменения шаблона.

Когда использовать какую функцию

Исследуйте этот вопрос и ответ и этот вопрос и ответ , чтобы понять, как и когда использовать WP_Query, pre_get_posts, и query_posts().

 232
Author: Chip Bennett, 2017-04-13 12:37:46

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

<?php
if ( get_query_var('paged') ) {
    $paged = get_query_var('paged');
} elseif ( get_query_var('page') ) { // 'page' is used instead of 'paged' on Static Front Page
    $paged = get_query_var('page');
} else {
    $paged = 1;
}

$custom_query_args = array(
    'post_type' => 'post', 
    'posts_per_page' => get_option('posts_per_page'),
    'paged' => $paged,
    'post_status' => 'publish',
    'ignore_sticky_posts' => true,
    //'category_name' => 'custom-cat',
    'order' => 'DESC', // 'ASC'
    'orderby' => 'date' // modified | title | name | ID | rand
);
$custom_query = new WP_Query( $custom_query_args );

if ( $custom_query->have_posts() ) :
    while( $custom_query->have_posts() ) : $custom_query->the_post(); ?>

        <article <?php post_class(); ?>>
            <h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
            <small><?php the_time('F jS, Y') ?> by <?php the_author_posts_link() ?></small>
            <div><?php the_excerpt(); ?></div>
        </article>

    <?php
    endwhile;
    ?>

    <?php if ($custom_query->max_num_pages > 1) : // custom pagination  ?>
        <?php
        $orig_query = $wp_query; // fix for pagination to work
        $wp_query = $custom_query;
        ?>
        <nav class="prev-next-posts">
            <div class="prev-posts-link">
                <?php echo get_next_posts_link( 'Older Entries', $custom_query->max_num_pages ); ?>
            </div>
            <div class="next-posts-link">
                <?php echo get_previous_posts_link( 'Newer Entries' ); ?>
            </div>
        </nav>
        <?php
        $wp_query = $orig_query; // fix for pagination to work
        ?>
    <?php endif; ?>

<?php
    wp_reset_postdata(); // reset the query 
else:
    echo '<p>'.__('Sorry, no posts matched your criteria.').'</p>';
endif;
?>

Источник:

 22
Author: webvitaly, 2017-02-21 01:38:22

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

Используя paginate_links() как вы упомянули выше, в основном по умолчанию (и при условии, что у вас включены довольно постоянные ссылки), ваши ссылки на страницы по умолчанию будут иметь значение mysite.ca/page-slug/page/#, что прекрасно, но приведет к ошибкам 404, потому что WordPress не знает о эта конкретная структура URL-адреса и на самом деле будет искать дочернюю страницу "страницы", которая является дочерней для "страницы-слизняка".

Хитрость здесь в том, чтобы вставить изящное правило перезаписи, которое применяется только к этой конкретной странице "псевдоархива", которая принимает структуру /page/#/ и переписывает ее в строку запроса, понятную WordPress, а именно mysite.ca/?pagename=page-slug&paged=#. Обратите внимание pagename и paged, а не name и page (что вызвало у меня буквально ЧАСЫ горя, мотивируя этот ответ здесь!).

Вот правило перенаправления:

add_rewrite_rule( "page-slug/page/([0-9]{1,})/?$", 'index.php?pagename=page-slug&paged=$matches[1]', "top" );

Как всегда, при изменении правил перезаписи не забудьте удалить постоянные ссылки , посетив Настройки > Постоянные ссылки в интерфейсе администратора.

Если у вас есть несколько страниц, которые будут вести себя таким образом (например, при работе с несколькими пользовательскими типами записей), вы можете избежать создания нового правила перезаписи для каждого фрагмента страницы. Мы можем написать более общее регулярное выражение, которое будет работать для любой страницы, которую вы идентифицируете.

Один подход приведен ниже:

function wpse_120407_pseudo_archive_rewrite(){
    // Add the slugs of the pages that are using a Global Template to simulate being an "archive" page
    $pseudo_archive_pages = array(
        "all-movies",
        "all-actors"
    );

    $slug_clause = implode( "|", $pseudo_archive_pages );
    add_rewrite_rule( "($slug_clause)/page/([0-9]{1,})/?$", 'index.php?pagename=$matches[1]&paged=$matches[2]', "top" );
}
add_action( 'init', 'wpse_120407_pseudo_archive_rewrite' );

Недостатки /предостережения

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

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

 7
Author: Tom Auger, 2016-12-17 00:49:08

Я изменил запрос основного цикла с помощью query_posts(). Почему не работает разбивка на страницы и как это исправить?

Созданный чип с отличным ответом нуждается в модификации сегодня.
В течение некоторого времени у нас есть переменная $wp_the_query, которая должна быть равна глобальной переменной $wp_query сразу после выполнения основного запроса.

Вот почему эта часть из ответа Чипа:

Взломать основной объект запроса

Больше не нужен. Мы можем забыть эту часть с созданием временная переменная.

// Pagination fix
$temp_query = $wp_query;
$wp_query   = NULL;
$wp_query   = $custom_query;

Итак, теперь мы можем позвонить:

$wp_query   = $wp_the_query;

Или еще лучше мы можем позвонить:

wp_reset_query();

Все остальное, описанное Чипом, остается. После этой части запроса-сброса вы можете вызвать функции разбиения на страницы, которые являются f($wp_query), - они зависят от $wp_query глобальных.


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

Https://core.trac.wordpress.org/ticket/39483

 3
Author: prosti, 2020-06-15 08:21:38
global $wp_query;
        $paged = get_query_var('paged', 1);

    $args = array( 
        'post_type' => '{your_post_type_name}',
        'meta_query' => array('{add your meta query argument if need}'),  
        'orderby' => 'modified',
        'order' => 'DESC',
        'posts_per_page' => 20,
        'paged' => $paged 
    );
    $query = new WP_Query($args);

    if($query->have_posts()):
        while ($query->have_posts()) : $query->the_post();
            //add your code here
        endwhile;
        wp_reset_query();

        //manage pagination based on custom Query.
        $GLOBALS['wp_query']->max_num_pages = $query->max_num_pages;
        the_posts_pagination(array(
            'mid_size' => 1,
            'prev_text' => __('Previous page', 'patelextensions'),
            'next_text' => __('Next page', 'patelextensions'),
            'before_page_number' => '<span class="meta-nav screen-reader-text">' . __('Page', 'patelextensions') . ' </span>',
        ));
    else:
    ?>
        <div class="container text-center"><?php echo _d('Result not found','30'); ?></div>
    <?php
        endif;
    ?>
 3
Author: Ravi Patel, 2018-03-06 09:39:57