Come ottenere articoli da due categorie con WP_Query?

2 feb 2014, 14:45:05
Visualizzazioni: 21.7K
Voti: 5

Sto usando WP_Query per creare sezioni 'ultimi articoli' e 'articoli popolari' sulla mia home page. Sto cercando di estrarre solo 5 articoli da 2 categorie (9 e 11) ma mostra solo gli articoli della categoria 9.

Ecco il PHP che sto usando per 'ultimi articoli'-

<ul>
        <?php 
        // Array delle categorie
        $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 ); //la query

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

        <?php
        if($i==0){ //Imposta l'output per il primo articolo
        ?>  
            <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(); ?>">Leggi di più »</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; //fine del loop ?>
        <?php wp_reset_postdata(); // reset della query ?>
        </ul> 

Qualche suggerimento?

6
Commenti

Hmm, sembra che dovrebbe funzionare. Cosa succede se provi a interrogare solo la categoria 11?

Rarst Rarst
2 feb 2014 15:24:41

Per favore controlla anche non solo interrogando 5 post - potrebbe essere che i primi cinque siano solo dalla categoria 9.

fischi fischi
2 feb 2014 16:19:50

'showposts' è deprecato. Prova a usare 'posts_per_page' invece.

Mayeenul Islam Mayeenul Islam
2 feb 2014 17:23:02

Oh =/ posts_per_page non funzionava per me

user3205234 user3205234
3 feb 2014 16:12:37

Quando le funzionalità vengono deprecate, significa che smetteranno di funzionare nei futuri aggiornamenti di WordPress?

user3205234 user3205234
3 feb 2014 16:12:59

@user3205234 Quando le funzionalità vengono deprecate, significa che smetteranno di funzionare nei futuri aggiornamenti di WordPress? - Ragionevolmente sì. Ma WordPress ha un ampio supporto per le versioni più vecchie, quindi finora non è un problema visibile. Ma in futuro WordPress potrebbe interrompere il supporto e potrebbe spostarle a plugin specifici per versione.

Mayeenul Islam Mayeenul Islam
26 apr 2015 09:34:21
Mostra i restanti 1 commenti
Tutte le risposte alla domanda 3
0

Nella mia esperienza, l'utilizzo dei filtri 'posts_*' ('posts_request', 'posts_where'...) in combinazione con str_replace / preg_replace è inaffidabile e poco flessibile:

Inaffidabile perché se un altro filtro modifica uno di questi filtri, nel migliore dei casi si ottengono risultati imprevisti, nel peggiore si generano errori SQL.

Poco flessibile perché modificare un argomento, ad esempio 'include_children' per le categorie, o riutilizzare il codice per 3 termini invece di 2 richiede molto lavoro.

Inoltre, adattare il codice per renderlo compatibile con il multisite richiede modifiche manuali al SQL.

Quindi, a volte, anche se non è la soluzione migliore in termini di prestazioni, un approccio più canonico è la soluzione migliore e più flessibile.

E le prestazioni possono essere migliorate con alcuni trucchi di caching...

La mia proposta:

  1. scrivere una funzione che utilizzi usort per ordinare i post provenienti da query diverse (ad esempio una per termine)
  2. scrivere una funzione che alla prima esecuzione esegua query separate, unisca i risultati, li ordini, li memorizzi nella cache e li restituisca. Nelle richieste successive restituisca semplicemente i risultati memorizzati
  3. gestire l'invalidamento della cache quando necessario

##Codice##

Innanzitutto la funzione che ordina i post:

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

Poi la funzione che ottiene i risultati non memorizzati:

function my_fresh_terms_get_posts( $args, $terms, $tax_query_args = NULL ) {
  $posts = array();
  // abbiamo bisogno di conoscere almeno la tassonomia
  if ( ! is_array( $tax_query_args ) || ! isset( $tax_query_args['taxonomy'] ) ) return;
  // gestisce il tax_query di base
  $base_tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array();
  // esegue una query per ogni termine
  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() ) {
      // unisce i post recuperati nell'array $posts
      // prevenendo duplicati utilizzando gli ID come chiavi dell'array
      $ids = wp_list_pluck( $q->posts, 'ID' );
      $keyed = array_combine( $ids, array_values( $q->posts ) );
      $posts += $keyed;
    }
  }
  return $posts;
}

Ora la funzione che verifica la cache e la restituisce se disponibile o restituisce i risultati non memorizzati

function my_terms_get_posts( $args, $terms, $tax_query_args = NULL, $order = 'DESC' ) {
  // abbiamo bisogno di conoscere almeno la tassonomia
  if ( ! is_array( $tax_query_args ) || ! isset( $tax_query_args['taxonomy'] ) ) return;
  $tax = $tax_query_args['taxonomy'];
  // ottiene i risultati memorizzati
  $cached = get_transient( "my_terms_get_posts_{$tax}" );
  if ( ! empty( $cached ) ) return $cached;
  // nessun risultato memorizzato, ottiene i post 'freschi'
  $posts = my_fresh_terms_get_posts( $args, $terms, $tax_query_args );
  if ( ! empty($posts) ) {
    // ordina i post e li memorizza nella cache
    $posts = my_date_terms_posts_sort( $posts, $order );
    set_transient( "my_terms_get_posts_{$tax}",  $posts, DAY_IN_SECONDS );
  }
  return $posts;
}

La cache viene pulita automaticamente ogni giorno, tuttavia, è possibile invalidarla ogni volta che un nuovo post viene aggiunto o aggiornato in una specifica tassonomia. Questo può essere fatto aggiungendo una funzione di pulizia della cache su '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 );

##Utilizzo##

// il numero di post da recuperare per ogni termine
// il totale dei post recuperati sarà uguale a questo numero x il numero di termini passati
// alla funzione my_terms_get_posts
$perterm = 5;

// prima definisci gli argomenti generali:
$paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1;
$args = array(
  'posts_per_page' => $perterm,
  'paged' => $paged,
);

// argomenti della tassonomia
$base_tax_args = array(
  'taxonomy' => 'category'
);
$terms = array( 9, 11 ); // è possibile utilizzare anche gli slug

// ottiene i post
$posts = my_terms_get_posts( $args, $terms, $base_tax_args );

// loop
if ( ! empty( $posts ) ) {
  foreach ( $posts as $_post ) {
    global $post;
    setup_postdata( $_post );
    //-------------------------> il codice del loop va qui    
  }
  wp_reset_postdata();
}

Le funzioni sono abbastanza flessibili da poter utilizzare query complesse, anche query per tassonomie aggiuntive:

ESEMPIO:

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

In questo modo il 'tax_query' impostato nell'array $args verrà unito dinamicamente con l'argomento della query tassonomica in $base_tax_args, per ognuno dei termini nell'array $terms.

È anche possibile ordinare i post in ordine crescente:

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

Nota bene:

  1. IMPORTANTE: Se alcuni post appartengono a più di una categoria tra quelle passate alla funzione (ad esempio nel caso dell'OP alcuni post hanno sia la categoria 9 che la 11) il numero di post recuperati non sarà quello previsto, perché la funzione restituirà quei post una sola volta
  2. Il codice richiede PHP 5.3+
19 mar 2014 22:34:00
2

Secondo il Codex:

Mostrare Post da Diverse Categorie (Visualizza i post che appartengono a queste categorie, utilizzando l'id della categoria) sarebbe:

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

Inoltre, i Parametri di Paginazione del Codex indicano che 'showposts' è deprecato e sostituito con 'posts_per_page'.

posts_per_page (int) - numero di post da mostrare per pagina (disponibile dalla Versione 2.1, ha sostituito il parametro showposts).

2 feb 2014 17:29:44
Commenti

Grazie per la tua risposta, proverò questa soluzione dopo aver provato quella di s_ha_dum. (posts per page non funzionava davvero per me prima per qualche strana ragione)

user3205234 user3205234
3 feb 2014 16:08:46

@user3205234: Dovresti usare posts_per_page ma non risolverà il problema che penso tu abbia - che una categoria o l'altra riempia il tuo limite di post prima che l'altra sia rappresentata.

s_ha_dum s_ha_dum
3 feb 2014 16:11:46
5

Quello che stai chiedendo è quasi un duplicato di questa domanda: Come posso creare la mia meta_query annidata utilizzando posts_where / posts_join?

Il problema, come suggerisce @fischi, è quasi sicuramente che i risultati provengono da una categoria o dall'altra e raggiungono il limite di post prima che entrambe siano equamente rappresentate. Per far funzionare questo, hai bisogno di un UNION. WP_Query non è in grado di gestire quella logica, ma con gli hook puoi farlo funzionare.

$cat = 9; // la tua prima categoria
$showposts = 5; // i risultati effettivi sono il doppio di questo valore
$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); // manipola questo

  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 ); // la query
var_dump($the_query->posts);

Un paio di note: WP_Query analizzerà l'argomento della categoria e troverà le categorie figlie, quindi il primo UNION includerà tutti i figli della categoria 9. Non ho duplicato quella logica per la categoria 11. Se hai bisogno di quella funzionalità, puoi fare riferimento al sorgente di WP_Query e riprodurre l'effetto.

2 feb 2014 17:56:28
Commenti

Oh, scusa, avrei dovuto cercare meglio per trovare la risposta =/

user3205234 user3205234
3 feb 2014 16:05:40

Non c'è bisogno di scuse. La domanda a cui facevi riferimento ti avrebbe avvicinato alla soluzione, e se conosci bene PHP/MySQL avresti potuto arrivare alla risposta, ma non è un vero duplicato.

s_ha_dum s_ha_dum
3 feb 2014 16:07:39

Grazie per la tua risposta, proverò a implementarla. Non avevo mai sentito parlare di UNION prima d'ora, una cosa nuova da approfondire per me. Anche se i 5 post più recenti provengono sicuramente da entrambe le categorie.

user3205234 user3205234
3 feb 2014 16:07:43

Ahh, il mio MySQL non è ancora il massimo - ho ancora molto da imparare! Grazie per il tuo aiuto =)

user3205234 user3205234
3 feb 2014 16:09:42

Anche se i "cinque più recenti" provengono da entrambe le categorie, quello che otterresti senza l'unione sarebbe 3 da una e 2 dall'altra, oppure 4 da una e 1 dall'altra - non sarebbe un ritorno equo da entrambe.

s_ha_dum s_ha_dum
3 feb 2014 16:55:09