WordPress - 按元值排序(如果存在),否则为date

时间:2013-06-10 04:09:42

标签: php sql wordpress

我正在尝试允许我的用户按照我的不同自定义字段对搜索结果进行排序。

我正在使用pre_get_posts过滤器,一切正常,除了一件事。

我遇到的问题是,当使用自定义字段进行排序时,只有具有该自定义字段集的帖子才会显示在搜索中。

显然,当用户更改搜索结果的排序方式时,搜索结果的数量会发生变化,这是不可接受的。

我想要的是具有自定义字段的帖子按顺序首先显示,然后其余帖子显示在日期排序。

以下是我的相关代码:

<?php
add_filter('pre_get_posts', 'h5b_search_pre_get_posts');

function h5b_search_pre_get_posts ($qry) {
    $validOrders    = array('price', 'date', 'popularity');
    $orderBy        = (isset($_GET['myorder']) and in_array($_GET['myorder'], $validOrders)) ? $_GET['myorder'] : 'price';

    if ($qry->is_main_query() and $qry->query_vars['s'] != '') {
        # This only includes the posts that have "item_price" set
        if ($orderBy == 'price') {
            $qry->set('orderby', 'meta_value_num date');
            $qry->set('order', 'ASC DESC');
            $qry->set('meta_key', 'item_price');
        }
        # This works fine and includes all posts (obviously)
        elseif ($orderBy == 'date') {
            $qry->set('orderby', 'date');
            $qry->set('order', 'DESC');
        }
    }
}

编辑:这是实际的MySQL查询的样子。我如何更改此内容以便在wp_postmeta.meta_value上进行排序(如果存在) - 如果不存在,则按日期排序?

SELECT 
    SQL_CALC_FOUND_ROWS wp_posts.ID 
FROM 
    wp_posts 
INNER JOIN 
    wp_postmeta 
ON 
    (wp_posts.ID = wp_postmeta.post_id) 
WHERE 
    1=1 AND 
    ((
        (wp_posts.post_title LIKE '%lorem%') OR 
        (wp_posts.post_content LIKE '%lorem%')
    )) AND 
    wp_posts.post_type IN ('post', 'page', 'attachment', 'items', 'locations') AND 
    (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private') AND 
    (wp_postmeta.meta_key = 'item_price' ) 
GROUP BY 
    wp_posts.ID 
ORDER BY 
    wp_postmeta.meta_value+0,wp_posts.post_date DESC LIMIT 0, 6

我最好使用WP_Query方法解决它,但如果必须,我可以考虑运行自己的SQL。

Edit2:我觉得我需要使用像IF NOT NULL这样的东西 - 你会用WP_Query怎么做?它甚至可能吗?

编辑(再次):这是同一个问题(我认为:P):https://wordpress.stackexchange.com/questions/28409/way-to-include-posts-both-with-without-certain-meta-key-in-args-for-wp-query

5 个答案:

答案 0 :(得分:2)

这里的问题不是数据没有按日期列排序,问题是没有返回空meta_value的数据。

您没有看到未指定元数据的条目的原因是INNER JOIN可能会针对wp_postmeta.post_id is null的情况过滤掉LEFT OUTER JOIN。将联接更改为SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts LEFT OUTER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) WHERE 1=1 AND (( (wp_posts.post_title LIKE '%lorem%') OR (wp_posts.post_content LIKE '%lorem%') )) AND wp_posts.post_type IN ('post', 'page', 'attachment', 'items', 'locations') AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private') AND (wp_postmeta.meta_key = 'item_price' OR wp_postmeta.meta_key is NULL ) GROUP BY wp_posts.ID ORDER BY wp_postmeta.meta_value+0,wp_posts.post_date DESC LIMIT 0, 6

WP_Query

更新

要更改$join中的联接类型,请使用posts_join属性设置{{1}}过滤器,如Plugin API/Filter Reference/posts join

中所述

另见WP_Query Class reference

答案 1 :(得分:2)

它不会漂亮(或优化),但我认为你可以在这里使用IFCASE

ORDER BY 
CASE wp_postmeta.meta_value WHEN IS NOT NULL THEN wp_postmeta.meta_value END ASC,
CASE wp_postmeta.meta_value WHEN IS NULL THEN wp_posts.post_date END DESC

注意:我自己没有尝试过,所以可能存在语法错误,但理论上应该有效

进一步阅读: Can you add an if statement in ORDER BY?

答案 2 :(得分:0)

我想我有一个解决方案..

使用两个meta_key,一个所有帖子都有(如“_thumbnail_id”), 和你希望用作过滤器的meta_key ..  所以你的args:

$qry->set( 
'meta_query',
 array(
 'relation' => 'OR',
 array( 'key' => 'item_price', 'value' => '', 'compare' => 'EXISTS' ),
 array( 'key' => '_thumbnail_id', 'value' => '', 'compare' => 'EXISTS' )
 ));

$qry->set('orderby', 'meta_value date'); 
$qry->set('order', 'ASC DESC'); 
$qry->set('meta_key', 'item_price');

答案 3 :(得分:0)

我知道,这个帖子已经有6个月大了,但似乎从来没有足够的答案。我刚刚遇到与问题的原始作者完全相同的问题。所以这是我发现的最终SQL查询,并返回所需的结果:

SELECT SQL_CALC_FOUND_ROWS * 
FROM wp_posts 
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = 'showPostInBanner' AND mt1.meta_value='1') 
LEFT JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) 
WHERE 1=1 AND ( wp_term_relationships.term_taxonomy_id IN (34,47,51,50,68,78,82,90,97,155,227,253,285,294,314,373,425,436,452,456,521,627,667,680,694,710,730,741,751) ) 
AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID 
ORDER BY mt1.meta_value+0 DESC, wp_posts.post_date DESC LIMIT 0, 4

基于此(我的查询试图获得稍微不同的结果)您的查询应该看起来像这样使它工作(如果我没有包含任何新错误 - 但我的查询现在肯定有效):

SELECT SQL_CALC_FOUND_ROWS * 
FROM wp_posts 
LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = 'showPostInBanner' AND mt1.meta_value='1') 
LEFT JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) 
WHERE 1=1 AND ((
        (wp_posts.post_title LIKE '%lorem%') OR 
        (wp_posts.post_content LIKE '%lorem%')
    )) AND 
AND wp_posts.post_type IN ('post', 'page', 'attachment', 'items', 'locations') AND 
    (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private')
GROUP BY wp_posts.ID 
ORDER BY mt1.meta_value+0 DESC, wp_posts.post_date DESC LIMIT 0, 6

答案 4 :(得分:-1)

对于Edit2: 是的,可以使用meta_query。试试

'meta_query' => array(
    array(
                        'key' => 'meta_key',
                        'value' => 'some value', //try to put null here 
                        'compare' => '!='
                        )
    ),