Tipuri personalizate de postări, WP_Query și 'orderby'

30 aug. 2012, 13:31:32
Vizualizări: 18.9K
Voturi: 4

Am un tip personalizat de postare cu următoarea configurație:

$supports = array(
    'title'
    , 'editor'
    , 'thumbnail'
    , 'revisions'
    , 'page-attributes'
);

$args = array(
  'hierarchical' => true
  , 'supports' => $supports
  [...]
);

register_post_type('myType', $args);

Doresc să afișez toate postările sortate exact ca în zona de admin (indentarea este pentru lizibilitate):

1, 
2, 
3, 
   1, (părinte 3)
   2, (părinte 3)
4

Am încercat următoarea interogare cu orderby setat la 'menu_order':

$loop = new WP_Query( array(
       'post_type' => 'myType'
     , 'posts_per_page' => 50
     , 'orderby' => 'menu_order'
     , 'order' => 'ASC'
));

Din păcate, toate postările sunt sortate doar după menu_order, ignorând relația de părinte (post_parent). Rezultatul este:

1,
1, (părinte 3)
2, 
2, (părinte 3)
3,
4

Modificând interogarea la 'orderby' => 'parent menu_order' se obține:

1,
2, 
3,
4
1, (părinte 3)
2, (părinte 3)

Se pare că totul funcționează conform design-ului, iar valoarea orderby este tradusă direct în clauza SQL 'Order By'.

Întrebare

Care este cea mai simplă metodă de a obține ordinea dorită?

SQL

Presupun că aceasta este interogarea SQL principală generată de WordPress:

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

Urmată de:

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)

Soluție alternativă

O soluție alternativă, dar nu o răspuns, este să atribuiți postărilor valori mai mari și mai "spațiate" pentru ordine:

100,
200, 
300,
   310,
   320,
400
0
Toate răspunsurile la întrebare 2
8

Consultați codex pentru alte opțiuni, dar se pare că în acest caz ați dori să utilizați 'parent' ca sortare principală.

$loop = new WP_Query( array(
       'post_type' => 'myType'
     , 'posts_per_page' => 50
     , 'orderby' => 'parent menu_order'
     , 'order' => 'ASC'
));

Aceasta va sorta în primul rând după părinte, cu o sortare secundară după ordinea din meniu. Acest lucru ar trebui să vă ofere rezultatul dorit.

30 aug. 2012 16:27:25
Comentarii

Mulțumesc Eric, dar din păcate asta duce la 1, 2, 3, 4, 1(părinte 3), 2 (părinte 3).

SunnyRed SunnyRed
30 aug. 2012 16:42:11

Hmm, bizar. Încearcă să le schimbi? Sau să folosești doar părintele? Nu am testat asta, mă bazez doar pe codex.

Eric Holmes Eric Holmes
30 aug. 2012 17:39:07

+1 În sfârșit cineva care a înțeles că un spațiu permite argumente multiple.

kaiser kaiser
30 aug. 2012 19:57:10

@SunnyRed Poți să postezi exact șirul de interogare care iese din asta? (Indiciu: Debug Bar Plugin + Extensions).

kaiser kaiser
30 aug. 2012 19:57:38

Mulțumesc, Kaiser. Sugestia ta cu Debug Bar a făcut deja ca întrebarea mea să merite pusă. Am adăugat sql - sper că e corect, pentru că la prima vedere e destul de mult.

SunnyRed SunnyRed
30 aug. 2012 20:54:46

După cum arată instrucțiunea SQL, ordinea ar trebui inversată la: 'orderby' => 'parent menu_order', deoarece instrucțiunea sql le inversează deja.

Eric Holmes Eric Holmes
30 aug. 2012 21:24:49

Salut, Erik. Scuze, asta s-a întâmplat pentru că am încercat diferite abordări. WP și ordinea SQL sunt sincronizate.

SunnyRed SunnyRed
31 aug. 2012 12:27:33

Motivul acestui comportament este că valoarea 'parent' pentru categoriile de nivel superior va fi 0, iar ordonarea este astfel corectă, deoarece toate intrările al căror părinte este '0' vor apărea primele.

Bendoh Bendoh
5 sept. 2012 04:38:28
Arată celelalte 3 comentarii
1

Din câte văd, nu există o soluție la nivel de bază de date pentru această problemă. Aceasta este o situație întâlnită destul de des, atunci când este nevoie să transformi o listă cu referințe de structură într-un array ordonat, unde elementele copil apar imediat după părinții lor. Acest lucru poate fi realizat în PHP, dar deși soluția este destul de compactă, nu este foarte simplă de înțeles.

Următoarea soluție adaugă un filtru la filtrul the_posts, care structurează și apoi aplatizează setul de rezultate cu o funcție recursivă.

// Adaugă postările copil ale fiecărui nivel în lista de rezultate, în 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 ) {
    // Nu face nimic în afara administrării. Acționează doar pe interogarea principală. Doar pentru postări de tip 'page'.
    if( is_admin() || !$query->is_main_query() || $query->get( 'post_type' ) != 'page' )
        return;

    $refs = $list = array();
    // Creează structura ierarhică într-un singur pas.
    // Mulțumiri lui 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;
    }

    // Creează o listă simplă și sortată
    $result = array();
    recursively_flatten_list( $list, $result );

    return $result;
}
add_filter( 'the_posts', 'my_sort_posts', 10, 2 );

Am testat această soluție și este suficient de generală pentru ierarhii arbitrare de pagini.

Acest cod presupune că postările sunt deja ordonate după menu_order. Dacă folosești această soluție, asigură-te că modifici parametrul orderby la doar "menu_order" acolo unde faci apelul la new WP_Query.

5 sept. 2012 06:37:32
Comentarii

Super! A trebuit să-l modific la $loop->posts = my_sort_posts($loop->posts); fără să adaug filtrul, dar așa funcționează perfect. MERCI!

SunnyRed SunnyRed
7 sept. 2012 15:18:39