pre_get_posts
过滤器修改内置的WP搜索,使用户可以按不同字段对帖子(包括一堆自定义帖子类型)进行排序。我遇到的问题是是当我告诉WP按元值排序时,它将排除所有未设置该元值的帖子。如果将排序从“价格”更改为“日期”,这将导致结果数量发生更改,因为“帖子”未设置“价格”,但设置了“项目”。
这不是我想要的是什么,所以我想知道是否有一种方法可以包含所有帖子-即使那些缺少我正在排序的元值的帖子-并将没有值的帖子放在最后。
我知道如何对多个字段进行排序,但这无济于事。是否在wp_query的args中带有&没有某些meta_key?但是那里没有解决方案。
更新
我已经尝试了答案,但是不确定我是否正确理解,这就是我现在所拥有的:
<?php
function my_stuff ($qry) {
$qry->set('meta_query', array(array(
'key' => 'item_price',
'value' => '',
'compare' => 'NOT EXISTS'
)));
$qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both
$qry->set('order', 'ASC DESC');
$qry->set('meta_key', 'item_price');
}
元值是一个数字(顾名思义,它用于存储价格)
更新2
I' ve注释掉了订单材料,现在我只剩下这个:
<?php
$qry->set('meta_query', array(array(
'key' => 'item_price',
'value' => '',
'compare' => 'NOT EXISTS'
)));
使用此代码,查询似乎返回所有没有
item_price
键的帖子而且没有任何帖子。即问题现在已经解决。如果我也添加订单代码,我将得到0个结果。
编辑:...三年后...:PI再次遇到这个问题。我尝试了所有给出的答案,但没有任何效果。不知道为什么有些人似乎认为他们有用,但至少对我没有用。
我最终解决的方法是使用
save_post
过滤器-确保所有帖子都具有自定义字段希望继续。我必须这样做有点烦人,但只要您尽早这样做,您就可能没有问题。在这种情况下,我在帖子上建立了一个“查看计数器”,希望用户能够对阅读次数最多的帖子进行排序。同样,按视图计数排序时,从未被查看过的帖子(我想这不太可能-但仍然)消失了。我添加了以下代码,以确保所有帖子都具有视图计数:
add_action('save_post', function ($postId) {
add_post_meta($postId, '_sleek_view_count', 0, true);
});
#1 楼
有两种可能的解决方案:1。所有帖子都有meta
我在这里找到的最佳解决方案是给其余帖子/产品商品价格为0。您可以手动执行此操作,也可以循环浏览所有帖子,如果价格为空,然后对其进行更新。
要使其在将来易于管理,您可以加入
save_post
,并在首次添加时为它们提供一个值(仅当其为空白时)。2。多个查询
您可以在执行操作时运行第一个查询,并存储返回的帖子的ID。然后,您可以针对所有帖子和订购日期运行另一个查询,但不包括从第一个查询返回的ID。
然后可以分别打印出两个结果,您将获得所需的结果。
评论
三年后,我又遇到了同样的问题:P必须使用save_post方法(我已经用所使用的代码更新了我的问题)。
–动力浮标
17年7月7日在9:11
无需使用钩子或运行多个查询,这是可能的。看我的答案。
–雅各布·拉库亚(Jacob Raccuia)
20年7月11日在4:51
#2 楼
Easy Peasy,已于2018年经过测试,目前已在生产中使用。$query->set( 'meta_query', array(
'relation' => 'OR',
array(
'key' => 'custom_meta_key',
'compare' => 'EXISTS'
),
array(
'key' => 'custom_meta_key',
'compare' => 'NOT EXISTS'
)
) );
$query->set( 'orderby', 'meta_value title' );
此命令将检查带有和不带有meta键且未指定值的所有项。元查询可靠地为订单提供了密钥。已经过测试。但是,我不确定元查询使用多个键时它将如何工作。
实际示例
/**
* Modifies query before retrieving posts. Sets the
* `meta_query` and `orderby` param when no `orderby`
* param is set, (default ordering).
*
* @param WP_Query $query The full `WP_Query` object.
* @return void
*/
function example_post_ordering( $query ) {
// if not in wp-admin,
// and the query is the main query,
// and the query is not a singular query,
// and the query does not have an orderby param set...
// Note: check for post types, etc. here as desired.
if ( ! is_admin()
&& $query->is_main_query()
&& ! $query->is_singular()
&& empty( $query->get( 'orderby' ) ) ) {
// Setting just `meta_key` is not sufficient, as this
// will ignore posts that do not yet, or never will have
// a value for the specified key. This meta query will
// register the `meta_key` for ordering, but will not
// ignore those posts without a value for this key.
$query->set( 'meta_query', array(
'relation' => 'OR',
array(
'key' => 'custom_meta_key',
'compare' => 'EXISTS'
),
array(
'key' => 'custom_meta_key',
'compare' => 'NOT EXISTS'
)
) );
// Order by the meta value, then by the title if multiple
// posts share the same value for the provided meta key.
// Use `meta_value_num` if the meta values are numeric.
$query->set( 'orderby', 'meta_value title' );
}
}
add_action( 'pre_get_posts', 'example_post_ordering', 10 );
这将订购帖子默认情况下为
custom_meta_key
,并且不会忽略没有该键的值的帖子。评论
仅仅通过阅读代码,似乎要做的就是获取具有custom_meta_key的帖子,以及获取没有custom_meta_key的帖子。随意包含排序的实际工作示例。
–动力浮标
18年4月18日在17:33
没错,这就是它的全部工作,但是以下行负责按meta_value(要查询的meta键的)排序。 $ query-> set('orderby','meta_value title'); (当多个帖子的meta键值相同时,按元值排序,然后按标题排序)。应该使用传入的$ query变量在pre_get_posts挂钩中完成此操作。请记住,询问的问题是如何按元值排序,而不忽略没有该元键值的帖子。
– noahmason
18年4月18日在18:18
@powerbuoy参见更新的实际示例
– noahmason
18年4月18日在18:55
好吧,下次遇到这个问题时,我会尝试一下。
–动力浮标
18年4月27日在18:13
在一个自定义get_posts()调用中为我工作,将带有_featured meta的帖子推到顶部,然后按日期排序。谢谢!
–natebeaty
18年9月9日在18:03
#3 楼
此方法将返回所有帖子,包括带有和不带有meta_key
的帖子,但在订购时会做一些奇怪的事情。该问题的所有不同答案,并通过反复试验来分析生成的SQL。似乎设置array('meta_query' => array('relation' => 'OR'))
会输出一个适当的LEFT JOIN
而不是INNER JOIN
来包含缺少元数据的帖子。指定NOT EXISTS
可以防止WHERE
子句过滤掉缺少meta字段的帖子。对于这个特定的WP_Query
,生成的SQL是(添加了缩进/换行符):add_action('pre_get_posts', 'my_stuff');
function my_stuff ($qry) {
$qry->set(
'meta_query',
array(
'relation' => 'OR', # Matches to this meta_query should be added to those matching the 'meta_key' query
array(
'key' => 'item_price',
'value' => 'bug #23268',
'compare' => 'NOT EXISTS'
)
)
);
$qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both
$qry->set('order', 'ASC DESC');
$qry->set('meta_key', 'item_price');
}
结果是列出所有meta_value为
item_price
的帖子以及那些缺少item_price
的帖子。所有带有item_price
的帖子将相对于彼此正确排序,但是缺少item_price
的帖子将使用一些随机的其他元值(例如,_edit_last
在我的数据库中或其他内部完全免费的内部wordpress元数据中看起来经常是1
)为其在wp_postmeta.meta_value
子句中的ORDER BY
。因此,尽管此方法很接近并且可能适用于某些数据,但它还是被破坏了。所以,我只能说,如果您的item_price
值恰好与MySQL为缺少item_price
的帖子选择的随机元字段冲突,那么这可能对您来说很好。如果您只需要保证item_price
的帖子相对于彼此正确地排序,而不考虑其他帖子的排序,那就可以了。但是我认为这只是wordpress的一个缺点。请纠正我,我希望我错了,并且有一种方法可以解决这个问题;-)。似乎对于
INNER JOIN wp_postmeta
,当给定帖子中缺少postmeta
时,MySQL正在从与该帖子关联的多个meta_key
行中选择一个随机行。从SQL的角度来看,我们需要弄清楚如何告诉wordpress输出ORDER BY mt1.meta_value
。与NULL
不同,当缺少我们要求的meta_key
时,此列正确为wp_postmeta.meta_value
。如果可以的话,SQL会在其他任何值之前对这些NULL
(缺少条目)进行排序,从而为我们提供了明确定义的顺序:首先是所有缺少特定postmeta字段的帖子,其次是具有该字段的帖子。但这就是整个问题:'orderby' => 'meta_value'
只能引用'meta_key' => 'item_price'
,而未混叠的wp_postmeta
始终是INNER JOIN
,而不是LEFT JOIN
,这意味着wp_postmeta.meta_value
和wp_postmeta.meta_key
永远不会是NULL
。所以我想我必须说,这已经用wordpress的内置
WP_Query
来实现了,因为它已被记录(在wordpress-3.9.1中)。烦。因此,如果您实际上需要此功能才能正常工作,则可能需要挂接到其他地方的wordpress中并直接修改生成的SQL。评论
看起来很有前途!下次遇到此问题时,我将尝试此操作。我想立即给您答案,但希望先确认它对我有用。
–动力浮标
14年8月5日在16:09
这使所有内容都无法显示给我。执行此操作后未显示任何内容。
–杰克
16-10-26在11:32
@杰克耶在这里一样。今天又遇到了这个问题,并尝试了这个问题。返回0个结果。
–动力浮标
2015年6月7日9:00
你们正在使用哪个版本的wordpress?我认为这篇文章介绍了如何使用Wordpress不支持的内部未公开API,因此仅当您使用的是wordpress-3.9.1或没有太多版本时才可能有效。
– Binki
17年7月7日在14:40
#4 楼
我想我有一个解决方案。您可以使用两个
meta_key
,其中一个所有帖子都有(like "_thumbnail_id")
,而您希望将meta_key
用作过滤器。所以您的参数:
$qry->set(
'meta_query',
array(
'relation' => 'OR',
array(
'key' => 'item_price',
'value' => '',
'compare' => 'EXISTS'
),
array(
'key' => 'item_price',
'value' => '',
'compare' => 'EXISTS'
)
)
);
$qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both
$qry->set('order', 'ASC DESC');
$qry->set('meta_key', 'item_price');
评论
这里的问题是空字符串比较,将其删除,它可以执行'value'=>'',第二次比较应该不存在,并且不需要最后一条设置指令
–点头
18年4月24日在15:19
#5 楼
这里每个人都必须解决的问题与元查询的顺序有关。为了正确排序,您需要将“ NOT EXISTS”查询放在“ EXISTS”查询之前。原因是因为WordPress使用了最后一个“ LEFT JOIN”语句的meta_value在“ ORDER BY”子句中。
例如:
$pageQuery = new WP_Query([
'meta_query' => [
'relation' => 'OR',
['key' => 'item_price', 'compare' => 'NOT EXISTS'], // this comes first!
['key' => 'item_price', 'compare' => 'EXISTS'],
],
'order' => 'DESC',
'orderby' => 'meta_value_num',
'post_status' => 'publish',
'post_type' => 'page',
'posts_per_page' => 10,
]);
#6 楼
我也遇到了类似的问题,以下解决方案对我有帮助:$args = array(
'post_type' => 'kosh_products',
'posts_per_page' => -1,
'meta_query' => array(
'relation' => 'OR',
'category_sort_order' => array(
'key' => '_sort_order',
'compare' => 'EXISTS'
),
'category_sort_order_not_exists' => array(
'key' => '_sort_order',
'compare' => 'NOT EXISTS'
),
),
'orderby' => array(
'category_sort_order' => 'ASC',
'date' => 'ASC'
));
$query = new WP_Query( $args );
我在WordPress Codex上找到了标题为“'orderby'且带有多个'meta_key's'的描述:https ://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters
#7 楼
如果合适,您可以在每次保存或更新帖子时添加默认元值(如果该元值不存在)。function addDefaultMetaValue($post_id) {
add_post_meta($post_id, 'item_price', 0, true);
}
add_action('save_post', 'addDefaultMetaValue');
如果使用自定义帖子类型,将
add_action('save_post', 'addDefaultMetaValue');
替换为add_action('save_post_{post_type}', 'addDefaultMetaValue');
,例如add_action('save_post_product', 'addDefaultMetaValue');
#8 楼
我自己遇到了数字元值的问题,并指出查询的顺序也很重要。对我来说,NOT EXISTS
查询必须是第一个查询。示例:
$query->set( 'orderby', 'meta_value_num' );
$query->set( 'meta_query', [
'relation' => 'OR',
[ 'key' => 'your_meta_name', 'compare' => 'NOT EXISTS' ],
[
'key' => 'your_meta_name',
'compare' => 'EXISTS',
],
] );
为了获得正确的数字方向,这也很重要值是要设置为
’orderby’
的常规’meta_value_num’
。否则,对于数字值,您会有奇怪的结果。 g .: 1,2,20,21,3,4,5…
代替:
1,2,3, 4,5…20,21
#9 楼
$query = new WP_Query( array (
'meta_key' => 'your_keys_name',
'orderby' => 'meta_value',
'order' => 'DESC',
'meta_query' => array( array(
'key' => 'your_meta_key',
'value' => '',
'compare' => 'NOT EXISTS',
// 'type' => 'CHAR',
) )
) );
如果有数值,则改用
orderby
。免责声明:这是未经测试,但应该可以。关键是您需要指定
meta_value
和meta_value_num
值。否则,您将无法与不存在的值进行比较,这将使查询两种类型的帖子成为可能。这有点像黑客,但只要能奏效... 评论
感谢您的回答,请检查我更新的问题,我不确定我是否正确理解您。
–动力浮标
13年6月10日,0:05
我仍然没有完成这项工作,因此,如果您有解决方案,我很想知道我在做什么错。另外,如果您要领取奖金,我也提供悬赏:stackoverflow.com/questions/17016770/…
–动力浮标
13年6月20日在2:45
两件事情。 “ your_keys_name”和“ your_meta_key”都应该是相同的字符串,而不是不同的字符串,否则您似乎误解了这个问题。其次,我在本地设置上对此进行了测试,它排除了存在键的任何帖子(通过meta_query),排除了缺少键的任何帖子(通过meta_key),导致没有帖子显示。但是,此答案是迈向至少要用;-)表示的步骤。
– Binki
2014年7月31日下午4:16
哦,有趣的是,如果您仅向meta_query添加'relation'=>'OR',那么此答案确实有效。古怪的东西o_o。
– Binki
2014年7月31日下午4:18
@binki只需对我的问题进行编辑,然后更改您认为应该更改的位即可。这是一个社区驱动的网站:)
– kaiser
2014年7月31日在9:58
#10 楼
我认为@kaiser想要做的是通过应用某种虚拟机where条件不过滤任何这些帖子的方式来告诉查询返回具有该meta键的所有帖子。因此,如果您知道自定义字段可以采用的所有值都是x,y,z,则可以说“ WHERE meta_key IN(x,y,z)”,但是这样做的目的是可以通过说出!=(' '):$query = new WP_Query( array (
'orderby' => 'meta_value_num',
'order' => 'DESC',
'meta_query' => array( array(
'key' => 'item_price',
'value' => '',
'compare' => '!=',
) )
) );
也没有经过测试,但是觉得值得尝试:-)。
评论
现在尚无法真正测试,但可以肯定的是,它只会返回设置了item_price而不是“”的帖子。
–动力浮标
13年8月23日在11:13
#11 楼
我最终遇到了一点麻烦(IMHO),但是对于我来说,它确实对我有用。您可以连接到过滤器posts_join_paged和posts_orderby来更新联接和顺序字符串。只要您首先加入该字段,就可以根据自己的需要进行排序,而不是假设该特定帖子必须存在该字段,而不是WP_Query。然后,您可以从WP_Query参数中删除
meta_key
,orderby
和`order。下面是一个示例。在某些情况下,我必须在每个函数的顶部都进行转义,因为它将把它添加到使用WP_Query的所有内容中。您可能需要修改它以满足您的特定需求。可悲的是,缺少这两个过滤器的文档,所以...祝您好运! :)
add_filter('posts_join_paged', 'edit_join', 999, 2);
add_filter('posts_orderby', 'edit_orderby', 999, 2);
/**
* Edit join
*
* @param string $join_paged_statement
* @param WP_Query $wp_query
* @return string
*/
function edit_join($join_paged_statement, $wp_query)
{
global $wpdb;
if (
!isset($wp_query->query)
|| $wp_query->is_page
|| $wp_query->is_admin
|| (isset($wp_query->query['post_type']) && $wp_query->query['post_type'] != 'my_custom_post_type')
) {
return $join_paged_statement;
}
$join_to_add = "
LEFT JOIN {$wpdb->prefix}postmeta AS my_custom_meta_key
ON ({$wpdb->prefix}posts.ID = my_custom_meta_key.post_id
AND my_custom_meta_key.meta_key = 'my_custom_meta_key')
";
// Only add if it's not already in there
if (strpos($join_paged_statement, $join_to_add) === false) {
$join_paged_statement = $join_paged_statement . $join_to_add;
}
return $join_paged_statement;
}
/**
* Edit orderby
*
* @param string $orderby_statement
* @param WP_Query $wp_query
* @return string
*/
function edit_orderby($orderby_statement, $wp_query)
{
if (
!isset($wp_query->query)
|| $wp_query->is_page
|| $wp_query->is_admin
|| (isset($wp_query->query['post_type']) && $wp_query->query['post_type'] != 'my_custom_post_type')
) {
return $orderby_statement;
}
$orderby_statement = "my_custom_meta_key.meta_value DESC";
return $orderby_statement;
}
评论
该代码有效。但是meta_value是作为字符串处理的。因此6的等级更高,为50。可以进行任何修改以将其视为数字吗?
–开车兜风
17年1月24日在17:59
@Drivingralle cast(my_custom_meta_key.meta_value为unsigned)DESC应该可以解决问题...
–tfrommen
17年2月2日在21:34
谢谢@tfrommen。 $ orderby_statement =“ cast(my_custom_meta_key.meta_value为未签名)DESC”;效果很好。
–开车兜风
17年2月6日在13:22
#12 楼
此解决方案对我有用:add_action( 'pre_get_posts', 'orden_portfolio' );
function orden_portfolio( $query ) {
if( ! is_admin() ) {
$query->set( 'orderby', 'meta_value_num' );
$query->set( 'order', 'ASC' );
$query->set( 'meta_query', [
'relation' => 'OR',
[
'key' => 'ce_orden',
'compare' => 'NOT EXISTS' ],
[
'key' => 'ce_orden',
'compare' => 'EXISTS',
],
] );
return $query;
}
}
但是,此解决方案首先显示具有null meta_value的记录。此其他解决方案显示ASC顺序,并在末尾显示空值:
function custom_join($join) {
global $wpdb;
if( ! is_admin() ) {
$join .= $wpdb->prepare(
' LEFT JOIN ' . $wpdb->postmeta . ' cpm ON cpm.post_id = ' . $wpdb->posts . '.ID AND cpm.meta_key = %s'
, 'ce_orden' );
}
return $join;
}
add_filter('posts_join','custom_join');
function custom_orderby($orderby_statement){
global $wpdb;
if ( ! is_admin() ) {
$orderby_statement = "CAST( COALESCE(cpm.meta_value,99999) as SIGNED INTEGER) ASC";
}
return $orderby_statement;
}
add_filter('posts_orderby','custom_orderby', 10, 2 );
#13 楼
我能够通过使用一个查询和适当的参数来解决此问题。 WP 4.1+$args = array(
'posts_per_page' => -1,
'post_status' => 'publish',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'custom_sort',
'compare' => 'EXISTS'
),
array(
'key' => 'custom_sort',
'compare' => 'NOT EXISTS'
)
),
'orderby' => 'meta_value_num title',
'order' => 'ASC',
));
$query = new WP_Query($args);
在这里查看我的完整答案:https://wordpress.stackexchange.com/a/370841/15209
评论
请向我们显示您的代码。使其更易于回答。首先:meta_query和tax_query始终是一个array(array()),因为它们组合了多个数组。第二-正如我的答案中提到的-您需要使用meta_value_num作为数字。可能还需要实际定义meta_value_num(请参阅WP_Query-Codex页面条目)。最后,按ASC和DESC方向排序没有任何意义。那不可能空格分隔符仅适用于orderby,您不能告诉它对第一个ASC和第二个DESC进行排序。这就是posts_clauses过滤器的作用。
并确保您的meta_value_num条目是实数。经常看到有人指出这是一个数字,但实际上将其另存为字符串在数据库中。
感谢您的帮助,我会尽力帮助您。 ASC DESC的原因是,据我所知,它可以对ASC中的meta_value和DESC中的日期进行排序。
@Howdy_McGee是正确的。我的某些自定义帖子类型已设置了此值。有些没有。而内置的帖子类型(例如POST和PAGE)则没有。因此,每当我尝试对自定义字段进行排序时,只会显示带有自定义字段的帖子。