web-dev-qa-db-ja.com

カスタム投稿タイプ、WP_Query、 'orderby'

次のような設定のカスタム投稿タイプがあります。

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

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

register_post_type('myType', $args);

すべての投稿を表示し、それらをwp-admin領域のようにソートしてもらいたい(インデントは読みやすくするためです)。

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

そのため、order-typeを 'menu_order'に設定して次のクエリを試しました。

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

残念ながら、すべての投稿はmenu_orderでソートされていますが、親の関係(menu_order属性)を無視してpost_parentで排他的にソートされています。だから私はこのようなものを得る

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

クエリを'orderby' => 'parent menu_order'に変更すると、次のようになります。

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

つまり、すべてが設計どおりに機能し、orderby値が対応するSQLの「Order By」に直接変換されるため、全体としては私には見えます。

質問

希望の注文を得るための最も簡単な方法は何ですか?

SQL

これはwordpressが作成する主要なSQLクエリだと思います。

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

それはその後でフォローアップされます:

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

次のように、既知の回避策ではありますが、答えではありませんが、すべての投稿に高い間隔と「間隔の広い」順序の値を指定します。

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

私が言うことができる限り、データベースレベルでこれに対する回避策はありません。これは私がよく遭遇する問題です、それはあなたが構造参照を持つリストをそれらの親の直後に現れる子要素を持つ順序配列に変える必要がある場合です。これはPHPで実現できますが、この解決策はかなりコンパクトですが、それほど単純ではありません。

次の解決策は、the_postsフィルタにフィルタを追加します。これは、再帰関数を使用して結果セットを構造化して平坦化します。

// 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 );

私はこの解決策をテストしました、そしてそれは任意のページ階層にとって十分に一般的です。

このコードは、投稿が既にmenu_orderによって順序付けられていることを前提としています。この解決策を使用する場合は、必ずorderbyパラメーターを"menu_order"を呼び出す場所のnew WP_Queryに変更してください。

5
Bendoh

他のオプションについては codex を参照してください。ただし、この場合は主なソートとして「parent」を使用したいと思うでしょう。

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

これは主に親によってソートされ、メニューの順序は2番目になります。これはあなたにあなたの望ましい結果を与えるはずです。

6
Eric Holmes