Cum să obții articole din două categorii cu WP_Query?
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?
Î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:
- scrieți o funcție care să folosească
usort
pentru a ordona postările provenite din diferite interogări (de exemplu, una per termen) - 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
- 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:
- 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ă.
- Codul necesită PHP 5.3+

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 parametrulshowposts
).

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)

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.

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ă.

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.

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