Come utilizzare WP_Query per interrogare più categorie con un numero limitato di post per categoria?

25 ago 2010, 23:28:25
Visualizzazioni: 14.3K
Voti: 11

Ho 3 categorie con 15 post ciascuna, voglio fare UN'unica query al database che mi restituisca solo i primi 5 post per ogni categoria, come posso farlo?

$q = new WP_Query(array( 'post__in' => array(2,4,8), 'posts_per_page' => **PRIMI_5_PER_CIASCUNA_CAT** ));

Se non fosse possibile, cosa è più efficiente: ottenere tutti i post della categoria genitore e scorrerli oppure creare 3 query separate?

3
Commenti

Come vuoi che siano ordinati?

MikeSchinkel MikeSchinkel
26 ago 2010 00:05:15

pubblicati di recente... quindi i 5 più recenti della categoria A, i contenuti più recenti della categoria B ecc..

Amit Amit
26 ago 2010 00:09:41

Ok, vedi la mia risposta qui sotto

MikeSchinkel MikeSchinkel
26 ago 2010 02:12:36
Tutte le risposte alla domanda 3
11
17

Quello che vuoi ottenere è possibile ma richiederà di approfondire SQL, che preferisco evitare quando possibile (non perché non lo conosca, sono uno sviluppatore SQL avanzato, ma perché in WordPress è meglio utilizzare le API quando possibile per minimizzare futuri problemi di compatibilità legati a potenziali cambiamenti nella struttura del database).

SQL con un operatore UNION è una possibilità

Per usare SQL ti servirà un operatore UNION nella tua query, qualcosa come questo assumendo che gli slug delle tue categorie siano "category-1", "category-1" e "category-3":

SELECT * FROM wp_posts WHERE ID IN (
  SELECT tr.object_id
  FROM wp_terms t 
  INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
  INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
  WHERE tt.taxonomy='category' AND t.slug='category-1'
  LIMIT 5

  UNION

  SELECT tr.object_id
  FROM wp_terms t 
  INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
  INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
  WHERE tt.taxonomy='category' AND t.slug='category-2'
  LIMIT 5

  UNION

  SELECT tr.object_id
  FROM wp_terms t 
  INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
  INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
  WHERE tt.taxonomy='category' AND t.slug='category-3'
  LIMIT 5
)

Puoi usare SQL UNION con un filtro posts_join

Usando il codice sopra puoi fare la chiamata direttamente oppure puoi usare un hook filter posts_join come segue; nota che sto usando un PHP heredoc quindi assicurati che SQL; sia allineato a sinistra. Inoltre ho usato una variabile globale per permetterti di definire le categorie al di fuori dell'hook elencando gli slug delle categorie in un array. Puoi inserire questo codice in un plugin o nel file functions.php del tuo tema:

<?php
global $top_5_for_each_category_join;
$top_5_for_each_category_join = array('category-1','category-2','category-3');
add_filter('posts_join','top_5_for_each_category_join',10,2);
function top_5_for_each_category_join($join,$query) {
  global $top_5_for_each_category_join;
  $unioned_selects = array();
  foreach($top_5_for_each_category_join as $category) {
    $unioned_selects[] =<<<SQL
SELECT object_id
FROM wp_terms t
INNER JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
INNER JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tt.taxonomy='category' AND t.slug='{$category}'
LIMIT 5
SQL;
  }
  $unioned_selects = implode("\n\nUNION\n\n",$unioned_selects);
  return $join . " INNER JOIN ($unioned_selects) categories ON wp_posts.ID=categories.object_id " ;
}

Ma potrebbero esserci effetti collaterali

Ovviamente usare gli hook di modifica delle query come posts_join può avere effetti collaterali dato che agiscono globalmente sulle query, quindi di solito è necessario racchiudere le modifiche in un if che le applica solo quando necessario, e determinare i criteri da testare può essere complicato.

Meglio concentrarsi sull'ottimizzazione?

Tuttavia, immagino che la tua domanda sia più legata all'ottimizzazione che alla possibilità di fare una query per i primi 5 articoli per 3 categorie, giusto? Se è così, forse ci sono altre opzioni che utilizzano le API di WordPress?

Meglio usare la Transients API per la cache?

Suppongo che i tuoi articoli non cambino così spesso, corretto? E se accettassi il costo delle tre (3) query periodicamente e poi memorizzassi i risultati usando la Transients API? Avresti codice mantenibile e ottime prestazioni; addirittura migliori rispetto alla query UNION perché WordPress memorizzerà gli elenchi di articoli come un array serializzato in un singolo record della tabella wp_options.

Puoi prendere il seguente esempio e salvarlo nella root del tuo sito come test.php per testarlo:

<?php

$timeout = 4; // 4 ore
$categories = array('category-1','category-2','category-3');

include "wp-load.php";
$category_posts = get_transient('top5_posts_per_category');
if (!is_array($category_posts) || count($category_posts)==0) {
  echo "Hello Every {$timeout} hours!";
  $category_posts = array();
  foreach($categories as $category) {
    $posts = get_posts("post_type=post&numberposts=5&taxonomy=category&term={$category}");
    foreach($posts as $post) {
      $category_posts[$post->ID] = $post;
    }
  }
  set_transient('top5_posts_per_category',$category_posts,60*60*$timeout);
}
header('Content-Type:text/plain');
print_r($category_posts);

Riepilogo

Sebbene tu possa fare ciò che hai chiesto usando una query SQL UNION e l'hook filter posts_join, probabilmente è meglio utilizzare la cache con la Transients API.

Spero sia utile?

26 ago 2010 01:23:23
Commenti

La memorizzazione nella cache tramite transient è un'ottima cosa per ridurre l'impatto sul sito.

hakre hakre
26 ago 2010 02:01:48

@Mike ottima idea usare i Transient.

Chris_O Chris_O
26 ago 2010 03:41:30

@Mike, se vivessi nel tuo continente avrei seguito le tue lezioni! grazie ancora!

Amit Amit
26 ago 2010 17:13:21

@Amit: LOL! :-)

MikeSchinkel MikeSchinkel
26 ago 2010 21:21:14

Non esiste un modo per limitare l'hook join a un solo oggetto WP_query?

Manny Fleurmond Manny Fleurmond
18 mag 2011 01:15:51

@Manny Fleurmond - Di solito dico di fare un'altra domanda (cosa che dovresti fare comunque) ma un modo è creare una sottoclasse di WP_Query, aggiungere una proprietà e poi verificare quella proprietà.

MikeSchinkel MikeSchinkel
19 mag 2011 19:44:34

potresti anche salvare il transient indefinitamente e poi svuotarlo su save_post? c'è un buon esempio (usando l'hook edit_term) nel codex

helgatheviking helgatheviking
27 feb 2012 19:22:33

@helgatheviking - Certo, se preferisci.

MikeSchinkel MikeSchinkel
7 set 2014 01:25:15

@MikeSchinkel 2 anni dopo e uso i transients continuamente!

helgatheviking helgatheviking
7 set 2014 01:40:19

@helgatheviking La mia risposta ti ha dato una spinta iniziale?!?

MikeSchinkel MikeSchinkel
7 set 2014 03:27:31

@MikeSchinkel Forse? Non saprei proprio dire. Stai mettendo alla prova i limiti della mia memoria. :)

helgatheviking helgatheviking
7 set 2014 13:11:46
Mostra i restanti 6 commenti
4

WP_Query() non supporta qualcosa come Primi X Per Ogni Categoria. Invece di WP_Query() puoi usare get_posts() su ognuna delle tue categorie, diciamo tre volte:

<?php
$posts      = array();
$categories = array(2,4,8);
foreach($categories as $cat) {
  $posts[] = get_posts('numberposts=5&offset=1&category='.$cat);
}
?>

$posts ora contiene i primi cinque post per ogni categoria.

25 ago 2010 23:54:06
Commenti

non sono 3 accessi al database? Volevo sapere se puoi farlo in un solo accesso perché pensavo fosse più efficiente.

Amit Amit
25 ago 2010 23:56:36

ma è proprio a questo che serve il database, no? Interrogare i dati da esso. Il database è fatto per questo tipo di operazioni. Probabilmente è più veloce che eseguire una complicata query SQL come quella che stai chiedendo. Non aver paura di usare questi strumenti. Se rompe il tuo sito, segnalalo qui.

hakre hakre
26 ago 2010 00:05:45

mm.. capisco, non so molto sull'efficienza di php/mysql/database (mi piacerebbe leggere di più), ero sicuro che meno accessi fossero meglio e poi elaborare il risultato da solo.

Amit Amit
26 ago 2010 00:11:26

beh, vero, generalmente più è meglio e meno è peggio. Ma prova e testa prima. Più "scadente" è il tuo software, più potenziale ha per l'ottimizzazione. Quindi è meglio imparare facendo, perché alla fine scriverai software migliore più velocemente e scriverai più velocemente software migliore.

hakre hakre
26 ago 2010 00:15:11
0

Non conosco un modo per ottenere i primi cinque post per ciascuna delle categorie con una singola query. Se prevedi di avere sempre solo 45 post, allora interrogare il database una volta e ottenere i tuoi cinque post per ogni categoria è probabilmente l'approccio più efficiente. Tuttavia, interrogare il database tre volte e combinare i risultati non è una cosa negativa.

25 ago 2010 23:38:26