具有多个自定义 post 类型和多个日期列名称的复杂 meta_query 和 orderby
Complex meta_query and orderby with multiple custom post types, and multiple date column names
我正在尝试查询两个自定义 post 类型,exhibitions
和 events
。
展览有 exhibition_start_date
和 exhibition_end_date
自定义字段。
事件有一个 event_date
自定义字段。
我将如何在一个 orderby 子句中对多列进行排序?
我一直在看这个以供参考 https://make.wordpress.org/core/2015/03/30/query-improvements-in-wp-4-2-orderby-and-meta_query/。
理想情况下我的结果应该是这样的:
- 展览(2016 年 10 月)
- 活动(2016 年 9 月)
- 事件(2016 年 8 月)
- 展览(2016 年 7 月)
- 活动(2016 年 6 月)
到目前为止,我的查询排序不正确:
$today = date('Ymd');
$past_date = date( 'Ymd', strtotime( $today . ' - 12 months' ) );
$args = array(
'post_type' => array( 'exhibition', 'event' ),
'meta_query' => array(
'relation' => 'OR',
'exhibition_clause' => array(
array (
'key' => 'exhibition_end_date',
'compare' => '<',
'value' => $today,
),
array (
'key' => 'exhibition_end_date',
'compare' => '>=',
'value' => $past_date,
)
),
'event_clause' => array(
array (
'key' => 'event_date',
'compare' => '<',
'value' => $today,
),
array (
'key' => 'event_date',
'compare' => '>=',
'value' => $past_date,
)
)
),
'orderby' => array(
'exhibition_clause' => 'DESC',
'event_clause' => 'DESC',
),
);
编辑:连接到 'posts_orderby' 过滤器后:
过滤器:
function my_exhibition_event_date_posts_orderby( $orderby ) {
if ( is_exhibition_event_query() ) {
unset( $GLOBALS['is_exhibition_event_query'] );
return "IFNULL(mt1.meta_value+0, mt2.meta_value+0) DESC";
}
return $orderby;
}
add_filter( 'posts_orderby', 'my_exhibition_event_date_posts_orderby', 10, 1 );
sql输出:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id ) INNER JOIN wp_postmeta AS mt2 ON ( wp_posts.ID = mt2.post_id ) INNER JOIN wp_postmeta AS mt3 ON ( wp_posts.ID = mt3.post_id ) WHERE 1=1 AND (
(
( wp_postmeta.meta_key = 'exhibition_end_date' AND wp_postmeta.meta_value < '20161023' )
AND
( mt1.meta_key = 'exhibition_end_date' AND mt1.meta_value >= '20080623' )
)
OR
(
( mt2.meta_key = 'event_date' AND mt2.meta_value < '20161023' )
AND
( mt3.meta_key = 'event_date' AND mt3.meta_value >= '20080623' )
)
) AND wp_posts.post_type IN ('exhibition', 'event') AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'acf-disabled' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private') GROUP BY wp_posts.ID ORDER BY IFNULL(mt2.meta_value+0, mt1.meta_value+0) DESC LIMIT 0, 20
不过,这仍然不对。这是 post 返回的顺序:
- 事件(2014 年 10 月 15 日)
- 事件(2014 年 10 月 16 日)
- 事件(2014 年 10 月 31 日)
- 事件(2014 年 8 月 6 日)
- 展览(2016 年 10 月 14 日)
- 展览(2014 年 10 月 22 日)
何时应按结束日期排序,最近的日期在前。
一个选项是为查询 orderby
放置一个挂钩,内容大致如下:
add_filter( 'posts_orderby', 'my_posts_orderby', 10, 1 );
function my_posts_orderby( $orderby ) {
if (is_exhibition_event_query()) {
return 'IFNULL(exhibition_end_date, event_date) ASC';
}
return $orderby;
}
您需要实施 is_exhibition_event_query()
以将此查询修改限制为此特定查询。一种选择是在查询之前设置一个全局变量,并在查询完成后取消设置。
更新
最终查询应如下所示:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts
INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
WHERE
( wp_postmeta.meta_key = 'exhibition_end_date' OR
wp_postmeta.meta_key = 'event_date' )
AND wp_postmeta.meta_value < '20161023'
AND wp_postmeta.meta_value >= '20080623'
AND wp_posts.post_type IN ('exhibition', 'event')
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'acf-disabled' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private')
ORDER BY wp_postmeta.meta_value DESC
LIMIT 0, 20
我不确定这是否可以通过 WP_Query
实现。对于重要的查询,我总是使用直接 SQL 查询(wpdb->get_results()
)。除了 posts_orderby
之外,查询的其他部分也有类似的挂钩,因此有时使用 WP_Query
并使用这些挂钩中的一个或多个调整查询会更直接。
不幸的是,我无法找出正确的查询来直接从数据库中获取我想要的订单。但是,我确实想出了一个简单的 usort
函数来比较元值。
为了进一步复杂化订购此命令所需的 SQL,exhibition_start_date
以 Ymd
格式存储,而 event_date
以 [=15] 格式存储=].
usort( $the_query->posts, 'my_compare_exhibition_and_event_dates_descending' );
function my_compare_exhibition_and_event_dates_descending($a, $b) {
$a_date = get_post_meta( $a->ID, 'exhibition_start_date', true );
if ( empty( $a_date ) ) {
$a_date = get_post_meta( $a->ID, 'event_date', true );
$a_date = DateTime::createFromFormat( 'Y-m-d H:i:s', $a_date )->format( 'Ymd' );
}
$b_date = get_post_meta( $b->ID, 'exhibition_start_date', true );
if ( empty( $b_date ) ) {
$b_date = get_post_meta( $b->ID, 'event_date', true );
$b_date = DateTime::createFromFormat( 'Y-m-d H:i:s', $b_date )->format( 'Ymd' );
}
return ( $a_date > $b_date ) ? -1 : 1;
}
我正在尝试查询两个自定义 post 类型,exhibitions
和 events
。
展览有 exhibition_start_date
和 exhibition_end_date
自定义字段。
事件有一个 event_date
自定义字段。
我将如何在一个 orderby 子句中对多列进行排序?
我一直在看这个以供参考 https://make.wordpress.org/core/2015/03/30/query-improvements-in-wp-4-2-orderby-and-meta_query/。
理想情况下我的结果应该是这样的:
- 展览(2016 年 10 月)
- 活动(2016 年 9 月)
- 事件(2016 年 8 月)
- 展览(2016 年 7 月)
- 活动(2016 年 6 月)
到目前为止,我的查询排序不正确:
$today = date('Ymd');
$past_date = date( 'Ymd', strtotime( $today . ' - 12 months' ) );
$args = array(
'post_type' => array( 'exhibition', 'event' ),
'meta_query' => array(
'relation' => 'OR',
'exhibition_clause' => array(
array (
'key' => 'exhibition_end_date',
'compare' => '<',
'value' => $today,
),
array (
'key' => 'exhibition_end_date',
'compare' => '>=',
'value' => $past_date,
)
),
'event_clause' => array(
array (
'key' => 'event_date',
'compare' => '<',
'value' => $today,
),
array (
'key' => 'event_date',
'compare' => '>=',
'value' => $past_date,
)
)
),
'orderby' => array(
'exhibition_clause' => 'DESC',
'event_clause' => 'DESC',
),
);
编辑:连接到 'posts_orderby' 过滤器后:
过滤器:
function my_exhibition_event_date_posts_orderby( $orderby ) {
if ( is_exhibition_event_query() ) {
unset( $GLOBALS['is_exhibition_event_query'] );
return "IFNULL(mt1.meta_value+0, mt2.meta_value+0) DESC";
}
return $orderby;
}
add_filter( 'posts_orderby', 'my_exhibition_event_date_posts_orderby', 10, 1 );
sql输出:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) INNER JOIN wp_postmeta AS mt1 ON ( wp_posts.ID = mt1.post_id ) INNER JOIN wp_postmeta AS mt2 ON ( wp_posts.ID = mt2.post_id ) INNER JOIN wp_postmeta AS mt3 ON ( wp_posts.ID = mt3.post_id ) WHERE 1=1 AND (
(
( wp_postmeta.meta_key = 'exhibition_end_date' AND wp_postmeta.meta_value < '20161023' )
AND
( mt1.meta_key = 'exhibition_end_date' AND mt1.meta_value >= '20080623' )
)
OR
(
( mt2.meta_key = 'event_date' AND mt2.meta_value < '20161023' )
AND
( mt3.meta_key = 'event_date' AND mt3.meta_value >= '20080623' )
)
) AND wp_posts.post_type IN ('exhibition', 'event') AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'acf-disabled' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private') GROUP BY wp_posts.ID ORDER BY IFNULL(mt2.meta_value+0, mt1.meta_value+0) DESC LIMIT 0, 20
不过,这仍然不对。这是 post 返回的顺序:
- 事件(2014 年 10 月 15 日)
- 事件(2014 年 10 月 16 日)
- 事件(2014 年 10 月 31 日)
- 事件(2014 年 8 月 6 日)
- 展览(2016 年 10 月 14 日)
- 展览(2014 年 10 月 22 日)
何时应按结束日期排序,最近的日期在前。
一个选项是为查询 orderby
放置一个挂钩,内容大致如下:
add_filter( 'posts_orderby', 'my_posts_orderby', 10, 1 );
function my_posts_orderby( $orderby ) {
if (is_exhibition_event_query()) {
return 'IFNULL(exhibition_end_date, event_date) ASC';
}
return $orderby;
}
您需要实施 is_exhibition_event_query()
以将此查询修改限制为此特定查询。一种选择是在查询之前设置一个全局变量,并在查询完成后取消设置。
更新
最终查询应如下所示:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts
INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
WHERE
( wp_postmeta.meta_key = 'exhibition_end_date' OR
wp_postmeta.meta_key = 'event_date' )
AND wp_postmeta.meta_value < '20161023'
AND wp_postmeta.meta_value >= '20080623'
AND wp_posts.post_type IN ('exhibition', 'event')
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'acf-disabled' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private')
ORDER BY wp_postmeta.meta_value DESC
LIMIT 0, 20
我不确定这是否可以通过 WP_Query
实现。对于重要的查询,我总是使用直接 SQL 查询(wpdb->get_results()
)。除了 posts_orderby
之外,查询的其他部分也有类似的挂钩,因此有时使用 WP_Query
并使用这些挂钩中的一个或多个调整查询会更直接。
不幸的是,我无法找出正确的查询来直接从数据库中获取我想要的订单。但是,我确实想出了一个简单的 usort
函数来比较元值。
为了进一步复杂化订购此命令所需的 SQL,exhibition_start_date
以 Ymd
格式存储,而 event_date
以 [=15] 格式存储=].
usort( $the_query->posts, 'my_compare_exhibition_and_event_dates_descending' );
function my_compare_exhibition_and_event_dates_descending($a, $b) {
$a_date = get_post_meta( $a->ID, 'exhibition_start_date', true );
if ( empty( $a_date ) ) {
$a_date = get_post_meta( $a->ID, 'event_date', true );
$a_date = DateTime::createFromFormat( 'Y-m-d H:i:s', $a_date )->format( 'Ymd' );
}
$b_date = get_post_meta( $b->ID, 'exhibition_start_date', true );
if ( empty( $b_date ) ) {
$b_date = get_post_meta( $b->ID, 'event_date', true );
$b_date = DateTime::createFromFormat( 'Y-m-d H:i:s', $b_date )->format( 'Ymd' );
}
return ( $a_date > $b_date ) ? -1 : 1;
}