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フィルタ
posts_orderby
フィルタのみを使用する:moviesをwpcf-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}
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 として使用するためです。
うまくいけばあなたはあなたのニーズに合わせてこれをさらに修正することができます。
これは一見複雑な質問です。オブジェクトに複数の日付がある場合、クエリの範囲内で文脈上の日付で並べ替えることが特に重要です。伝統的にソートと順序付けは日付の配列の最も低い値を使用します、それは特定の日付範囲を照会するときしばしば誤った順序付けをもたらします。
参考のために、ここで私が私自身のアプローチで使用したコードの裏肉を取り除いた。
// 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_pages
とposts_orderby
を使用して、WPが通常のWP_Query関数の結果であるSQLクエリを生成する方法を変更します。具体的には、
orderdate
変数を作成するための新しいLEFT JOINを作成し、これが将来であることの確認に進みます。orderdate
変数を使用します。私はこれを一緒にハッキングするのに6時間を費やしたこと、そしてそれが最もクリーンではないことを私は意識しています、しかしうまくいけばそれにどこかに何らかの価値があると思います。