Cum se repară paginarea pentru bucle personalizate?

28 oct. 2013, 21:00:53
Vizualizări: 136K
Voturi: 144

Am adăugat o interogare personalizată/secundară într-un fișier template/șablon de pagină personalizat; cum pot face WordPress să folosească interogarea mea personalizată pentru paginare, în loc să folosească paginarea buclei principale?

Addendum

Am modificat interogarea buclei principale prin query_posts(). De ce nu funcționează paginarea și cum o pot repara?

1
Comentarii

De asemenea, citește acest tutorial: https://devnote.in/wordpress-paginate_links-how-to-use-it Va fi mult mai util.

Fefar Ravi Fefar Ravi
5 oct. 2021 19:34:53
Toate răspunsurile la întrebare 5
11
247

Problema

În mod implicit, în orice context dat, WordPress utilizează interogarea principală pentru a determina paginarea. Obiectul interogării principale este stocat în variabila globală $wp_query, care este folosită și pentru afișarea buclei principale de interogare:

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

Când utilizezi o interogare personalizată, creezi un obiect de interogare complet separat:

$custom_query = new WP_Query( $custom_query_args );

Și acea interogare este afișată printr-o buclă separată:

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

Dar funcțiile de paginare, inclusiv previous_posts_link(), next_posts_link(), posts_nav_link() și paginate_links(), își bazează rezultatul pe obiectul interogării principale, $wp_query. Acea interogare principală poate sau nu să fie paginată. Dacă contextul curent este un șablon de pagină personalizat, de exemplu, obiectul principal $wp_query va consta doar dintr-un singur post - cel al ID-ului paginii căreia i se atribuie șablonul de pagină personalizat.

Dacă contextul curent este un index de arhivă de un anumit tip, interogarea principală $wp_query poate consta din suficiente postări pentru a provoca paginare, ceea ce duce la următoarea parte a problemei: pentru obiectul principal $wp_query, WordPress va transmite un parametru paged către interogare, bazat pe variabila de interogare URL paged. Când interogarea este preluată, acel parametru paged va fi folosit pentru a determina care set de postări paginate să returneze. Dacă este apăsat un link de paginare afișat și este încărcată pagina următoare, interogarea ta personalizată nu va avea nicio modalitate de a ști că paginarea s-a schimbat.

Soluția

Transmiterea parametrului corect Paged către interogarea personalizată

Presupunând că interogarea personalizată folosește un array de argumente:

$custom_query_args = array(
    // Parametrii interogării personalizate merg aici
);

Va trebui să transmiți parametrul corect paged către array. Poți face acest lucru preluând variabila de interogare URL folosită pentru a determina pagina curentă, prin get_query_var():

get_query_var( 'paged' );

Apoi poți adăuga acel parametru în array-ul de argumente al interogării personalizate:

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

Notă: Dacă pagina ta este o pagină frontală statică, asigură-te că folosești page în loc de paged, deoarece o pagină frontală statică folosește page și nu paged. Iată ce ar trebui să ai pentru o pagină frontală statică:

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

Acum, când interogarea personalizată este preluată, va fi returnat setul corect de postări paginate.

Utilizarea obiectului de interogare personalizat pentru funcțiile de paginare

Pentru ca funcțiile de paginare să ofere rezultatul corect - adică linkuri anterioare/următoare/pagină relative la interogarea personalizată - WordPress trebuie forțat să recunoască interogarea personalizată. Acest lucru necesită un mic "hack": înlocuirea obiectului principal $wp_query cu obiectul de interogare personalizat, $custom_query:

Modificarea obiectului de interogare principal

  1. Salvează obiectul de interogare principal: $temp_query = $wp_query
  2. Anulează obiectul de interogare principal: $wp_query = NULL;
  3. Înlocuiește interogarea personalizată în obiectul de interogare principal: $wp_query = $custom_query;

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

Acest "hack" trebuie făcut înainte de a apela orice funcții de paginare

Resetarea obiectului de interogare principal

După ce funcțiile de paginare au fost afișate, resetează obiectul de interogare principal:

$wp_query = NULL;
$wp_query = $temp_query;

Remedieri pentru funcțiile de paginare

Funcția previous_posts_link() va funcționa normal, indiferent de paginare. Ea determină doar pagina curentă și apoi afișează linkul pentru page - 1. Totuși, este necesară o remediere pentru ca next_posts_link() să funcționeze corect. Acest lucru se întâmplă deoarece next_posts_link() utilizează parametrul max_num_pages:

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

Ca și în cazul altor parametri de interogare, în mod implicit funcția va folosi max_num_pages pentru obiectul principal $wp_query. Pentru a forța next_posts_link() să țină cont de obiectul $custom_query, va trebui să transmiți max_num_pages către funcție. Poți prelua această valoare din obiectul $custom_query: $custom_query->max_num_pages:

<?php next_posts_link( 'Postări mai vechi' , $custom_query->max_num_pages ); ?>

Punând totul cap la cap

Următorul este un construct de bază al unei bucle de interogare personalizată cu funcții de paginare care funcționează corect:

// Definește parametrii interogării personalizate
$custom_query_args = array( /* Parametrii merg aici */ );

// Obține pagina curentă și adaugă la array-ul de parametri ai interogării personalizate
$custom_query_args['paged'] = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1;

// Instanțiază interogarea personalizată
$custom_query = new WP_Query( $custom_query_args );

// Remediu pentru paginare
$temp_query = $wp_query;
$wp_query   = NULL;
$wp_query   = $custom_query;

// Afișează bucla interogării personalizate
if ( $custom_query->have_posts() ) :
    while ( $custom_query->have_posts() ) :
        $custom_query->the_post();
        // Aici intră conținutul buclei
    endwhile;
endif;
// Resetează datele postului
wp_reset_postdata();

// Paginarea buclei interogării personalizate
previous_posts_link( 'Postări mai vechi' );
next_posts_link( 'Postări mai noi', $custom_query->max_num_pages );

// Resetează obiectul de interogare principal
$wp_query = NULL;
$wp_query = $temp_query;

Anexă: Ce zici de query_posts()?

query_posts() pentru bucle secundare

Dacă folosești query_posts() pentru a afișa o buclă personalizată, în loc să instanțiezi un obiect separat pentru interogarea personalizată prin WP_Query(), atunci _doing_it_wrong() și vei întâmpina mai multe probleme (printre care și probleme de paginare). Primul pas pentru rezolvarea acestor probleme va fi conversia utilizării improprii a query_posts() la un apel corespunzător WP_Query().

Utilizarea query_posts() pentru modificarea buclei principale

Dacă vrei doar să modifici parametrii pentru interogarea buclei principale - cum ar fi schimbarea numărului de postări pe pagină sau excluderea unei categorii - poți fi tentat să folosești query_posts(). Dar tot nu ar trebui. Când folosești query_posts(), forțezi WordPress să înlocuiască obiectul de interogare principal. (WordPress face de fapt o a doua interogare și suprascrie $wp_query.) Problema, însă, este că face această înlocuire prea târziu în proces pentru a actualiza paginarea.

Soluția este să filtrezi interogarea principală înainte ca postările să fie preluate, prin hook-ul pre_get_posts.

În loc să adaugi asta în fișierul șablonului de categorie (category.php):

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

Adaugă următoarele în functions.php:

function wpse120407_pre_get_posts( $query ) {
    // Testează pentru indexul de arhivă al categoriei
    // și asigură-te că interogarea este interogarea principală
    // și nu o interogare secundară (cum ar fi un meniu de navigare
    // sau un widget de postări recente, etc.
    if ( is_category() && $query->is_main_query() ) {
        // Modifică numărul de postări pe pagină
        $query->set( 'posts_per_page', 5 ); 
    }
}
add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' );

În loc să adaugi asta în fișierul șablonului indexului de blog (home.php):

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

Adaugă următoarele în functions.php:

function wpse120407_pre_get_posts( $query ) {
    // Testează pentru indexul principal de postări de blog
    // și asigură-te că interogarea este interogarea principală
    // și nu o interogare secundară (cum ar fi un meniu de navigare
    // sau un widget de postări recente, etc.
    if ( is_home() && $query->is_main_query() ) {
        // Exclude categoria cu ID-ul 5
        $query->set( 'category__not_in', array( 5 ) ); 
    }
}
add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' );

În acest fel, WordPress va folosi obiectul $wp_query deja modificat pentru a determina paginarea, fără a fi necesară nicio modificare a șablonului.

Când să folosești ce funcție

Cercetează această întrebare și răspuns și această întrebare și răspuns pentru a înțelege cum și când să folosești WP_Query, pre_get_posts și query_posts().

28 oct. 2013 21:00:53
Comentarii

Aș dori ca paginile din codex să poată fi la fel de complete.

Pieter Goosen Pieter Goosen
3 apr. 2014 15:41:19

Chip, mi-ai făcut ziua mai bună!

tepkenvannkorn tepkenvannkorn
8 sept. 2014 11:57:18

Chip, mereu îmi economisești atât de mult timp! Dacă măcar Google ar rank-ui mai sus răspunsurile tale (semnătură pentru Googleri) înainte să înnebunesc căutând ;) mulțumesc.

Sagive Sagive
14 sept. 2014 18:56:33

Folosind exemplul tău, nu am putut face paginarea să funcționeze până când nu am folosit un bloc if-else, așa cum este găsit la mijlocul (în locul condiționalului ? :) acestei pagini: http://themeforest.net/forums/thread/pagination-on-wordpress-static-page-set-to-front-page/28120, foarte ciudat. În rest, acest răspuns m-a învățat multe.

P a u l P a u l
8 dec. 2014 08:52:43

Răspuns excelent - 1 lucru, aveam probleme cu rularea funcției de link pentru postările următoare/anterioare într-un apel ajax - pur și simplu nu funcționa - după o scurtă investigare, am descoperit că variabila globală paged nu era actualizată (evident din cauza mediului admin-ajax.php), așa că am adăugat asta: global $paged; $paged = $custom_query_args['paged']; și a funcționat :)

rmorse rmorse
20 dec. 2014 18:00:12

...Nu folosesc cuvântul 'erou' cu ușurință, dar tu ești cel mai mare erou din istoria Americii. - Lionel Hutz

dgo dgo
5 ian. 2015 20:05:26

Am creat acum paginare pentru bucla mea personalizată, pe o pagină principală, care afișează pagini copil. Încerc să accesez a doua pagină, prin http:example.com/main_page/page/2, dar sunt redirecționat înapoi la http://example.com/main_page. Aveți vreo idee de ce se întâmplă asta?

Mr Pablo Mr Pablo
6 iul. 2016 13:39:40

Și nu uitați să resetați structura de permalinkuri (resalvați setările structurii de permalinkuri)

Dan. Dan.
28 nov. 2016 21:31:54

Funcționează încă această soluție în WordPress 4.5+? Când urmez aceste instrucțiuni, rezultatul apare pe o "postări mai noi" pe pagina 1, dar când fac clic pentru a merge la pagina 2, rezultatele subinterogării sunt în continuare primele 10 postări.

Slam Slam
19 iul. 2018 03:16:04

M-am luptat cu paginarea mea pentru atât de mult timp, apoi am citit acest mic sfat: Notă: Dacă pagina ta este o pagină frontală statică, asigură-te că folosești page în loc de paged, deoarece o pagină frontală statică folosește page și nu paged. Acesta este lucrul pe care ar trebui să-l ai pentru o pagină frontală statică

user1676224 user1676224
25 iun. 2019 16:06:34

absolut perfect, funcționează minunat. una dintre puținele soluții la o problemă pe care am văzut-o explicată atât de clar și care chiar funcționează :D

djack109 djack109
1 iun. 2020 14:50:58
Arată celelalte 6 comentarii
0
22

Folosesc acest cod pentru o buclă personalizată cu paginare:

<?php
if ( get_query_var('paged') ) {
    $paged = get_query_var('paged');
} elseif ( get_query_var('page') ) { // 'page' este folosit în loc de 'paged' pe Pagina Principală Statică
    $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') ?> de <?php the_author_posts_link() ?></small>
            <div><?php the_excerpt(); ?></div>
        </article>

    <?php
    endwhile;
    ?>

    <?php if ($custom_query->max_num_pages > 1) : // paginare personalizată  ?>
        <?php
        $orig_query = $wp_query; // remediu pentru ca paginarea să funcționeze
        $wp_query = $custom_query;
        ?>
        <nav class="prev-next-posts">
            <div class="prev-posts-link">
                <?php echo get_next_posts_link( 'Articole mai vechi', $custom_query->max_num_pages ); ?>
            </div>
            <div class="next-posts-link">
                <?php echo get_previous_posts_link( 'Articole mai noi' ); ?>
            </div>
        </nav>
        <?php
        $wp_query = $orig_query; // remediu pentru ca paginarea să funcționeze
        ?>
    <?php endif; ?>

<?php
    wp_reset_postdata(); // resetare interogare 
else:
    echo '<p>'.__('Ne pare rău, nu există articole care să corespundă criteriilor dumneavoastră.').'</p>';
endif;
?>

Sursă:

9 nov. 2015 18:27:40
1

Superb ca întotdeauna, Chip. Ca un adaos la asta, ia în considerare situația în care folosești un șablon de pagină global atașat unei Pagini pentru un "text introductiv", urmat de o subinterogare pe care dorești să fie paginată.

Folosind paginate_links() cum menționezi mai sus, cu setări predefinite în mare parte (și presupunând că ai permalink-uri frumoase activate), link-urile tale de paginare vor folosi în mod implicit structura mysite.ca/page-slug/page/#, ceea ce e minunat, dar va genera erori 404 deoarece WordPress nu recunoaște această structură de URL și va încerca să găsească o pagină copil a "page" care este copil a "page-slug".

Trucul aici este să inserezi o regulă de rescriere inteligentă care se aplică doar acelui anumit "pseudo-archive page" cu un anumit slug, care acceptă structura /page/#/ și o rescrie într-un șir de interogare pe care WordPress ÎL POATE înțelege, și anume mysite.ca/?pagename=page-slug&paged=#. Observă pagename și paged, nu name și page (ceea ce mi-a cauzat literalmente ORE de frustrare, motivând acest răspuns!).

Iată regula de redirecționare:

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

Ca întotdeauna, când modifici regulile de rescriere, nu uita să actualizezi permalink-urile vizitând Setări > Permalink-uri în panoul de administrare.

Dacă ai mai multe pagini care se vor comporta astfel (de exemplu, când lucrezi cu multiple tipuri de postări personalizate), poți dori să eviți crearea unei noi reguli de rescriere pentru fiecare slug de pagină. Putem scrie o expresie regulată mai generică care funcționează pentru orice slug de pagină identificat.

O abordare este prezentată mai jos:

function wpse_120407_pseudo_archive_rewrite(){
    // Adaugă slug-urile paginilor care folosesc un Șablon Global pentru a simula fi o pagină "arhivă"
    $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' );

Dezavantaje / Limitări

Un dezavantaj al acestei abordări, care mă face să am o ușoară greață, este hardcodarea slug-ului Paginii. Dacă un administrator schimbă vreodată slug-ul acelei pagini pseudo-arhivă, ești terminat - regula de rescriere nu se va mai potrivi și vei primi temutul 404.

Nu sunt sigur că pot găsi o soluție pentru această metodă, dar ar fi frumos dacă șablonul global de pagină ar declanșa cumva regula de rescriere. Poate într-o zi voi reveni la acest răspuns dacă nimeni nu a rezolvat această problemă până atunci.

17 dec. 2016 02:49:08
Comentarii

Puteți utiliza hook-ul pentru salvarea postării, verificați dacă pagina are șablonul dvs. de arhivă sub cheia meta _wp_page_template, apoi adăugați o altă rescriere și reîmprospătați regulile.

Milo Milo
17 dec. 2016 02:54:39
0

Am modificat interogarea principală a buclei prin query_posts(). De ce nu funcționează paginarea și cum o pot repara?

Răspunsul excelent creat de Chip trebuie actualizat astăzi.
De ceva vreme avem variabila $wp_the_query care ar trebui să fie egală cu globalul $wp_query imediat după execuția interogării principale.

De aceea, partea din răspunsul lui Chip:

Hack the main query object

nu mai este necesară. Putem uita de această parte cu crearea variabilei temporare.

// Fix pentru paginare
$temp_query = $wp_query;
$wp_query   = NULL;
$wp_query   = $custom_query;

Acum putem apela:

$wp_query   = $wp_the_query;

sau chiar mai bine putem apela:

wp_reset_query();

Toate celelalte aspecte prezentate de Chip rămân valabile. După acea parte de resetare a interogării, puteți apela funcțiile de paginare care sunt f($wp_query), — ele depind de globalul $wp_query.


Pentru a îmbunătăți în continuare mecanismul de paginare și pentru a oferi mai multă libertate funcției query_posts, am creat această posibilă îmbunătățire:

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

13 ian. 2017 23:21:24
0
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();
            //adaugă codul tău aici
        endwhile;
        wp_reset_query();

        //gestionează paginarea pe baza interogării personalizate
        $GLOBALS['wp_query']->max_num_pages = $query->max_num_pages;
        the_posts_pagination(array(
            'mid_size' => 1,
            'prev_text' => __('Pagina anterioară', 'patelextensions'),
            'next_text' => __('Pagina următoare', 'patelextensions'),
            'before_page_number' => '<span class="meta-nav screen-reader-text">' . __('Pagina', 'patelextensions') . ' </span>',
        ));
    else:
    ?>
        <div class="container text-center"><?php echo _d('Rezultate negăsite','30'); ?></div>
    <?php
        endif;
    ?>
6 mar. 2018 11:39:57