web-dev-qa-db-ja.com

動作しないことによるpre_get_postsの順序

実行前にクエリを変更するためにpre_get_postsフックを使用しています。

問題の投稿は公演であるため、日付と時刻に基づいています。

将来のパフォーマンスをすべて取得するようにクエリを変更します(つまり、日付が今日よりも大きい、または日付が今日で、時間が現在の時間以上である)。これは動作しますが、私が主に日付の昇順で、次に時間の昇順でそれらを注文しようとすると、順序は予想通りではありません。

投稿は日付順に並べられていますが、今日の公演のみが時間順に並べられており、他の日の公演の時間はランダムに並べられています。

これは、orderbyパラメータに関連したmeta_query配列の命名に関係していると思いますが、これを解決する方法を考え出すことはできません。

私のコードは次のとおりです。

    //No limit on number to get
    $query->set('posts_per_page', -1);
    //Get future performances
    $query->set('meta_query', array(
        'relation'  => 'AND',   
        array(
            'relation'  => 'OR',
            'performance_date' => array(
                'key' => 'performance_date', 
                'value' => date('Ymd'),
                'compare' => '>',
                'type' => 'NUMERIC'
            ),
            array(
                'relation'  => 'AND',
                'performance_date' => array(
                    'key' => 'performance_date', 
                    'value' => date('Ymd'),
                    'compare' => '=',
                    'type' => 'NUMERIC'
                ),                      
                'performance_time' => array(
                    'key' => 'performance_time', 
                    'value' => date('H:i'),
                    'compare' => '>=',
                    'type' => 'TIME'
                )
            )               
        )
    ));
    //Order by closest performance first
    $query->set('orderby', array(
        'performance_date' => 'ASC',
        'performance_time' => 'ASC'
    )); 
1
Michael

あなたがperformance_datetimeカスタムフィールドを持っているなら、これはあなたのメタクエリを単純化することができるので私はこれがより簡単であろうと思います。

以下はあなたが遊ぶためのデモの楽しみです:

私が問題を理解している限り、wp_postsテーブルに3つの内部結合、つまりwp_postmetamt1およびmt2があります。しかし、これらの結合テーブルのmeta_keyフィールドの値は、必ずしもperformance_dateまたはperformance_timeとは限りません。そのため、これらのテーブルの1つでは、メタ値による順序付けは、WP_Queryによって返されるすべての投稿に対して正確ではありません。

この場合に限り、ハードコーディングされた回避策は次のようになります。

ORDER BY 
   CASE
        WHEN wp_postmeta.meta_key = 'performance_date' 
            THEN CAST( wp_postmeta.meta_value AS SIGNED )
        WHEN mt1.meta_key = 'performance_date' 
            THEN CAST( mt1.meta_value AS SIGNED )
        WHEN mt2.meta_key = 'performance_date' 
            THEN CAST( mt2.meta_value AS SIGNED )
        ELSE
            wp_postmeta.post_id
    END ASC,
    CASE
        WHEN wp_postmeta.meta_key = 'performance_time' 
            THEN CAST( wp_postmeta.meta_value AS TIME)
        WHEN mt1.meta_key = 'performance_time' 
            THEN CAST( mt1.meta_value AS TIME)
        WHEN mt2.meta_key = 'performance_time' 
            THEN CAST( mt2.meta_value AS TIME)
        ELSE
            wp_postmeta.post_id
    END ASC

これをテストすることができます:

$query->set('orderby', 'performance' );

カスタム注文が次のテストされていないプラグインによってサポートされているところ:

add_filter( 'posts_orderby', function( $orderby, \WP_Query $q )
{
    if( 'performance' !== $q->get( 'orderby' ) )
        return $orderby;

    global $wpdb;

    $order = ( 'ASC' === strtoupper( $q->get( 'order' ) ) ) ? 'ASC' : 'DESC';
    $orderby = "
       CASE
            WHEN {$wpdb->postmeta}.meta_key = 'performance_date' 
                THEN CAST( {$wpdb->postmeta}.meta_value AS SIGNED )
            WHEN mt1.meta_key = 'performance_date' 
                THEN CAST( mt1.meta_value AS SIGNED )
            WHEN mt2.meta_key = 'performance_date' 
                THEN CAST( mt2.meta_value AS SIGNED )
            ELSE
                {$wpdb->postmeta}.post_id
       END {$order},
       CASE
            WHEN {$wpdb->postmeta}.meta_key = 'performance_time' 
                THEN CAST( {$wpdb->postmeta}.meta_value AS TIME)
            WHEN mt1.meta_key = 'performance_time' 
                THEN CAST( mt1.meta_value AS TIME)
            WHEN mt2.meta_key = 'performance_time' 
                THEN CAST( mt2.meta_value AS TIME)
            ELSE
                {$wpdb->postmeta}.post_id
        END {$order}";

    return $orderby;
}, 10, 2 );

これはかなり柔軟性がないことに注意してください。クエリを変更したときにはうまくいかないかもしれません。しかし、うまくいけばあなたはあなたのニーズに合わせてこれを調整し、さらに柔軟にすることができます。

2
birgire