web-dev-qa-db-fra.com

Comment créer une page d'arrivée de catégorie suivie de pages de publications?

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

La clé ici est que je ne montre aucune publication de l'archive des catégories sur la première page. J'ai donc besoin de la page suivante pour commencer avec la première publication. J'ai d'abord essayé de le faire en utilisant offset et pagination manuelle en mettant l'offset de la requête à 0 si la requête 'paginée' était 2. Mais l'offset est ignoré s'il est mis à zéro, le mieux que je puisse faire est réglez le décalage sur 1 et commencez avec le deuxième poste de la catégorie.

C'est 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 qui apparaît Wired does, consiste à utiliser la pagination par défaut afin que les publications commencent à la page 1, mais insèrent 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é" vaut 0 pour les deux catégories/news/page/1 et/category/news.

Existe-t-il un moyen de vérifier si l'utilisateur a demandé/catégorie/actualités/page/1 par opposition à/catégorie/actualités? Sinon, y a-t-il moyen d'afficher tous les articles de la catégorie commençant à la page 2?

8
willcwelch

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

  • Vous ne pouvez pas définir la requête pour renvoyer des articles 0 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 ne contiendra toujours que le même nombre de publications, de sorte que la propriété $max_num_pages restera toujours la même.

Nous devrons en quelque sorte "tromper" la classe WP_Query pour renvoyer nos publications 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 dans le code. Avant de faire, je voudrais cependant soulever quelques notes ici

NOTES IMPORTANTES:

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

  • Le code nécessite au moins PHP 5.3. Toute version antérieure à 5.3 provoquera une erreur fatale. Remarque, si vous utilisez toujours une version inférieure à PHP 5.5, vous devriez déjà avoir effectué une mise à niveau depuis longtemps

  • Modifiez le code et utilisez-le à votre guise, selon vos besoins

L'idée de bulbe de lumière:

CE DONT WE SERA BESOIN

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

  • Le numéro de la page en cours de visualisation

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

  • Personnalisé offset

  • Modifier la propriété $found_posts de la requête pour corriger la propriété $max_num_pages

La pagination est WP_Query descend en 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'];
}

En gros, une fois le décalage défini explicitement, le paramètre paged est ignoré. Le premier paramètre de la clause SQL LIMIT est recalculé à partir de l'offset et correspond au nombre de publications à ignorer dans la requête SQL générée.

D'après votre question, apparemment, si vous définissez offset sur 0, la requête décalée é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 devriez déboguer le problème

Pour revenir au problème actuel, nous utiliserons le même type de logique pour calculer et régler notre décalage de manière à ce que le message 1 soit affiché à la page 2, puis paginer la requête. Pour la première page, nous ne modifierons rien, donc les posts supposés être à la page 1 seront toujours sur la page comme d'habitude, il suffirait de les "masquer" plus tard pour ne pas les afficher à 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 à partir de la page 1 à la page 2. Comme je l’ai déjà indiqué, 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 action pre_get_posts qui est essayez également de définir un décalage ou utilisez des filtres de clause posts_* (plus précisément, le filtre post_limits). Ce serait quelque chose que vous auriez besoin de déboguer vous-même.

Le problème suivant est de corriger la pagination. Comme je l’ai dit précédemment, votre page sera courte. Pour cela, nous devrons ajouter la valeur de get_option( 'posts_per_page' ) au nombre de publications trouvées dans la requête, car nous compensons la requête de ce montant. En faisant cela, nous ajoutons effectivement 1 à la propriété $max_num_pages.

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 aller 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 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 est de 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 catégorie news sera affichée. Ce modèle sera très simplement

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 constater, j’ai inclus deux parties de modèle, news-special.php et news-loop.php. Les bases des deux modèles personnalisés sont les suivantes:

  • news-special.php -> Cette partie du modèle correspondra à ce que vous souhaitez afficher sur la première page. Ajoutez toutes vos informations statiques personnalisées ici. Veillez à ne pas appeler la boucle dans ce modèle car cela afficherait les articles de la première page.

  • news-loop.php -> Ceci est le modèle dans lequel 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 filtre category_template pour utiliser ce modèle lorsque nous afficherons la première page de la catégorie news. Veillez également à ne pas appeler la boucle par défaut dans ce modèle. Assurez-vous également que votre convention de dénomination ne rencontre pas les noms de modèles dans la hiérarchie

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

MODIFIER

Grâce à l'OP, il existe un bogue bien défini dans la classe WP_Query: consultez le ticket trac n ° 34060 . Le code que j'ai posté provient de Wordpress v4.4 et le bogue est corrigé dans cette version.

Je suis retourné au code source de la v4.3, où se trouve le bogue, et je peux confirmer que 0 est ignoré lorsqu'il est défini comme valeur sur offset car le code vérifie simplement si le paramètre offset est empty. 0 est considéré comme vide en PHP. Je ne sais pas si ce problème (bogue) ne se trouve que dans la v4.3 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 billet de trac. Comme je l'ai dit, ce bug a été corrigé dans la v4.4

7
Pieter Goosen

Mettre à 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,
);
0
Kika