web-dev-qa-db-ja.com

複数の投稿メタ日付フィールドを持つ投稿の注文

WordPressの開発者、こんにちは。何年にもわたって、私はWordPressに関して多くの質問に答えてきました。ご協力ありがとうございます。今日、私は自分自身に質問があり、専門家の助言を求めています。

私は映画館のためのサイトを作っています。カスタム投稿タイプ映画があります。私は Types プラグインを使って、各映画の上映日と-timesを繰り返しの日付/時刻フィールドとして保存しています。それはmeta_key "wpcf-showtime"を持つpostmetaテーブルへのUnixタイムスタンプとして日付/時刻を格納します。各映画は多数の上映時間を有し、したがって同じキーを有する多数のmeta_key/meta_valueペアがある。

post_id:70、meta_key:wpcf-showtime、meta_value 1417548600
post_id:70、meta_key:wpcf-showtime、meta_value 1417813200
post_id:70、meta_key:wpcf-showtime、meta_value 1417896000

このサイトのフロントページでは、来週の水曜日までに進行中のすべての映画、つまり過去に上映された映画、または未来としてマークされた映画を除いて、上映時間がある映画を表示したいと思います。

私の問題は映画を注文することです。現在の時間に最も近い上映時間を有する映画が最初に来るように、私はそれらを上映時間順に並べて見せたい。

これは私が実行しているクエリです:

$startdate = time();
$enddate = new DateTime('thursday next week');

$ongoing_movies = new WP_Query( array( 
    'post_type' => 'movies',
    'meta_key'  => 'wpcf-showtime',
    'orderby'   => 'meta_value_num', 
    'order'     => 'ASC',
    'tax_query' => array(
        array(
            'taxonomy' => 'genre',
            'field'    => 'slug',
            'terms'    => 'future', 
            'operator' => 'NOT IN'
        )
    ),
    'meta_query' => array(
        array(
            'key'      => 'wpcf-showtime',
            'value'    => array ( $startdate, $enddate->format('U')),
            'compare'  => 'BETWEEN', 
        ),
    )
));

これはshowtime順に正しい映画を返します。しかし、ソートに使用されるshowtimeは、その特定の映画用に保存されたmeta_key "wpcf-showtime"の最初の値です。すなわち最初に表示された映画は、次の上映があるものではなく、最も早い上映時間がdbに格納されたものであるかもしれません。

私はアプローチを見つける上でどんな助けにも感謝するでしょう。 pre_get_postsアクション、posts_orderbyフィルタ

4

1)posts_orderbyフィルタのみを使用する:

movieswpcf-showtimeメタ値の minimum の順に並べたいとします。

ORDER BYフィルターを使用して、WP_Queryによって生成されたSQLのposts_orderby部分を変更できます。

これが例です:

add_filter( 'posts_orderby', 'wpse_posts_orderby' );
$ongoing_movies = new WP_Query( $args );

フィルタコールバックは次のように定義されています。

/**
 *  Use MIN() on the meta value part ordering.
 */
function wpse_posts_orderby( $orderby )
{      
    global $wpdb;

    // Only run this callback once:
    remove_filter( current_filter(), __FUNCTION__ );

    // Replacement part:  
    $find    = "{$wpdb->postmeta}.meta_value+0 ";
    $replace = "mt1.meta_value+0 ";

    // Make sure we add MIN() to the correct part:    
    if( $find == str_ireplace( array( 'ASC', 'DESC' ), '', $orderby ) )
    {
        // Preserve the order direction:
        $orderby = str_ireplace(
            $find,
            "MIN( $replace) ",
            $orderby
        );
    }
    return $orderby;
}

ここでは、メタ値の制限がmt1.meta_valueの最初の引数配列である場合、meta_queryを使用します。

これは変わります:

ORDER BY wp_postmeta.meta_value+0 {ASC|DESC}

ORDER BY MIN( mt1.meta_value+0 ) {ASC|DESC}

2)WP_Queryにカスタム注文パラメータを導入する

あなたはこのセットアップを使うことを試みることができます:

$startdate = time();
$enddate = new DateTime('thursday next week');

$ongoing_movies = new WP_Query( array( 
    'post_type'    => 'movies',
    'meta_key'     => 'wpcf-showtime',
    'wpse_orderby' => 'meta_value_num_min',      // <-- New parameter to order by MIN!
    'orderby'      => 'meta_value_num', 
    'order'        => 'ASC',
    'tax_query'    => array(
        array(
            'taxonomy' => 'genre',
            'field'    => 'slug',
            'terms'    => 'future', 
            'operator' => 'NOT IN'
        )
    ),
    'meta_query' => array(
        array(
            'key'      => 'wpcf-showtime',
            'value'    => array ( $startdate, $enddate->format('U')),
            'compare'  => 'BETWEEN', 
            'type'     => 'UNSIGNED'     // <-- Let's override the default 'CHAR'
        ),
    )
));

wpse_orderbyパラメータをサポートするために以下のプラグインを使用します。

<?php
/**
 * Plugin Name: Modify the WP_Query ordering to support MAX and MIN
 * Description: Possible values of 'wpse_orderby' are  'meta_value_num_{min,max}'.
 * Plugin URI:  http://wordpress.stackexchange.com/a/173496/26350
 * Author:      Birgir Erlendsson (birgire)
 * Version:     0.0.2
 */ 

add_action( 'init', function(){
    if( class_exists( 'WPSE_Modify_Ordering' ) )
    {
        $o = new WPSE_Modify_Ordering;
        $o->init();
    }
});

class WPSE_Modify_Ordering
{
    private $type    = '';
    private $order   = '';
    private $orderby = '';

    public function init()
    {
        add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
    }

    public function pre_get_posts( WP_Query $q )
    {
        if( 
            in_array( 
                $q->get( 'wpse_orderby' ), 
                array( 'meta_value_num_min', 'meta_value_num_max' ) 
            ) 
        )
        {
            $this->type = strtoupper( substr( $q->get( 'wpse_orderby' ), -3 ) );
            $this->order = ( '' !== $q->get( 'order' ) ) ? $q->get( 'order' ) : 'ASC';
            add_filter( 'posts_orderby', array( $this, 'posts_orderby' ) );
        }
    }

    public function posts_orderby( $orderby )
    {
        remove_filter( current_filter(), array( $this, __FUNCTION__ ) );
        return " {$this->type}( mt1.meta_value+0 ) " . $this->order;
    }

} // end class

wpse_orderbyパラメーターは、ネイティブのorderbyパラメーターをオーバーライドします。値meta_value_num_minおよびmeta_value_num_maxをサポートします。

メタ値の制限はmt1.meta_valueの最初の引数の配列なので、ここではmeta_queryの順序付けを行います。

orderbyパラメータを保持する理由は、上記のプラグインがde-activatedの場合、 fallback として使用するためです。

うまくいけばあなたはあなたのニーズに合わせてこれをさらに修正することができます。

4
birgire

これは一見複雑な質問です。オブジェクトに複数の日付がある場合、クエリの範囲内で文脈上の日付で並べ替えることが特に重要です。伝統的にソートと順序付けは日付の配列の最も低い値を使用します、それは特定の日付範囲を照会するときしばしば誤った順序付けをもたらします。

参考のために、ここで私が私自身のアプローチで使用したコードの裏肉を取り除いた。

// Wrap query in filters to modify behaviour
// ==============  
add_filter('posts_join_paged','doty_custom_posts_join');
add_filter('posts_orderby','doty_custom_posts_orderby');
$new_query = new WP_Query( $args );
remove_filter('posts_orderby','doty_custom_posts_orderby');
remove_filter('posts_join_paged','doty_custom_posts_join');

// Hooks into the 'WHERE' component of the SQL query
// ==============
function doty_custom_posts_join($where) {
  global $wpdb,$query_dates;
  $start_date = (int)$query_dates[0]-86400;
  $where .= "LEFT JOIN (
        SELECT post_id,($start_date - MIN(meta_value)) as orderdate
        FROM $wpdb->postmeta
        WHERE meta_key = 'dates'
        AND meta_value > $start_date 
        GROUP BY post_id) AS date_orders
        ON $wpdb->posts.ID = date_orders.post_id";
  return $where;
}

// Hooks into the 'ORDER BY' component of the SQL query
// ==============
function doty_custom_posts_orderby($orderby) {
  $orderby = " date_orders.orderdate DESC";
  return $orderby;
}

posts_join_pagesposts_orderbyを使用して、WPが通常のWP_Query関数の結果であるSQLクエリを生成する方法を変更します。具体的には、

  • グローバル変数から範囲の開始日を取得します
  • 配列から最も近い日付から選択されるorderdate変数を作成するための新しいLEFT JOINを作成し、これが将来であることの確認に進みます。
  • ソートキーにorderdate変数を使用します。

私はこれを一緒にハッキングするのに6時間を費やしたこと、そしてそれが最もクリーンではないことを私は意識しています、しかしうまくいけばそれにどこかに何らかの価値があると思います。

1
Jono Alderson