web-dev-qa-db-fra.com

Type de message personnalisé, WP_Query et 'orderby'

J'ai un type de message personnalisé avec la configuration suivante:

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

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

register_post_type('myType', $args);

Je souhaite afficher tous les articles et les trier comme dans le domaine wp-admin (l'indentation est pour la lisibilité):

1, 
2, 
3, 
   1, (parent 3)
   2, (parent 3)
4

J'ai donc essayé la requête suivante avec le type d'ordre défini sur 'menu_order':

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

Malheureusement, toutes les publications sont triées par menu_order, mais exclusivement par menu_order, en ignorant la relation parent (attribut post_parent). Alors je reçois quelque chose comme ça

1,
1, (parent 3)
2, 
2, (parent 3)
3,
4

Changer la requête en 'orderby' => 'parent menu_order' conduit à ce qui suit

1,
2, 
3,
4
1, (parent 3)
2, (parent 3)

Cela me semble donc tout à fait, puisque tout fonctionne comme prévu et que la valeur orderby est traduite directement dans le code "Order By" de SQL correspondant.

Question

Quel est le moyen le plus simple d'obtenir la commande souhaitée?

SQL

Je suppose que c’est la principale requête sql-query créée par 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

qui est ensuite suivi 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)

Workaround

Une solution connue, mais qui n’est pas une solution, consiste à attribuer à toutes les publications des valeurs d’ordre plus élevées et plus "espacées", telles que

100,
200, 
300,
   310,
   320,
400
4
SunnyRed

Autant que je sache, il n'y a pas de solution de rechange au niveau de la base de données. C’est un problème que je rencontre assez souvent. Vous devez donc transformer une liste avec des références de structure en un tableau ordonné avec des éléments enfants apparaissant immédiatement après leurs parents. Cela peut être accompli en PHP, mais bien que cette solution soit assez compacte, elle n’est pas terriblement simple.

La solution suivante ajoute un filtre au filtre the_posts, qui structure puis aplatit le jeu de résultats avec une fonction récursive.

// Add each level's child posts to the result list, in order
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 ) {
    // Don't do outside admin. Only operate on main query. Only operate on queries for pages.
    if( is_admin() || !$query->is_main_query() || $query->get( 'post_type' ) != 'page' )
        return;

    $refs = $list = array();
    // Make heirarchical structure in one pass.
    // Thanks again, 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;
    }

    // Create single, sorted list
    $result = array();
    recursively_flatten_list( $list, $result );

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

J'ai testé cette solution et elle est assez générale pour les hiérarchies de pages arbitraires.

Ce code suppose que les articles sont déjà commandés par menu_order. Si vous devez utiliser cette solution, veillez à définir le paramètre orderby sur "menu_order" uniquement, à l'endroit où vous appelez new WP_Query.

5
Bendoh

Voir le codex pour d'autres options, mais il semblerait que vous souhaitiez utiliser "parent" comme type principal dans ce cas.

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

Cela triera principalement par parent, avec une sorte d'ordre secondaire de menu. Cela devrait vous donner le résultat souhaité.

6
Eric Holmes