Tipo di post personalizzato, WP_Query e 'orderby'

30 ago 2012, 13:31:32
Visualizzazioni: 18.9K
Voti: 4

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
0
Tutte le risposte alla domanda 2
8

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.

30 ago 2012 16:27:25
Commenti

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

SunnyRed SunnyRed
30 ago 2012 16:42:11

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

Eric Holmes Eric Holmes
30 ago 2012 17:39:07

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

kaiser kaiser
30 ago 2012 19:57:10

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

kaiser kaiser
30 ago 2012 19:57:38

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.

SunnyRed SunnyRed
30 ago 2012 20:54:46

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

Eric Holmes Eric Holmes
30 ago 2012 21:24:49

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

SunnyRed SunnyRed
31 ago 2012 12:27:33

Il motivo di questo comportamento è che il valore di 'parent' per le categorie di primo livello sarà 0, e quindi l'ordinamento è corretto, poiché tutte le voci il cui genitore è '0' verranno visualizzate per prime.

Bendoh Bendoh
5 set 2012 04:38:28
Mostra i restanti 3 commenti
1

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.

5 set 2012 06:37:32
Commenti

Fantastico! Ho dovuto modificarlo in $loop->posts = my_sort_posts($loop->posts); senza aggiungere il filtro, ma in questo modo funziona alla perfezione. GRAZIE!

SunnyRed SunnyRed
7 set 2012 15:18:39