I do have a custom post type with the following setup:
$supports = array(
'title'
, 'editor'
, 'thumbnail'
, 'revisions'
, 'page-attributes'
);
$args = array(
'hierarchical' => true
, 'supports' => $supports
[...]
);
register_post_type('myType', $args);
I would like to show all posts and have them sorted like in the wp-admin area (indention is for readability):
1,
2,
3,
1, (parent 3)
2, (parent 3)
4
Therefore I tried the following query with the order-type set to ‘menu_order’:
$loop = new WP_Query( array(
'post_type' => 'myType'
, 'posts_per_page' => 50
, 'orderby' => 'menu_order'
, 'order' => 'ASC'
));
Unfortunately all post are sorted by menu_order
, but by menu_order
exclusively, ignoring the parent relationship (post_parent
attribute). So I get something like this
1,
1, (parent 3)
2,
2, (parent 3)
3,
4
Changing the query to 'orderby' => 'parent menu_order'
leads to the following
1,
2,
3,
4
1, (parent 3)
2, (parent 3)
So all in all it looks to me, as everything works as designed and the orderby-value is directly translated to the corresponding SQL ‘Order By’.
Question
What is the easiest way to get the desired order?
SQL
I assume this is the main sql-query wordpress creates:
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
which is then followed up with:
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
A known workaround, but not an answer is to give all posts higher and more “spaced” order values, like
100,
200,
300,
310,
320,
400
As far as I can tell, there is no work-around to this at the database level. This is a problem I run into somewhat often, which is the case you need to turn an list with structure references into an an ordered array with children elements appearing immediately after their parents. This can be accomplished in PHP, but while this solution is fairly compact, it isn’t terribly straight-forward.
The following solution adds a filter to the
the_posts
filter, which structures and then flattens the result set with a recursive function.I’ve tested this solution and it’s general enough for arbitrary page hierarchies.
This code assumes that the posts are already ordered by
menu_order
. If you are to use this solution, be sure to change theorderby
parameter to just"menu_order"
where you make the call tonew WP_Query
.See the codex for other options, but it looks like you would want to be using ‘parent’ as a primary sort in this case.
This will primarily sort by parent, with a secondary sort of menu order. This should give you your desired result.