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

122

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

добавление

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

Чип Беннетт
источник

Ответы:

215

Проблема

По умолчанию в любом данном контексте 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 параметр в запрос на основе pagedПеременная запроса URL. Когда запрос извлекается, этот pagedпараметр будет использоваться для определения того, какой набор постраничных постов должен возвращаться. Если щелкнуть отображаемую ссылку нумерации страниц и загрузить следующую страницу, ваш пользовательский запрос не сможет узнать, что нумерация страниц изменилась .

Решение

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

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

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

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

get_query_var( 'paged' );

Затем вы можете добавить этот параметр в ваш собственный массив args запроса:

$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().

Чип Беннетт
источник
31
Страницы желаний в кодексе могут быть настолько полными.
Питер Гусен
Чип, ты сделал мой день!
Тепкенваннкорн
1
Чип, ты всегда экономишь столько времени! если бы только Google оценил бы ваши ответы выше (выноска для googlers), прежде чем я сошел с ума в поисках;) спасибо.
Sagive SEO
Используя ваш пример, я не мог заставить пейджинг работать, пока не использовал блок if-else, который был найден посередине (вместо?: Условного ) вниз по этой странице: themeforest.net/forums/thread/… , очень странно. В противном случае этот ответ многому меня научил.
P аул
2
Великий ответ - 1 вещь, у меня были проблемы с запуском следующей / предыдущей функции после ссылки в вызов AJAX - это просто не будет брать - после того, как быстро рыть вокруг , я обнаружил , что глобальное pagedне обновляется (OBV - то делать с Администраторский окружение ajax.php) поэтому я добавил это: global $paged; $paged = $custom_query_args['paged']; и это сработало :)
acSlater
21

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

<?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;
?>

Источник:

webvitaly
источник
1
Еще один хороший учебник с вариантом этого ответа: callmenick.com/post/custom-wordpress-loop-with-pagination
mrwweb
5

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

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

Хитрость заключается в том, чтобы вставить изящное правило перезаписи, которое применяется только к тому конкретному слагу страницы «страница псевдоархива», который принимает /page/#/структуру и переписывает ее в строку запроса, которую понимает WordPress CAN, а именно 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.

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

Том Оже
источник
1
Вы можете подключить сохранение после записи, проверить, есть ли на странице ваш шаблон архива под мета-ключом _wp_page_template, а затем добавить еще одно правило перезаписи и очистки.
Мило
2

Я изменил запрос основного цикла через 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

Прости
источник
1
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;
    ?>
Рави Патель
источник