Comment puis-je créer une page de destination de catégorie suivie de pages de publications?

9

J'essaie de créer un modèle de catégorie dans mon thème qui affichera du contenu statique si l'utilisateur se trouve sur la première page de l'archive, puis affiche les messages de la catégorie sur les pages suivantes. J'essaie essentiellement de reproduire le comportement de la catégorie sur Wired.com où http://www.wired.com/category/design est une page de destination tandis que / category / design / page / 1 affiche une liste chronologique inversée de messages comme vous attendrait sur une archive de catégorie.

La clé ici est que je n'affiche aucun post de l'archive de catégorie sur la première page, j'ai donc besoin de la page suivante pour commencer avec le premier post. J'ai d'abord essayé de le faire en utilisant l' offset et la pagination manuelle en définissant l'offset de la requête à 0 si la variable de requête «paginée» était 2. Mais l'offset est ignoré lorsqu'il est défini sur zéro, donc le mieux que je puisse faire est de définir l'offset sur 1 et commencez par le deuxième poste de la catégorie.

Voici ce que j'ai ajouté à functions.php:

add_action('pre_get_posts', 'category_query_offset', 1 );
function category_query_offset(&$query) {
  // Before anything else, make sure this is the right query
  if (!$query->is_category('news')) {
    return;
  }

  // Next, determine how many posts per page
  $ppp = get_option('posts_per_page');

  //Next, detect and handle pagination
  if ($query->is_paged) {
    // Manually determine page query offset
    $page_offset = (($query->query_vars['paged']-1) * $ppp) - $ppp;
    // Apply adjust page offset
    $query->set('offset', $page_offset );
  }
}

Ce qui serait mieux, et ce que semble faire Wired , c'est d'utiliser la pagination par défaut pour que les articles commencent à la page 1, mais insérez une page 0 pour le contenu statique. En théorie, cela semble possible en affichant le contenu statique si «paginé» vaut 0 et en affichant la première page de messages si «paginé» vaut 1. Le problème est que «paginé» n'est jamais 1 car Wordpress définit «paginé» 'à zéro lorsque l'utilisateur demande la première page. Cela signifie que «paginé» est égal à 0 pour / category / news / page / 1 et / category / news.

Existe-t-il un moyen de vérifier si l'utilisateur a demandé / category / news / page / 1 par opposition à / category / news? À défaut, existe-t-il un moyen d'afficher tous les messages de la catégorie à partir de la page 2?

willcwelch
la source

Réponses:

7

C'est une question assez intéressante ( que j'ai surévaluée, spécialement pour votre approche et vos recherches ). La grosse courbe est ici la première page de la requête:

  • Vous ne pouvez pas définir la requête pour renvoyer des 0articles sur la première page

  • En déplaçant le contenu de chaque page d'une page vers le haut, vous perdrez la dernière page car la requête n'aura toujours que le même nombre de publications, de sorte que la $max_num_pagespropriété restera toujours la même.

Nous devrons en quelque sorte "tromper" la WP_Queryclasse pour renvoyer nos messages correctement avec le décalage d'une page, et également obtenir le nombre correct de pages afin de ne pas perdre la dernière page de la requête

Regardons l'idée suivante et essayons de tout mettre en code. Avant de le faire, je voudrais cependant soulever quelques notes ici

NOTES IMPORTANTES:

  • Tout n'est pas testé, il pourrait donc être bogué. Assurez-vous de tester cela localement avec le débogage activé

  • Le code nécessite au moins PHP 5.3, toute version inférieure à 5.3 entraînera une erreur fatale. Remarque, si vous utilisez toujours une version inférieure à PHP 5.5, vous devriez déjà avoir mis à niveau il y a longtemps

  • Modifiez et abusez du code comme bon vous semble pour répondre à vos besoins exacts

L'IDÉE DE L'AMPOULE:

CE DONT NOUS AURONS BESOIN

Pour que tout fonctionne, nous aurons besoin des éléments suivants:

  • Le numéro de page en cours d'affichage

  • L' posts_per_pageoption définie dans les paramètres de lecture

  • Douane offset

  • Modifier la $found_postspropriété de la requête pour corriger la $max_num_pagespropriété

La pagination se WP_Queryrésume à quelques lignes de code très simples

if ( empty($q['nopaging']) && !$this->is_singular ) {
    $page = absint($q['paged']);
    if ( !$page )
        $page = 1;
    // If 'offset' is provided, it takes precedence over 'paged'.
    if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) ) {
        $q['offset'] = absint( $q['offset'] );
        $pgstrt = $q['offset'] . ', ';
    } else {
        $pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
    }
    $limits = 'LIMIT ' . $pgstrt . $q['posts_per_page'];
}

Ce qui se passe essentiellement, une fois qu'un décalage est défini explicitement, le pagedparamètre est ignoré. Le premier paramètre de la LIMITclause SQL est recalculé à partir de l'offset et sera le nombre de publications à ignorer dans la requête SQL générée.

D'après votre question, apparemment lors de la définition offsetde 0, la requête de décalage échoue, ce qui est étrange, car la vérification suivante doit renvoyer true

if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) )

0est un nombre valide et doit renvoyer true. Si cela ne fonctionne pas sur votre installation, vous devez déboguer le problème

Pour revenir au problème, nous allons utiliser le même type de logique pour calculer et définir notre décalage pour obtenir le post 1 à la page 2 et à partir de là paginer la requête. Pour la première page, nous ne modifierons rien, donc les articles supposés être sur la page 1 seront toujours sur la page comme d'habitude, nous aurions juste besoin de les "cacher" plus tard afin de ne pas les afficher sur la page 1

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $current_page = $q->get( 'paged' ); // Get the current page number
        // We will only need to run this from page 2 onwards
        if ( $current_page != 0 ) { // You can also use if ( is_paged() ) {
            // Get the amount of posts per page
            $posts_per_page = get_option( 'posts_per_page' );
            // Recalculate our offset
            $offset = ( ( $current_page - 1) * $posts_per_page ) - $posts_per_page; // This should work on page 2 where it returns 0

            // Set our offset
            $q->set( 'offset', $offset );
        }
    }
});

Vous devriez voir les mêmes messages de la page 1 à la page 2. Comme je l'ai déjà dit, si cela ne se produit pas, soit is_numeric( 0 )renvoie false ( ce qui ne devrait pas être le cas ) ou vous avez une autre pre_get_postsaction qui essaie également de définir un décalage ou vous utilisent en utilisant les posts_*filtres de clause ( plus précisément, le post_limitsfiltre ). Ce serait quelque chose que vous auriez besoin de déboguer par vous-même.

Le prochain problème est de corriger la pagination, car comme je l'ai dit précédemment, vous aurez une page courte. Pour cela, nous devrons ajouter la valeur de get_option( 'posts_per_page' )à la quantité de messages trouvés dans la requête car nous compensons la requête de ce montant. ce faisant, nous ajoutons effectivement 1à la $max_num_pagespropriété.

add_action( 'found_posts', function ( $found_posts, $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $found_posts = $found_posts + get_option( 'posts_per_page');
    }
}, 10, 2 );

Cela devrait tout trier, sauf la première page.

TOUS ENSEMBLE MAINTENANT ( et spécialement pour @ialocin - Yellow Submarine )

Tout cela devrait entrer dans functions.php

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $current_page = $q->get( 'paged' ); // Get the current page number
        // We will only need to run this from page 2 onwards
        if ( $current_page != 0 ) { // You can also use if ( is_paged() ) {
            // Get the amount of posts per page
            $posts_per_page = get_option( 'posts_per_page' );
            // Recalculate our offset
            $offset = ( ( $current_page - 1) * $posts_per_page ) - $posts_per_page; // This should work on page 2 where it returns 0

            // Set our offset
            $q->set( 'offset', $offset );
        }
    }
});

add_filter( 'found_posts', function ( $found_posts, $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $found_posts = $found_posts + get_option( 'posts_per_page');
    }
    return $found_posts;
}, 10, 2 );

OPTIONS DE LA PREMIÈRE PAGE

Il y a quelques options ici:

OPTION 1

J'irais très probablement pour cette option. Ce que vous voudriez faire ici, c'est créer un category-news.php( si vous ne l'avez pas encore fait ). Ce sera le modèle qui sera utilisé chaque fois que la newscatégorie sera consultée. Ce modèle sera très simple

Exemple

<?php
get_header()

if ( !is_paged() ) { // This is the first page
    get_template_part( 'news', 'special' );
} else { // This is not the first page
    get_template_part( 'news', 'loop' );
}

get_sidebar();
get_footer();

Comme vous pouvez le voir, j'ai inclus deux parties de modèle, news-special.phpet news-loop.php. Maintenant, les bases des deux modèles personnalisés sont:

  • news-special.php-> Cette partie du modèle sera tout ce que vous souhaitez afficher sur la première page. Ajoutez toutes vos informations statiques personnalisées ici. Soyez très prudent de ne pas appeler la boucle dans ce modèle car cela afficherait les messages de la première page.

  • news-loop.php-> C'est le modèle où nous invoquerons la boucle. Cette section ressemblera à ceci:

    global $wp_query;
    while ( have_posts() ) {
    the_post();
    
        // Your template tags and markup
    
    }

OPTION 2

Créez un modèle séparé avec votre contenu statique et utilisez simplement le category_templatefiltre pour utiliser ce modèle lorsque nous affichons la première page de la newscatégorie. Veillez également à ne pas invoquer la boucle par défaut dans ce modèle. Assurez-vous également que votre convention de dénomination ici n'entre pas en collision avec les noms de modèle dans la hiérarchie des modèles

J'espère que c'est utile. N'hésitez pas à laisser des commentaires avec inquiétudes

ÉDITER

Grâce à l'OP, il y a un bug certain dans la WP_Queryclasse, vérifiez le ticket trac # 34060 . Le code que j'ai publié provient de Wordpress v4.4, et le bug est corrigé dans cette version.

Je suis retourné au code source de la v4.3, où se trouve le bogue, et je peux confirmer qu'il 0est ignoré lorsqu'il est défini comme valeur offsetcar le code vérifie simplement si le offsetparamètre est empty. 0est considéré comme vide en PHP. Je ne sais pas si ce comportement (bogue) ne se trouve que dans la v4.3 uniquement ou dans toutes les versions précédentes (selon le ticket, ce bogue est dans la v4.3), mais il existe un correctif pour ce bogue que vous pouvez vérifier dans le ticket trac. Comme je l'ai dit, ce bug a définitivement été corrigé dans la v4.4

Pieter Goosen
la source
2
Merci pour votre réponse détaillée. C'est en fait à peu près ce que j'ai implémenté lors de ma première tentative. Le problème était que le réglage de «décalage» à 0 ne fonctionnait pas. Il s'avère qu'il s'agissait en fait d'un bogue dans WordPress Core core.trac.wordpress.org/ticket/34060 . Le patcher a tout fait fonctionner. La seule chose qui manque à votre code est return $found_posts;après l'instruction if dans l'action found_posts. Merci!
willcwelch
Merci pour vos retours positifs. Je suis retourné au code source de la version 4.3 query.php, et oui, 0il sera ignoré car la vérification ici est vide et 0est considérée comme vide. Le code dans ma réponse est le code de mise à jour fourni avec la v4.4, donc je peux confirmer que le bogue est corrigé dans 4.4. Je mettrai à jour ma réponse en conséquence. Sur la found_postsquestion, bonne prise, c'est en fait un filtre et doit être retourné. Profitez
Pieter Goosen
haha, merci :) et pour tous ceux qui se demandent pourquoi ... un, deux, trois, quatre, puis-je avoir un peu plus ... le lien réel vers la chanson tous ensemble maintenant
Nicolai
@ialocin hahaha, je me souviendrai de celui-ci ;-)
Pieter Goosen
1
c'est assez accrocheur :) mais pas étonnant, car c'est à peu près écrit comme une chanson pour enfants
Nicolai
0

Mise à jour:

add_action('pre_get_posts', 'category_query_offset', 1 );
function category_query_offset($query) {
 // Before anything else, make sure this is the right query
 if (!$query->is_category('news')) {
    return;
 }

 // Next, determine paged
 $ppp = get_option('paged');
}
// Next, handle paged
if($ppp > 1){
    $query->set('paged', $ppp-1);
}

Essayez quelque chose comme

$paged = ( get_query_var('paged') ) ? get_query_var('paged')-1 : 1;
$args = array (
    'paged' => $paged,
);
Kika
la source
J'ai corrigé mon explication du fonctionnement de la variable «paginée». Il semble que ce que vous essayez de faire soit de réduire chaque numéro de page de 1, donc une demande de page 2 vous donne la page 1. Le problème est que lorsque la page 1 est interrogée, «paginée» est définie sur 0. En outre, get_query_var () doit être utilisé à la place de get_option pour 'paged'. Avec ce correctif, lorsque cela s'exécute, chaque fois que la requête est modifiée, elle boucle à nouveau jusqu'à ce que «paginé» soit égal à 0 pour toute demande de page.
willcwelch du