Come correggere l'impaginazione per i loop personalizzati?


122

Ho aggiunto una query personalizzata / secondaria a un file modello / modello pagina personalizzato; come posso fare in modo che WordPress usi la mia query personalizzata per l'impaginazione, anziché utilizzare l'impaginazione del ciclo di query principale?

appendice

Ho modificato la query del ciclo principale tramite query_posts(). Perché l'impaginazione non funziona e come posso risolverlo?

Risposte:


215

Il problema

Per impostazione predefinita, in qualsiasi contesto, WordPress utilizza la query principale per determinare l'impaginazione. L'oggetto query principale è archiviato nel $wp_queryglobale, che viene anche utilizzato per generare il ciclo di query principale:

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

Quando si utilizza una query personalizzata , si crea un oggetto query completamente separato:

$custom_query = new WP_Query( $custom_query_args );

E quella query viene emessa tramite un ciclo completamente separato:

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

Ma i tag modello di impaginazione, tra cui previous_posts_link(), next_posts_link(), posts_nav_link(), e paginate_links(), basano la loro uscita sul oggetto query principale , $wp_query. Quella query principale può o non può essere impaginata. Se il contesto corrente è un modello di pagina personalizzato, ad esempio, l' $wp_queryoggetto principale sarà costituito da un solo post : quello dell'ID della pagina a cui è assegnato il modello di pagina personalizzato.

Se il contesto corrente è un indice di archivio di qualche tipo, il principale $wp_querypuò essere costituito da post sufficienti per causare l'impaginazione, il che porta alla parte successiva del problema: per l' $wp_queryoggetto principale , WordPress passerà un paged parametro alla query, in base al pagedVariabile di query URL. Quando la query viene recuperata, quel pagedparametro verrà utilizzato per determinare quale set di post impaginati da restituire. Se si fa clic su un collegamento di impaginazione visualizzato e viene caricata la pagina successiva, la query personalizzata non avrà modo di sapere che l'impaginazione è cambiata .

La soluzione

Passando il parametro impaginato corretto alla query personalizzata

Supponendo che la query personalizzata utilizzi un array args:

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

Sarà necessario passare il pagedparametro corretto all'array. Puoi farlo recuperando la variabile della query URL utilizzata per determinare la pagina corrente, tramite get_query_var():

get_query_var( 'paged' );

È quindi possibile aggiungere quel parametro alla matrice di args query personalizzata:

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

Nota: se la tua pagina è una prima pagina statica , assicurati di utilizzare pageinvece di pagedcome usa una prima pagina statica pagee non paged. Questo è ciò che dovresti avere per una prima pagina statica

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

Ora, quando viene recuperata la query personalizzata, verrà restituito il set corretto di post impaginati.

Utilizzo dell'oggetto query personalizzato per le funzioni di impaginazione

Affinché le funzioni di impaginazione producano l'output corretto, ovvero i collegamenti precedente / successivo / della pagina relativi alla query personalizzata, WordPress deve essere costretto a riconoscere la query personalizzata. Ciò richiede un po 'di un "hack": la sostituzione del principale $wp_queryoggetto con l'oggetto query personalizzata, $custom_query:

Hack l'oggetto di query principale

  1. Eseguire il backup dell'oggetto query principale: $temp_query = $wp_query
  2. Annullare l'oggetto query principale: $wp_query = NULL;
  3. Scambia la query personalizzata nell'oggetto query principale: $wp_query = $custom_query;

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

Questo "hack" deve essere eseguito prima di chiamare qualsiasi funzione di impaginazione

Reimpostare l'oggetto query principale

Una volta emesse le funzioni di impaginazione, reimpostare l'oggetto query principale:

$wp_query = NULL;
$wp_query = $temp_query;

Correzioni della funzione di impaginazione

La previous_posts_link()funzione funzionerà normalmente, indipendentemente dall'impaginazione. Determina semplicemente la pagina corrente e quindi genera il collegamento per page - 1. Tuttavia, è necessaria una correzione per next_posts_link()l'output corretto. Questo perché next_posts_link()utilizza il max_num_pagesparametro:

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

Come con altri parametri di query, per impostazione predefinita la funzione utilizzerà max_num_pagesper l' $wp_queryoggetto principale . Per forzare la next_posts_link()contabilizzazione $custom_querydell'oggetto, dovrai passare max_num_pagesalla funzione. È possibile recuperare questo valore $custom_querydall'oggetto $custom_query->max_num_pages::

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

Mettere tutto insieme

Di seguito è riportato un costrutto di base di un ciclo di query personalizzato con funzioni di impaginazione correttamente funzionanti:

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

Addendum: che dire query_posts()?

query_posts() per loop secondari

Se stai usando query_posts()per generare un ciclo personalizzato, piuttosto che creare un'istanza di un oggetto separato per la query personalizzata tramite WP_Query(), allora sei _doing_it_wrong(), e incontrerai diversi problemi (non ultimo i problemi di impaginazione). Il primo passo per risolvere questi problemi sarà convertire l'uso improprio query_posts()in una WP_Query()chiamata corretta .

Utilizzo query_posts()per modificare il loop principale

Se desideri semplicemente modificare i parametri per la query del ciclo principale , ad esempio modificando i post per pagina o escludendo una categoria, potresti essere tentato di utilizzare query_posts(). Ma non dovresti ancora. Quando si utilizza query_posts(), si forza WordPress a sostituire l'oggetto query principale. (WordPress in realtà esegue una seconda query e sovrascrive $wp_query.) Il problema, tuttavia, è che esegue questa sostituzione troppo tardi nel processo per aggiornare l'impaginazione.

La soluzione è di filtrare la query principale prima che i post vengano recuperati , tramite l' pre_get_postshook.

Invece di aggiungere questo al file modello categoria ( category.php):

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

Aggiungi quanto segue a 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' );

Invece di aggiungere questo al file modello indice dei post di blog ( home.php):

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

Aggiungi quanto segue a 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' );

In questo modo, WordPress utilizzerà l' $wp_queryoggetto già modificato durante la determinazione dell'impaginazione, senza che sia necessario modificare il modello.

Quando utilizzare quale funzione

La ricerca questa domanda e risposta e questa domanda e risposta per capire come e quando usare WP_Query, pre_get_postse query_posts().


31
Le pagine dei desideri nel codice potrebbero essere così complete.
Pieter Goosen,

Chip, mi hai reso felice!
tepkenvannkorn,

1
Chip, risparmi sempre così tanto tempo! se solo Google classificasse le risposte più in alto (callout per i googler) prima che impazzissi a cercare;) grazie.
Sagive SEO,

Usando il tuo esempio non sono riuscito a far funzionare il paging fino a quando non ho usato un blocco if-else come trovato a metà strada (invece del? Condizionale) in questa pagina: themeforest.net/forums/thread/… , molto strano. Altrimenti questa risposta mi ha insegnato molto.
P

2
Ottima risposta - 1 cosa, stavo riscontrando problemi nell'esecuzione della funzione post link precedente / successivo in una chiamata Ajax - semplicemente non ci sarebbe voluto - dopo una rapida ricerca ho scoperto che il globale pagednon veniva aggiornato (obv qualcosa a che fare con admin- ajax.php) quindi ho aggiunto questo: global $paged; $paged = $custom_query_args['paged']; e ha funzionato :)
acSlater il

21

Uso questo codice per il loop personalizzato con l'impaginazione:

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

Fonte:


1
Un altro bel tutorial con una variante di questa risposta: callmenick.com/post/custom-wordpress-loop-with-pagination
mrwweb,

5

Fantastico come sempre Chip. Come aggiunta a questo, considera la situazione in cui stai usando un modello di pagina globale allegato a una Pagina per un po 'di "testo introduttivo" ed è seguito da una sottoquery che vuoi fare il paging.

Usando paginate_links () come accennato in precedenza, con impostazioni predefinite per lo più (e supponendo che tu abbia abbastanza permalink attivati) i tuoi collegamenti di impaginazione saranno predefiniti su mysite.ca/page-slug/page/#cui è adorabile ma genereranno 404errori perché WordPress non conosce quella particolare struttura URL e in realtà cerca una pagina figlio di "pagina" che è una figlia di "page-slug".

Il trucco qui è quello di inserire una regola di riscrittura elegante che si applica solo a quella particolare pagina "pagina pseudo archivio" che accetta la /page/#/struttura e la riscrive in una stringa di query che WordPress PUO 'capire, vale a dire mysite.ca/?pagename=page-slug&paged=#. Nota pagenamee pagednon namee page(che mi ha causato letteralmente ORE di dolore, motivando questa risposta qui!).

Ecco la regola di reindirizzamento:

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

Come sempre, quando si modificano le regole di riscrittura, ricordarsi di svuotare i permalink visitando Impostazioni> Permalink nel back-end di amministrazione.

Se hai più pagine che si comporteranno in questo modo (ad esempio, quando hai a che fare con più tipi di post personalizzati), potresti voler evitare di creare una nuova regola di riscrittura per ogni lumaca di pagina. Siamo in grado di scrivere un'espressione regolare più generica che funziona per qualsiasi lumaca di pagina identificata.

Un approccio è di seguito:

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' );

Svantaggi / Avvertenze

Uno svantaggio di questo approccio che mi fa vomitare un po 'in bocca è la codifica rigida della lumaca di Page. Se un amministratore cambia mai la lumaca di pagina di quella pagina pseudo-archivio, sei un brindisi: la regola di riscrittura non corrisponderà più e otterrai il temuto 404.

Non sono sicuro di pensare a una soluzione alternativa per questo metodo, ma sarebbe bello se fosse il modello di pagina globale che in qualche modo ha innescato la regola di riscrittura. Un giorno potrei rivisitare questa risposta se nessun altro ha rotto quel dado in particolare.


1
Puoi agganciare il salvataggio dei post, verificare se la pagina ha il tuo modello di archivio sotto la meta chiave _wp_page_template, quindi aggiungere un'altra regola di riscrittura e flush.
Milo,

2

Ho modificato la query del ciclo principale tramite query_posts(). Perché l'impaginazione non funziona e come posso risolverlo?

Ottima risposta Il chip creato deve essere modificato oggi.
Da qualche tempo abbiamo una $wp_the_queryvariabile che dovrebbe essere uguale al $wp_queryglobale subito dopo l'esecuzione della query principale.

Questo è il motivo per cui questa parte della risposta del Chip:

Hack l'oggetto di query principale

non è più necessario. Possiamo dimenticare questa parte con la creazione della variabile temporanea.

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

Quindi ora possiamo chiamare:

$wp_query   = $wp_the_query;

o meglio ancora possiamo chiamare:

wp_reset_query();

Tutto il resto del Chip descritto rimane. Dopo quella parte di query-reset è possibile chiamare le funzioni di impaginazione che sono f($wp_query), - dipendono dal $wp_queryglobale.


Al fine di migliorare ulteriormente la meccanica di impaginazione e dare più libertà alla query_postsfunzione ho creato questo possibile miglioramento:

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;
    ?>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.