Tipo di post personalizzato, WP_Query e 'orderby'
Ho un tipo di post personalizzato con la seguente configurazione:
$supports = array(
'title'
, 'editor'
, 'thumbnail'
, 'revisions'
, 'page-attributes'
);
$args = array(
'hierarchical' => true
, 'supports' => $supports
[...]
);
register_post_type('myType', $args);
Vorrei mostrare tutti i post e averli ordinati come nell'area di amministrazione di WordPress (l'indentazione è per migliorare la leggibilità):
1,
2,
3,
1, (genitore 3)
2, (genitore 3)
4
Per questo ho provato la seguente query con il tipo di ordinamento impostato su 'menu_order':
$loop = new WP_Query( array(
'post_type' => 'myType'
, 'posts_per_page' => 50
, 'orderby' => 'menu_order'
, 'order' => 'ASC'
));
Sfortunatamente tutti i post vengono ordinati solo per menu_order
, ignorando completamente la relazione genitore-figlio (attributo post_parent
). Quindi ottengo qualcosa del genere:
1,
1, (genitore 3)
2,
2, (genitore 3)
3,
4
Modificando la query in 'orderby' => 'parent menu_order'
si ottiene:
1,
2,
3,
4
1, (genitore 3)
2, (genitore 3)
Quindi sembra che tutto funzioni come previsto e il valore orderby venga tradotto direttamente nel corrispondente SQL 'Order By'.
Domanda
Qual è il modo più semplice per ottenere l'ordinamento desiderato?
SQL
Presumo che questa sia la query SQL principale che WordPress crea:
SELECT SQL_CALC_FOUND_ROWS wp_2_posts.ID
FROM wp_2_posts
WHERE 1=1 AND wp_2_posts.post_type = 'inhalt' AND (wp_2_posts.post_status = 'publish' OR wp_2_posts.post_status = 'private')
ORDER BY wp_2_posts.post_parent, wp_2_posts.menu_order ASC LIMIT 0, 50
seguita da:
SELECT wp_2_posts.*
FROM wp_2_posts
WHERE ID IN (40,42,44,46,48,50,52,54,56,58,60,76,62,65,69,71,74)
SELECT post_id, meta_key, meta_value
FROM wp_2_postmeta
WHERE post_id IN (40,42,44,46,48,50,52,54,56,58,60,62,65,74,69,71,76)
Soluzione alternativa
Una soluzione alternativa nota, ma non una risposta definitiva, è assegnare a tutti i post valori di ordinamento più alti e "spaziati", come:
100,
200,
300,
310,
320,
400

Consulta il codex per altre opzioni, ma sembra che in questo caso tu voglia utilizzare 'parent' come ordinamento primario.
$loop = new WP_Query( array(
'post_type' => 'myType'
, 'posts_per_page' => 50
, 'orderby' => 'parent menu_order'
, 'order' => 'ASC'
));
Questo ordinerà principalmente per genitore, con un ordinamento secondario per menu_order. Dovrebbe darti il risultato desiderato.

Grazie Eric, ma sfortunatamente questo porta a 1, 2, 3, 4, 1(parent 3), 2 (parent 3).

Mmh bizzarro. Prova a scambiarli? O usa solo parent? Non ho testato questo, mi baso solo sul codex.

+1 Finalmente qualcuno che ha capito che uno spazio permette argomenti multipli.

@SunnyRed Potresti per favore pubblicare l'esatta stringa di query che esce da quello? (Suggerimento: Debug Bar Plugin + Estensioni).

Grazie Kaiser. Il tuo suggerimento sulla debug bar ha già reso utile la mia domanda. Ho aggiunto la sql - spero sia quella corretta, dato che a prima vista ce n'è parecchia.

A giudicare dall'istruzione SQL, l'ordinamento dovrebbe essere invertito in:
'orderby' => 'parent menu_order'
, visto che l'istruzione sql li sta già invertendo.

Ciao, Erik. Scusa, è stato perché ho provato approcci diversi. WP e SQL sono sincronizzati nell'ordinamento.

Per quanto ne so, non esiste una soluzione alternativa a livello di database. Questo è un problema che incontro abbastanza spesso, ovvero quando è necessario trasformare una lista con riferimenti strutturali in un array ordinato con gli elementi figli che appaiono immediatamente dopo i loro genitori. Questo può essere realizzato in PHP, e sebbene la soluzione sia piuttosto compatta, non è del tutto immediata.
La soluzione seguente aggiunge un filtro al filtro the_posts
, che struttura e poi appiattisce il set di risultati con una funzione ricorsiva.
// Aggiunge i post figli di ogni livello alla lista dei risultati, in ordine
function recursively_flatten_list( $list, &$result ) {
foreach( $list as $node ) {
$result[] = $node['post'];
if( isset( $node['children'] ) )
recursively_flatten_list( $node['children'], $result );
}
}
function my_sort_posts( $posts, $query ) {
// Non eseguire fuori dall'admin. Opera solo sulla query principale. Opera solo su query per pagine.
if( is_admin() || !$query->is_main_query() || $query->get( 'post_type' ) != 'page' )
return;
$refs = $list = array();
// Crea la struttura gerarchica in un unico passaggio.
// Grazie ancora a Nate Weiner:
// http://blog.ideashower.com/post/15147134343/create-a-parent-child-array-structure-in-one-pass
foreach( $posts as $post ) {
$thisref = &$refs[$post->ID];
$thisref['post'] = $post;
if( $post->post_parent == 0)
$list[$post->ID] = &$thisref;
else
$refs[$post->post_parent]['children'][$post->ID] = &$thisref;
}
// Crea una singola lista ordinata
$result = array();
recursively_flatten_list( $list, $result );
return $result;
}
add_filter( 'the_posts', 'my_sort_posts', 10, 2 );
Ho testato questa soluzione ed è abbastanza generica da funzionare con gerarchie di pagine arbitrarie.
Questo codice presuppone che i post siano già ordinati per menu_order
. Se intendi utilizzare questa soluzione, assicurati di modificare il parametro orderby
impostandolo semplicemente a "menu_order"
dove effettui la chiamata a new WP_Query
.
