【问题标题】:WordPress - order by meta value if it exists, else dateWordPress - 如果存在,则按元值排序,否则按日期排序
【发布时间】:2013-06-05 16:32:15
【问题描述】:

我试图让我的用户按我拥有的不同自定义字段对搜索结果进行排序。

我正在使用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

【问题讨论】:

    标签: php sql wordpress


    【解决方案1】:

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

    您看不到未指定元数据的条目的原因是,在wp_postmeta.post_id is null 的情况下,INNER JOIN 可能会过滤掉它。将连接更改为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 过滤器,如Plugin API/Filter Reference/posts join 中所述

    另见WP_Query Class reference

    【讨论】:

    • 感谢您的回答。你知道是否有任何方法可以使用WP_Query 类将INNER JOIN 更改为LEFT OUTER JOIN?我非常希望使用我自己的 SQL,而是使用 WP API。
    • @powerbuoy 将$join 属性设置为posts_join 过滤器值。查看更新的答案。
    • 我在 PHPMyAdmin 中尝试过,但不幸的是,两个查询(INNER JOINLEFT OUTER JOIN)返回的结果数量完全相同。如果我完全删除排序,我会得到所有帖子,即使是那些不包含 item_price 键的帖子。
    • 我绝对不是 SQL 专家,但 WHERE 子句 "AND (wp_postmeta.meta_key = 'item_price')" 不能阻止所有没有设置 meta_key 的帖子出现吗?
    • 我认为您是对的,将其更改为AND (wp_postmeta.meta_key = 'item_price' OR wp_postmeta.meta_key is NULL),以在表连接后包含空行。您也可以删除 1=1 AND 位,因为它始终是 true 并且只是浪费。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      它不会很漂亮(或优化),但我认为你可以在这里使用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?

      【讨论】:

      • 我以前从未使用过CASE 和其他人,所以不知道为什么,但不幸的是,当我尝试用你的ORDER BY ... ASC 替换我自己的ORDER BY ... ASC 时出现SQL 错误:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IS NOT NULL THEN wp_postmeta.meta_value END ASC, CASE wp_postmeta.meta_value WHE' at line 1
      • @powerbuoy 我之前也没有尝试过,所以我在瞎拍。也许尝试不使用IS's?
      【解决方案4】:

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

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

      $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');
      

      【讨论】:

      • 设置meta_key 将忽略您的meta_query 声明
      【解决方案5】:

      我知道,这个帖子已经有 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-03-21
        • 1970-01-01
        • 1970-01-01
        • 2018-10-20
        • 2018-11-23
        • 2012-12-13
        • 2019-07-15
        • 2019-05-27
        相关资源
        最近更新 更多