web-dev-qa-db-fra.com

Exclure les 5 premiers postes de catégories spécifiques dans la boucle principale

J'ai lu cet article sur la façon de définir un décalage pour une catégorie spécifique de la boucle. Je me demandais si cela était possible pour plus d'une catégorie.

Mon problème avec le code suggéré dans l'article lié ci-dessus est que j'utilise un seul widget pour afficher cinq publications d'une catégorie que je peux définir dans les options du widget par ID. Je ne peux pas comprendre comment collecter les post_IDs des publications déjà bouclées.

Situation:

J'utilise des widgets personnalisés sur la page de mon blog pour afficher les 5 derniers articles dans deux catégories spécifiques: appelons-les categoryA et categoryB.

Répondez à la question de @Pieter Goosen dans les commentaires: Oui, je peux spécifier la catégorie en saisissant manuellement un ID dans les paramètres du widget et il est enregistré dans la base de données.

En dessous de ces widgets, la page de blog parcourt tous mes messages et les affiche.

Effet:

La boucle principale de la page du blog (celle située sous les widgets) affiche à nouveau les messages de la catégorie A et de la catégorie B. Ils apparaissent en double car les widgets ci-dessus ont déjà parcouru les catégories A et B.

Question:

Est-il possible d'exclure les cinq premiers postes des catégories A et B dans la boucle principale? Pourrait-il être fait avec pre_get_posts? Utiliser get_query_var? Comment?

EDIT

Mes propres approches

PREMIER ESSAI

J'ai modifié mes codes de widgets comme ceci:

global $do_not_duplicate;
while($featured_posts->have_posts()): $featured_posts->the_post(); 
    $do_not_duplicate[] = get_the_ID();

Comme mes widgets sont en cours d’enregistrement dans mon fichier functions.php, j’y ai ajouté les lignes suivantes:

global $do_not_duplicate;
$do_not_duplicate = array();
include('widgets/widgets.php');

Enfin sur le index.php, j'ai ajouté cette ligne à la boucle principale:

if(have_posts()) :
    global $do_not_duplicate;
    while(have_posts()): the_post();
        if( in_array( $post->ID, $do_not_duplicate ) ) continue;
        get_template_part('content');
    endwhile;
endif;

Remarque: Cela fonctionne fondamentalement mais ne supprime pas exactement les publications de la requête principale, mais les ignore. Cela signifie que le paramètre par défaut posts_per_page ne sera pas rempli et que cela ne fonctionnera pas avec le défilement infini de Jetpack. En outre, l'utilisation de globals n'est pas la solution.

SECOND TRY

J'ai adapté la suggestion de @birgire et l'ai modifiée un peu. J'ai pensé utiliser le filtre pre_get_posts suggéré en combinaison avec une boucle foreach pour les catégories utilisées par les widgets afin de collecter les identifiants des publications interrogées par ceux-ci. Voici mon extrait:

add_action('pre_get_posts', 'modified_mainloop', 10);
function modified_mainloop($query) {
    if(!is_admin() && $query->is_home() && $query->is_main_query()) {
        $cats_to_exclude = get_categories('include=3,4,9');
        foreach($cats_to_exclude as $category) {
            if($category->term_id == '9') {
                $num_posts_to_exclude = 3;
            } else {
                $num_posts_to_exclude = 5;
            }
            $posts_to_exclude = get_posts(array(
                'posts_per_page' => $num_posts_to_exclude,
                'category__in'   => array($category->term_id),
                'fields'         => 'ids'
            ));

        }
        $query->set('post__not_in', (array) $posts_to_exclude);

    }
}

Remarque: L'idée est correcte, mais le code ne fonctionne pas.

2
okiedokey

Voici un concept que vous pouvez essayer d'exclure 5 messages par catégorie, chacun des catégories 3 et 5, où sont exclus les messages appartenant à la catégorie 9 et l'une des catégories susmentionnées.

Voici la fonction: (CAVEAT:requiert PHP 5.4+ car j'ai utilisé la nouvelle syntaxe de tableau abrégé. Modifiez si nécessaire)

/**
 * function get_sticky_category_posts()
 *
 * Get an array of post ids according to arguments given.
 *
 * @author Pieter Goosen
 * @see https://wordpress.stackexchange.com/q/187477/31545
 *
 * @param array $args
 */
function get_sticky_category_posts( $args = [] )
{
    /*
     * If the array is empty or not valid, return null
     */
    if ( empty( $args ) || !is_array( $args ) )
        return null;

    $ids = '';

    foreach ( $args as $k=>$v ) {

        /*
         * Check that $v['taxonomy'], $v['included_terms'] and $v['posts_per_page'] are all set. If not, return null
         */ 
        if (    !isset( $v['taxonomy'] ) 
             || !isset( $v['included_terms'] )
             || !isset( $v['posts_per_page'] )
        )
        return null;

        /*
         * Sanitize and validate user input
         */
        $included_terms = filter_var( $v['included_terms'], FILTER_VALIDATE_INT, ['flags'  => FILTER_FORCE_ARRAY] );
        $taxonomy       = filter_var( $v['taxonomy'], FILTER_SANITIZE_STRING );

        /*
         * Create tax_query according to whether $v['excluded_terms'] is set
         */
        if ( !isset( $v['excluded_terms'] ) ) {
            $tax_query = [
                [
                    'taxonomy'         => $taxonomy,
                    'terms'            => $included_terms,
                    'include_children' => false,
                ],
            ];
        } else {
            $tax_query = [
                [
                    'taxonomy'         => $taxonomy,
                    'terms'            => $included_terms,
                    'include_children' => false,
                ],
                [
                    'taxonomy'         => $taxonomy,
                    'terms'            => filter_var( $v['excluded_terms'], FILTER_VALIDATE_INT, ['flags'  => FILTER_FORCE_ARRAY] ),
                    'include_children' => false,
                    'operator'         => 'NOT IN'
                ],
            ];
        } // endif ( !$v['excluded_term'] ) statement

        /*
         * Use get_posts to get an array of post ids to exclude
         */
        $posts_to_exclude = get_posts(
            [
                'posts_per_page' => filter_var( $v['posts_per_page'], FILTER_VALIDATE_INT ),
                'fields'         => 'ids',
                'tax_query'      => $tax_query
            ]
        );
        if ( $posts_to_exclude ) {

            /*
             * Concatenate all ids into a string using implode
             */
            $ids .= implode( ' ', $posts_to_exclude );
            $ids .= ' ';
        } //endif ( $posts_to_exclude )

    } //end foreach

    /*
     * If we don't have any ids, thus empty string, return null
     */
    if ( !$ids )
        return null;

    /*
     * Create a single flat array of post ids. Use rtrim to remove trailing white space
     */
    return explode( ' ', rtrim( $ids ) );
}

Voici comment cela fonctionne:

  • Vous devez passer un tableau multidimensionnel aux arguments de la fonction dans le format suivant

    $args = [
        0 => ['posts_per_page' => 5, 'taxonomy' => 'category', 'included_terms' => 3, 'excluded_terms' => 9],
        1 => ['posts_per_page' => 5, 'taxonomy' => 'category', 'included_terms' => 4, 'excluded_terms' => 9],
    ];
    $a = get_sticky_category_posts( $args );
    
  • Il y a quatre paramètres autorisés, que vous devez définir le posts_per_page, taxonomy et included_terms (qui doit être un entier ou un tableau d'identificateurs de termes}, sinon la fonction retourne null. L'autre paramètre autorisé (qui n'est pas obligatoire et peut être omis) est excluded_terms, qui est également un entier ou un tableau d'identificateurs de termes. Il s'agit des termes dans lesquels aucune publication ne doit être publiée. Ce qui est renvoyé est le suivant (c'est-à-dire que Suivant)

    • 5 identifiants de poste du terme de taxonomie 3 de la taxinomie category, mais les messages ayant les termes 3 et 9 ne doivent pas être renvoyés

    • 5 identifiants de poste du terme de taxonomie 4 de la taxinomie category, mais les messages ayant les termes 4 et 9 ne doivent pas être renvoyés

    • Le tableau retourné par la fonction (var_dump( $a )) ressemble à ceci

      array(10) {
        [0]=>
        string(3) "159"
        [1]=>
        string(3) "149"
        [2]=>
        string(3) "129"
        [3]=>
        string(3) "126"
        [4]=>
        string(3) "119"
        [5]=>
        string(2) "76"
        [6]=>
        string(3) "528"
        [7]=>
        string(3) "394"
        [8]=>
        string(3) "147"
        [9]=>
        string(2) "97"
      }
      

Ce tableau peut maintenant être passé de pre_get_posts au paramètre post__not_in pour exclure les publications de la fonction. Vous pouvez également utiliser ce tableau d'identifiants ailleurs, par exemple, les renvoyer à une requête personnalisée ou à d'autres fins, telles que les post-it sur d'autres pages, à l'exception de la page d'accueil utilisée dans cette réponse :-)

Voici un cas d'utilisation avec pre_get_posts. Cela supprimera les publications requises de la requête principale. (Nécessite PHP 5.4+ en raison de la syntaxe de la fonction et de l'utilisation de fermetures (des fermetures sont disponibles PHP 5.3+)

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Not necessary for home page, included it to be used on any other template like category pages
         && $q->is_main_query() // Make sure this is the main query
         && $q->is_home() // Targets the home page only
    ) {
        if ( function_exists( 'get_sticky_category_posts' ) ) {

            $args = [
                0 => ['posts_per_page' => 5, 'taxonomy' => 'category', 'included_terms' => 3, 'excluded_terms' => 9],
                1 => ['posts_per_page' => 5, 'taxonomy' => 'category', 'included_terms' => 4, 'excluded_terms' => 9],
            ];
            $sticky_cat_posts = get_sticky_category_posts( $args );

            if ( !empty( $sticky_cat_posts ) ) {
                $q->set( 'post__not_in', $sticky_cat_posts );
            }   
        }
    }
}); 
3
Pieter Goosen

Vous pouvez essayer ce type d’approche hook pre_get_posts (non testée):

add_action( 'pre_get_posts', function( $q )
{
    $exclude_cats = [ 12, 34 ]; // <-- Edit these category ids!

    if(    ! is_admin() 
        && $q->is_main_query()
        && ! is_singular()
        && ! is_category( $exclude_cats ) 
    )
    {
        $exclude_post_ids = get_posts( 
            [
                'fields'         => 'ids',
                'posts_per_page' => 5,
                'category__in'   => $exclude_cats,
            ]
        );
        $q->set( 'post__not_in', (array) $exclude_post_ids ); 
    }
} );

où vous pourriez avoir à adapter cela à vos besoins.

Vous pouvez également utiliser des slugs de catégorie à la place, mais vous devrez alors configurer un tax_query approprié, car category__in ne prend en charge qu'un tableau d'identifiants de catégorie.

3
birgire