it-swarm.com.de

Wie behebe ich die Paginierung für benutzerdefinierte Schleifen?

Ich habe einer Vorlagendatei/benutzerdefinierten Seitenvorlage eine benutzerdefinierte/sekundäre Abfrage hinzugefügt. Wie kann ich WordPress veranlassen, meine benutzerdefinierte Abfrage für die Paginierung zu verwenden, anstatt die Paginierung der Hauptabfrageschleife zu verwenden?

Nachtrag

Ich habe die Hauptschleifenabfrage über query_posts() geändert. Warum funktioniert die Paginierung nicht und wie behebe ich sie?

119
Chip Bennett

Das Problem

Standardmäßig verwendet WordPress in einem bestimmten Kontext die Hauptabfrage, um die Paginierung zu bestimmen. Das Hauptabfrageobjekt wird in der globalen $wp_query gespeichert, die auch zur Ausgabe der Hauptabfrageschleife verwendet wird:

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

Wenn Sie eine benutzerdefinierte Abfrage verwenden , erstellen Sie ein völlig separates Abfrageobjekt:

$custom_query = new WP_Query( $custom_query_args );

Und diese Abfrage wird über eine völlig separate Schleife ausgegeben:

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

Tags für Paginierungsvorlagen, einschließlich previous_posts_link() , next_posts_link() , posts_nav_link() und paginate_links() , basieren jedoch auf der Ausgabe von Hauptabfrageobjekt, $wp_query. Diese Hauptabfrage kann paginiert sein oder nicht. Wenn der aktuelle Kontext beispielsweise eine benutzerdefinierte Seitenvorlage ist, besteht das Hauptobjekt $wp_query nur aus einem einzelner Beitrag - der ID der Seite, der die benutzerdefinierte Seitenvorlage zugewiesen ist.

Wenn es sich bei dem aktuellen Kontext um einen Archivindex handelt, kann der Hauptparameter $wp_query aus genügend Beiträgen bestehen, um eine Paginierung zu verursachen. Dies führt zum nächsten Teil des Problems: Für das Hauptobjekt $wp_query übergibt WordPress einen Parameter pagedan die Abfrage. basierend auf der URL-Abfragevariablen pagedname__. Wenn die Abfrage abgerufen wird, wird mit dem Parameter pagedbestimmt, welche Gruppe von paginierten Posts zurückgegeben werden soll. Wenn auf einen angezeigten Paginierungslink geklickt und die nächste Seite geladen wird, Ihre benutzerdefinierte Abfrage kann nicht erkennen, dass sich die Paginierung geändert hat.

Die Lösung

Übergeben des korrekten Seitenparameters an die benutzerdefinierte Abfrage

Angenommen, die benutzerdefinierte Abfrage verwendet ein args-Array:

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

Sie müssen den korrekten Parameter pagedan das Array übergeben. Sie können dies tun, indem Sie die URL-Abfragevariable abrufen, mit der die aktuelle Seite ermittelt wurde, und zwar über get_query_var() :

get_query_var( 'paged' );

Sie können diesen Parameter dann an Ihr benutzerdefiniertes query args-Array anhängen:

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

Hinweis: Wenn Ihre Seite eine statische Titelseite ist, verwenden Sie pageanstelle von pagedals statische Titelseite pageund nicht pagedname__. Dies ist, was Sie für eine statische Titelseite haben sollten

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

Wenn die benutzerdefinierte Abfrage abgerufen wird, wird der richtige Satz von paginierten Beiträgen zurückgegeben.

Verwenden eines benutzerdefinierten Abfrageobjekts für Paginierungsfunktionen

Damit die Paginierungsfunktionen die richtige Ausgabe liefern - d. H. Vorherige/nächste/Seitenlinks relativ zur benutzerdefinierten Abfrage - muss WordPress gezwungen werden, die benutzerdefinierte Abfrage zu erkennen. Dies erfordert ein bisschen "Hack": Ersetzen des Hauptobjekts $wp_query durch das benutzerdefinierte Abfrageobjekt $custom_query:

Hacken Sie das Hauptabfrageobjekt

  1. Sichern Sie das Hauptabfrageobjekt: $temp_query = $wp_query
  2. Setzen Sie das Hauptabfrageobjekt auf Null: $wp_query = NULL;
  3. Tauschen Sie die benutzerdefinierte Abfrage in das Hauptabfrageobjekt aus: $wp_query = $custom_query;

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

Dieser "Hack" muss durchgeführt werden bevor Paginierungsfunktionen aufgerufen werden

Setzen Sie das Hauptabfrageobjekt zurück

Nachdem die Paginierungsfunktionen ausgegeben wurden, setzen Sie das Hauptabfrageobjekt zurück:

$wp_query = NULL;
$wp_query = $temp_query;

Korrekturen der Paginierungsfunktion

Die Funktion previous_posts_link() funktioniert unabhängig von der Paginierung normal. Sie ermittelt lediglich die aktuelle Seite und gibt dann den Link für page - 1 aus. Es ist jedoch ein Fix erforderlich, damit next_posts_link() ordnungsgemäß ausgegeben werden kann. Dies liegt daran, dass next_posts_link() den Parameter max_num_pages verwendet:

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

Wie bei anderen Abfrageparametern verwendet die Funktion standardmäßig max_num_pages für das Hauptobjekt $wp_query. Um zu erzwingen, dass next_posts_link() das Objekt $custom_query berücksichtigt, müssen Sie den max_num_pages an die Funktion übergeben. Sie können diesen Wert aus dem Objekt $custom_query abrufen: $custom_query->max_num_pages:

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

Alles zusammen

Das Folgende ist ein grundlegendes Konstrukt einer benutzerdefinierten Abfrageschleife mit ordnungsgemäß funktionierenden Paginierungsfunktionen:

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

Nachtrag: Was ist mit query_posts()?

query_posts() für Sekundärschleifen

Wenn Sie mit query_posts() eine benutzerdefinierte Schleife ausgeben und stattdessen ein separates Objekt für die benutzerdefinierte Abfrage über WP_Query() instanziieren, sind Sie _doing_it_wrong() und haben mehrere Probleme ( nicht das am wenigsten von dem Paginierungsprobleme sein werden). Der erste Schritt zur Behebung dieser Probleme besteht darin, die unsachgemäße Verwendung von query_posts() in einen ordnungsgemäßen WP_Query() -Aufruf umzuwandeln.

Verwenden von query_posts() zum Ändern der Hauptschleife

Wenn Sie lediglich die Parameter für die Hauptschleifenabfrage ändern möchten, z. B. die Beiträge pro Seite ändern oder eine Kategorie ausschließen möchten, sind Sie möglicherweise versucht, query_posts() zu verwenden. Aber du solltest es trotzdem nicht. Wenn Sie query_posts() verwenden, erzwingen Sie, dass WordPress das Hauptabfrageobjekt ersetzen. (WordPress führt tatsächlich eine zweite Abfrage durch und überschreibt $wp_query.) Das Problem ist jedoch, dass diese Ersetzung zu spät erfolgt, um die Paginierung zu aktualisieren.

Die Lösung besteht darin, die Hauptabfrage zu filtern, bevor Beiträge abgerufen werden über den Hook pre_get_posts.

Anstatt dies der Kategorie-Vorlagendatei (category.php) hinzuzufügen:

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

Fügen Sie functions.php Folgendes hinzu:

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

Anstatt dies zur Index-Vorlagendatei für Blog-Posts hinzuzufügen (home.php):

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

Fügen Sie functions.php Folgendes hinzu:

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

Auf diese Weise verwendet WordPress das bereits geänderte Objekt $wp_query, um die Paginierung zu bestimmen, ohne dass eine Änderung der Vorlage erforderlich ist.

Wann soll welche Funktion verwendet werden?

Erforschen Sie diese Frage und Antwort und diese Frage und Antwort , um zu verstehen, wie und wann Sie WP_Query , pre_get_posts und query_posts() verwenden sollen.

205
Chip Bennett

Ich benutze diesen Code für eine benutzerdefinierte Schleife mit Paginierung:

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

Quelle:

21
webvitaly

Genial wie immer Chip. Betrachten Sie als Ergänzung dazu die Situation, in der Sie eine globale Seitenvorlage verwenden, die an eine Seite für einen "Intro-Text" angehängt ist, und auf die eine Unterabfrage folgt, die ausgelagert werden soll.

Wenn Sie paginate_links () verwenden (und davon ausgehen, dass Sie hübsche Permalinks aktiviert haben), werden Ihre Paginierungslinks standardmäßig auf mysite.ca/page-slug/page/# gesetzt, was sehr schön ist, aber 404-Fehler auslöst, da WordPress nichts davon weiß bestimmte URL-Struktur und sucht tatsächlich nach einer untergeordneten Seite von "page", die ein untergeordnetes Element von "page-slug" ist.

Der Trick dabei ist, eine raffinierte Umschreiberegel einzufügen, die nur für diesen bestimmten "Pseudo-Archivseite" -Seiten-Slug gilt, der die /page/#/-Struktur akzeptiert, und sie in eine Abfragezeichenfolge umzuschreiben, die WordPress KANN, nämlich mysite.ca/?pagename=page-slug&paged=#. Beachten Sie pagename und paged nicht name und page (was mich buchstäblich STUNDEN Trauer verursachte und diese Antwort hier motivierte!).

Hier ist die Weiterleitungsregel:

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

Denken Sie beim Ändern der Umschreiberegeln wie immer daran, Ihre Permalinks zu löschen , indem Sie im Admin-Backend auf Einstellungen> Permalinks klicken.

Wenn Sie mehrere Seiten haben, die sich auf diese Weise verhalten (z. B. wenn Sie mit mehreren benutzerdefinierten Beitragstypen arbeiten), möchten Sie möglicherweise nicht für jeden Seitenblock eine neue Umschreiberegel erstellen. Wir können einen allgemeineren regulären Ausdruck schreiben, der für jeden von Ihnen identifizierten Seitenblock funktioniert.

Ein Ansatz ist unten:

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

Nachteile/Vorbehalte

Ein Nachteil dieses Ansatzes, der mich ein wenig in den Mund kotzen lässt, ist die harte Kodierung der Seitenschnecke. Wenn ein Administrator jemals den Seitenbuckel dieser Pseudo-Archiv-Seite ändert, wird getoastet - die Umschreiberegel stimmt nicht mehr überein und Sie erhalten die gefürchtete 404.

Ich bin nicht sicher, ob ich mir eine Problemumgehung für diese Methode vorstellen kann, aber es wäre schön, wenn die globale Seitenvorlage die Regel zum Umschreiben auslösen würde. Eines Tages werde ich vielleicht noch einmal auf diese Antwort zurückkommen, wenn niemand anders diese bestimmte Nuss geknackt hat.

5
Tom Auger

Ich habe die Hauptschleifenabfrage über query_posts() geändert. Warum funktioniert die Paginierung nicht und wie behebe ich sie?

Großartige Antwort Der erstellte Chip muss heute geändert werden.
Seit einiger Zeit haben wir die Variable $wp_the_query, die gleich dem globalen $wp_query sein sollte, unmittelbar nachdem die Hauptabfrage ausgeführt wurde.

Dies ist der Teil aus der Antwort des Chips:

Hacken Sie das Hauptabfrageobjekt

wird nicht mehr benötigt. Wir können diesen Teil beim Erstellen der temporären Variablen vergessen.

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

Jetzt können wir anrufen:

$wp_query   = $wp_the_query;

oder noch besser können wir anrufen:

wp_reset_query();

Alles andere, was Chip umrissen hat, bleibt. Nach diesem Query-Reset-Teil können Sie die Paginierungsfunktionen aufrufen, die f($wp_query) sind, - sie hängen von $wp_query global ab.


Um die Paginierungsmechanik weiter zu verbessern und der query_posts -Funktion mehr Freiheit zu geben, habe ich diese mögliche Verbesserung geschaffen:

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

2
prosti
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;
    ?>
1
ravi patel