Cum să obții articole din două categorii cu WP_Query?

2 feb. 2014, 14:45:05
Vizualizări: 21.7K
Voturi: 5

Folosesc WP_Query pentru a crea secțiunile 'ultimele articole' și 'articole populare' pe pagina mea principală. Încerc să extrag doar 5 articole din 2 categorii (9 și 11) dar îmi afișează doar articolele din categoria 9.

Iată codul PHP pe care îl folosesc pentru 'ultimele articole' -

<ul>
        <?php 
        // Definim array-ul cu ID-urile categoriilor
        $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 ); //inițializăm query-ul

        // Începem bucla
        $i = 0;while ($the_query->have_posts() ) : $the_query->the_post(); 
        ?>

        <?php
        if($i==0){ //Setăm output-ul pentru primul articol
        ?>  
            <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(); ?>">Citește mai mult »</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; //sfârșitul buclei ?>
        <?php wp_reset_postdata(); // resetăm query-ul ?>
        </ul> 

Aveți vreo sugestie?

6
Comentarii

Hm, ar trebui să funcționeze. Ce se întâmplă dacă încerci să interoghezi doar categoria 11?

Rarst Rarst
2 feb. 2014 15:24:41

Te rog să verifici nu doar interogarea a 5 postări - este posibil ca primele cinci să fie doar din categoria 9.

fischi fischi
2 feb. 2014 16:19:50

'showposts' este învechit. Încearcă să folosești 'posts_per_page' în schimb.

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

Oh =/ posts_per_page nu a funcționat pentru mine

user3205234 user3205234
3 feb. 2014 16:12:37

Când funcționalitățile sunt depreciate, înseamnă că nu vor mai funcționa în viitoarele actualizări WordPress?

user3205234 user3205234
3 feb. 2014 16:12:59

@user3205234 Când funcționalitățile sunt depreciate, înseamnă că nu vor mai funcționa în viitoarele actualizări WordPress? - În principiu da. Dar WordPress are un suport extins pentru versiunile mai vechi, așa că până acum nu a fost o problemă vizibilă. Dar în viitor WordPress poate/ar putea să întrerupă suportul și poate/ar putea să le mute în plugin-uri specifice versiunilor.

Mayeenul Islam Mayeenul Islam
26 apr. 2015 09:34:21
Arată celelalte 1 comentarii
Toate răspunsurile la întrebare 3
0

În experiența mea, utilizarea filtrelor 'posts_*' ('posts_request', 'posts_where'...) în combinație cu str_replace / preg_replace este nesigură și inflexibilă:

Nesigură deoarece, dacă un alt filtru modifică folosind unul dintre aceste filtre, în cel mai bun caz se obțin rezultate neașteptate, iar în cel mai rău caz erori SQL.

Inflexibilă pentru că schimbarea unui argument, de exemplu 'include_children' pentru categorii, sau reutilizarea codului pentru, de exemplu, 3 termeni în loc de 2 necesită multă muncă.

Mai mult, adaptarea codului pentru a fi compatibil cu multisite necesită editarea manuală a SQL.

De aceea, uneori, chiar dacă nu este cea mai bună soluție din punct de vedere al performanței, o abordare mai canonică este cea mai bună și mai flexibilă.

Iar performanța poate fi îmbunătățită cu câteva trucuri de caching...

Propunerea mea:

  1. scrieți o funcție care să folosească usort pentru a ordona postările provenite din diferite interogări (de exemplu, una per termen)
  2. scrieți o funcție care la prima rulare să execute interogări separate, să combine rezultatele, să le ordoneze, să le stocheze în cache și să le returneze. La solicitările ulterioare, pur și simplu să returneze rezultatele din cache
  3. gestionați invalidarea cache-ului atunci când este necesar

##Cod##

Mai întâi, funcția care ordonează postările:

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

Apoi, funcția care obține rezultatele ne-stocate în cache:

function my_fresh_terms_get_posts( $args, $terms, $tax_query_args = NULL ) {
  $posts = array();
  // avem nevoie să știm cel puțin taxonomia
  if ( ! is_array( $tax_query_args ) || ! isset( $tax_query_args['taxonomy'] ) ) return;
  // gestionează tax_query de bază
  $base_tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array();
  // execută o interogare pentru fiecare termen
  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() ) {
      // combină postările obținute în array-ul $posts
      // previne duplicatele folosind ID-urile ca chei de array
      $ids = wp_list_pluck( $q->posts, 'ID' );
      $keyed = array_combine( $ids, array_values( $q->posts ) );
      $posts += $keyed;
    }
  }
  return $posts;
}

Acum funcția care verifică cache-ul și îl returnează dacă este disponibil sau returnează rezultatele ne-stocate în cache

function my_terms_get_posts( $args, $terms, $tax_query_args = NULL, $order = 'DESC' ) {
  // avem nevoie să știm cel puțin taxonomia
  if ( ! is_array( $tax_query_args ) || ! isset( $tax_query_args['taxonomy'] ) ) return;
  $tax = $tax_query_args['taxonomy'];
  // obține rezultatele din cache
  $cached = get_transient( "my_terms_get_posts_{$tax}" );
  if ( ! empty( $cached ) ) return $cached;
  // nu există rezultate în cache, obține postări 'proaspete'
  $posts = my_fresh_terms_get_posts( $args, $terms, $tax_query_args );
  if ( ! empty($posts) ) {
    // ordonează postările și le stochează în cache
    $posts = my_date_terms_posts_sort( $posts, $order );
    set_transient( "my_terms_get_posts_{$tax}",  $posts, DAY_IN_SECONDS );
  }
  return $posts;
}

Cache-ul se curăță automat zilnic, totuși, este posibil să-l invalidăm de fiecare dată când o nouă postare este adăugată sau actualizată într-o anumită taxonomie. Acest lucru poate fi făcut adăugând o funcție de curățare a cache-ului pe '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 );

##Utilizare##

// numărul de postări de recuperat pentru fiecare termen
// totalul postărilor recuperate va fi egal cu acest număr x numărul de termeni pasați
// la funcția my_terms_get_posts
$perterm = 5;

// mai întâi definește argumentele generale:
$paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1;
$args = array(
  'posts_per_page' => $perterm,
  'paged' => $paged,
);

// argumentele taxonomiei
$base_tax_args = array(
  'taxonomy' => 'category'
);
$terms = array( 9, 11 ); // este posibil să se folosească și slug-uri

// obține postările
$posts = my_terms_get_posts( $args, $terms, $base_tax_args );

// buclă
if ( ! empty( $posts ) ) {
  foreach ( $posts as $_post ) {
    global $post;
    setup_postdata( $_post );
    //-------------------------> codul buclei merge aici    
  }
  wp_reset_postdata();
}

Funcțiile sunt suficient de flexibile pentru a utiliza interogări complexe, chiar și interogări pentru taxonomii adiționale:

DE EXEMPLU:

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

În acest fel, 'tax_query' setat în array-ul $args va fi combinat dinamic cu argumentul tax_query din $base_tax_args, pentru fiecare dintre termenii din array-ul $terms.

De asemenea, este posibil să ordonați postările în ordine crescătoare:

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

Vă rugăm să rețineți:

  1. IMPORTANT: Dacă unele postări aparțin mai multor categorii dintre cele pasate funcției (de exemplu, în cazul OP, unele postări au atât categoria 9 cât și 11), numărul de postări recuperate nu va fi cel așteptat, deoarece funcția va returna acele postări o singură dată.
  2. Codul necesită PHP 5.3+
19 mar. 2014 22:34:00
2

Conform Codex-ului:

Afișare Postări Din Mai Multe Categorii (Afișează postările care aparțin acestor categorii, folosind ID-ul categoriei) ar fi:

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

Și, conform Parametrilor de Paginare din Codex, 'showposts' este învechit și înlocuit cu 'posts_per_page'.

posts_per_page (int) - numărul de postări de afișat pe pagină (disponibil începând cu Versiunea 2.1, a înlocuit parametrul showposts).

2 feb. 2014 17:29:44
Comentarii

Mulțumesc pentru răspuns, voi încerca asta după ce testez varianta lui s_ha_dum. (opțiunea posts per page nu a funcționat cum trebuie pentru mine din nu știu ce motiv)

user3205234 user3205234
3 feb. 2014 16:08:46

@user3205234: Ar trebui să folosești posts_per_page, dar nu cred că va rezolva problema pe care o ai - faptul că o categorie îți umple limita de postări înainte ca cealaltă să fie reprezentată.

s_ha_dum s_ha_dum
3 feb. 2014 16:11:46
5

Ceea ce întrebați este aproape o dublură a acestei întrebări: Cum pot crea propriul meta_query imbricat folosind posts_where / posts_join?

Problema, așa cum sugerează @fischi, este aproape sigur că rezultatele provin dintr-o categorie sau alta și ating limita de postări înainte ca ambele să fie reprezentate în mod egal. Pentru a face acest lucru să funcționeze, aveți nevoie de o uniune (UNION). WP_Query nu este capabilă de această logică, dar cu ajutorul hook-urilor o puteți face să funcționeze.

$cat = 9; // prima dvs. categorie
$showposts = 5; // rezultatele reale sunt de două ori această valoare
$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); // manipulați această linie

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

Câteva observații: WP_Query va analiza argumentul categoriei și va găsi categoriile copil, astfel încât prima UNION va include toate categoriile copil ale categoriei 9. Eu nu am duplicat acea logică pentru categoria 11. Dacă aveți nevoie de această funcționalitate, puteți consulta sursa WP_Query și reproduce efectul.

2 feb. 2014 17:56:28
Comentarii

Oh, scuze, trebuia să caut mai atent pentru a găsi răspunsul =/

user3205234 user3205234
3 feb. 2014 16:05:40

Nu e nevoie de scuze. Întrebarea la care te-ai referit te-ar fi putut apropia de soluție, iar dacă ești priceput în PHP/MySQL ai fi putut ajunge la răspuns, dar nu este exact o duplicată.

s_ha_dum s_ha_dum
3 feb. 2014 16:07:39

Mulțumesc pentru răspuns, o să încerc. Nu am auzit de UNION până acum, ceva nou de studiat pentru mine. Deși cele mai recente 5 postări sunt cu siguranță din ambele categorii.

user3205234 user3205234
3 feb. 2014 16:07:43

Ahh, MySQL-ul meu nu este încă prea bun - mai am multe de învățat! Mulțumesc pentru ajutor =)

user3205234 user3205234
3 feb. 2014 16:09:42

Chiar dacă "cele mai noi cinci" sunt din ambele categorii, ceea ce ai obține fără uniune ar fi 3 dintr-o categorie și 2 din cealaltă, sau 4 dintr-o categorie și 1 din cealaltă - acesta nu este un rezultat echilibrat din fiecare.

s_ha_dum s_ha_dum
3 feb. 2014 16:55:09