web-dev-qa-db-fra.com

Comment recevoir des posts de deux catégories avec WP_Query?

J'utilise WP_Query pour créer des sections "Derniers articles" et "Articles populaires" sur ma page d'accueil. J'essaie simplement de tirer 5 messages de 2 catégories (9 et 11), mais il ne montrera que les messages de cat 9.

Voici le php que j'utilise pour les "derniers messages"

<ul>
        <?php 

        $cat = array(9,11);
        $showposts = 5;
        $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
        $args=array(
            'category__in' => $cat, 
            'showposts' => $showposts,
            'paged' => $paged,
            'orderby' => 'post_date',
            'order' => 'DESC',
            'post_status' => 'publish',
           );

        $the_query = new WP_Query ( $args ); //the query


        $i = 0;while ($the_query->have_posts() ) : $the_query->the_post(); //start the loop
        ?>

        <?php
        if($i==0){ //Sets the output for the top post
        ?>  
            <li class="first-news">
                <div class="post-thumbnail"><a href="<?php the_permalink(); ?>"><?php the_post_thumbnail(350,187); ?></a></div>
                <h2 class="post-box-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
                <p class="post-meta"><span class="tie-date"><?php the_time('F jS, Y') ?></span></p>
                <div class="entry"><?php the_excerpt(); ?></div>
                <div><a class="more-link" href="<?php the_permalink(); ?>">Read More »</a></div>

            </li>


        <?php
            $i++;
             } else { ?>

                <li class="other-news rar">
                <div class="post-thumbnail"><a href="<?php the_permalink(); ?>"><?php the_post_thumbnail(145,93); ?></a></div>
                <h3 class="post-box-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
                <p class="post-meta"><span class="tie-date"><?php the_time('F jS, Y') ?></span></p>
           </li>

        <?php } endwhile; //end of the loop ?>
        <?php wp_reset_postdata(); // reset the query ?>
        </ul> 

Aucune suggestion?

5
user3205234

D'après mon expérience, utiliser des filtres 'posts_*' ('posts_request', 'posts_where'...) en combinaison avec str_replace/preg_replace n'est ni fiable ni flexible:

Peu fiable car si un autre filtre utilise l'un de ces filtres, dans le meilleur des cas, on obtient des résultats inattendus, dans le pire des cas, des erreurs SQL.

Inflexible car changer un argument, par ex. "include_children" pour les catégories, ou réutilisez le code pour par exemple. 3 termes au lieu de 2 nécessitent beaucoup de travail.

De plus, adaptez le code pour qu'il soit compatible avec la nécessité de modifier le code SQL manuellement.

Ainsi, parfois, même si ce n’est pas la meilleure solution en matière de performances, une approche plus canonique est la meilleure et la plus souple.

Et les performances peuvent être améliorées avec quelques astuces de mise en cache ...

Mon propos:

  1. écrivez une fonction qui utilise usort pour commander des articles provenant de différentes requêtes (par exemple, un par terme)
  2. écrivez une fonction qui, à la première exécution, exécute des requêtes distinctes, fusionne les résultats, les ordonne, les met en cache et les renvoie. Sur demande ultérieure, renvoyez simplement les résultats mis en cache
  3. gérer l'invalidation de la mise en cache en cas de besoin

Code

D'abord la fonction que l'ordre poste:

function my_date_terms_posts_sort( Array $posts, $order = 'DESC' ) {
  if ( ! empty( $posts ) ) {
    usort( $posts, function( WP_Post $a, WP_Post $b ) use ( $order ) {
      $at = (int) mysql2date( 'U', $a->post_date );
      $bt = (int) mysql2date( 'U', $b->post_date );
      $orders = strtoupper($order) === 'ASC' ? array( 1, -1 ) : array( -1, 1 );
      return $at === $bt ? 0 : ( $at > $bt ) ? $orders[0] : $orders[1];
    } );
  }
  return $posts;
}

Ensuite, la fonction qui obtient des résultats non mis en cache:

function my_fresh_terms_get_posts( $args, $terms, $tax_query_args = NULL ) {
  $posts = array();
  // we need to know at least the taxonomy
  if ( ! is_array( $tax_query_args ) || ! isset( $tax_query_args['taxonomy'] ) ) return;
  // handle base tax_query
  $base_tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array();
  // run a query for each term
  foreach ( $terms as $term ) {
    $term_tax_query = wp_parse_args( array(
      'terms' => array( $term ),
      'field' => is_numeric( $term ) ? 'term_id' : 'slug'
    ), $tax_query_args );
    $args['tax_query'] = array_merge( $base_tax_query, array($term_tax_query) );
    $q = new WP_Query( $args ); 
    if ( $q->have_posts() ) {
      // merging retrieved posts in $posts array
      // preventing duplicates using ID as array keys
      $ids = wp_list_pluck( $q->posts, 'ID' );
      $keyed = array_combine( $ids, array_values( $q->posts ) );
      $posts += $keyed;
    }
  }
  return $posts;
}

Maintenant, la fonction qui vérifie le cache et le renvoie si disponible ou renvoie les résultats non mis en cache

function my_terms_get_posts( $args, $terms, $tax_query_args = NULL, $order = 'DESC' ) {
  // we need to know at least the taxonomy
  if ( ! is_array( $tax_query_args ) || ! isset( $tax_query_args['taxonomy'] ) ) return;
  $tax = $tax_query_args['taxonomy'];
  // get cached  results
  $cached = get_transient( "my_terms_get_posts_{$tax}" );
  if ( ! empty( $cached ) ) return $cached;
  // no cached  results, get 'fresh' posts
  $posts = my_fresh_terms_get_posts( $args, $terms, $tax_query_args );
  if ( ! empty($posts) ) {
    // order posts and cache them
    $posts = my_date_terms_posts_sort( $posts, $order );
    set_transient( "my_terms_get_posts_{$tax}",  $posts, DAY_IN_SECONDS );
  }
  return $posts;
}

Le cache est nettoyé automatiquement tous les jours , mais il est possible de l'invalider à chaque fois qu'une nouvelle publication dans une taxonomie spécifique est ajoutée ou mise à jour. Cela peut être fait en ajoutant une fonction de cache de nettoyage sur 'set_object_terms'

add_action( 'set_object_terms', function( $object_id, $terms, $tt_ids, $taxonomy ) {
  $taxonomies = get_taxonomies( array( 'object_type' => array('post') ), 'names' );
  if ( in_array( $taxonomy, (array) $taxonomies ) ) {
    delete_transient( "my_terms_get_posts_{$taxonomy}" );
  }
}, 10, 4 );

Usage

// the number of post to retrieve for each term
// total of posts retrieved will be equat to this number x the number of terms passed
// to my_terms_get_posts function
$perterm = 5;

// first define general args:
$paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1;
$args = array(
  'posts_per_page' => $perterm,
  'paged' => $paged,
);

// taxonomy args
$base_tax_args = array(
  'taxonomy' => 'category'
);
$terms = array( 9, 11 ); // is also possible to use slugs

// get posts
$posts = my_terms_get_posts( $args, $terms, $base_tax_args );

// loop
if ( ! empty( $posts ) ) {
  foreach ( $posts as $_post ) {
    global $post;
    setup_postdata( $_post );
    //-------------------------> loop code goes here    
  }
  wp_reset_postdata();
}

Les fonctions sont suffisamment flexibles pour utiliser des requêtes complexes, voire des requêtes pour des taxonomies supplémentaires:

PAR EXEMPLE.

$args = array(
  'posts_per_page' => $perterm,
  'paged' => $paged,
  'post_status' => 'publish',
  'tax_query' => array(
     'relation' => 'AND',
     array(
       'taxonomy' => 'another_taxonomy',
       'terms' => array( 'foo', 'bar' ),
       'field' => 'id'
     )
   )
);

$base_tax_args = array(
  'taxonomy' => 'category',
  'include_children' => FALSE
);

$terms = array( 'a-category', 'another-one' );

$posts = my_terms_get_posts( $args, $terms, $base_tax_args );

De cette manière, la 'taxe_query' définie dans le tableau $args sera fusionnée de manière dinamique avec l'argument de requête fiscale dans $base_tax_args, pour chacun des termes du tableau $terms.

Est-ce que les commandes possibles sont également en ordre croissant:

$posts = my_terms_get_posts( $args, $terms, $base_tax_args, 'ASC' );

Notez s'il vous plaît:

  1. IMPORTANT: si certains messages appartiennent à plus d’une catégorie parmi ceux passés à la fonction (par exemple, dans le cas où certains postes ont à la fois les catégories 9 et 11), le nombre de messages récupérés ne sera pas celui attendu, car la fonction le retournera une
  2. Le code a besoin de PHP 5.3+
5
gmazzap

Selon le Codex :

Afficher les articles de plusieurs catégories (Afficher les articles qui ont ces catégories, en utilisant l'id de la catégorie) serait:

$query = new WP_Query( 'cat=9,11' );

De plus, les paramètres de pagination du Codex indiquent que 'showposts' est obsolète et remplacé par 'posts_per_page'.

posts_per_page(int) - numéro de publication à afficher par page (disponible avec Version 2.1 , paramètre showposts remplacé).

4
Mayeenul Islam

Ce que vous demandez est un double de cette question: Comment créer ma propre méta_query imbriquée avec posts_where/posts_join?

Le problème, comme @fischi le suggère, est presque certainement que les résultats proviennent d'une catégorie ou d'une autre et atteignent la limite de publication avant que les deux ne soient représentés de manière égale. Pour que cela fonctionne, vous avez besoin d'une UNION. WP_Query n'est pas capable de cette logique, mais avec les hooks, vous pouvez le faire fonctionner.

$cat = 9; // your first category
$showposts = 5; // actual results are twice this value
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$args=array(
  'cat' => $cat, 
  'posts_per_page' => $showposts,
  'paged' => $paged,
  'orderby' => 'post_date',
  'order' => 'DESC',
  'post_status' => 'publish',
);

function create_cat_union($clauses) {
  remove_filter('posts_request','create_cat_union',1);
  $clauses = str_replace('SQL_CALC_FOUND_ROWS','',$clauses,$scfr);

  $scfr = (0 < $scfr) ? 'SQL_CALC_FOUND_ROWS' : '';

  $pattern = 'wp_term_relationships.term_taxonomy_id IN \(([0-9,]+)\)';
  $clause2 = preg_replace('|'.$pattern.'|','wp_term_relationships.term_taxonomy_id IN (11)',$clauses); // manipulate this

  return "SELECT {$scfr} u.* FROM (({$clauses}) UNION ({$clause2})) as u";
}
add_filter('posts_request','create_cat_union',1,2);

$the_query = new WP_Query ( $args ); //the query
var_dump($the_query->posts);

Quelques notes: WP_Query analysera l'argument de catégorie et recherchera des enfants de catégorie afin que la première UNION inclue tous les enfants de catégorie 9. In'a pasduplique cette logique pour la catégorie 11 Si vous avez besoin de cette fonctionnalité, vous pouvez vous référer à la source WP_Query et reproduire l’effet.

0
s_ha_dum