根据自定义 post 类型的最低和最高价格过滤查询

Filter query based on min and max price for custom post type

我有一个名为 price 的 ACF 字段。 price 分配给 vehicle post 类型。

我还有一个过滤器,允许用户根据定义的 min pricemax price.

显示 vehicle

我正在尝试展示介于定义的 min pricemax price 范围之间的 vehicle 的结果。

这是我目前的情况:

<?php

// get data from url
$min_price = $_GET['min-price'];
$max_price = $_GET['max-price'];

global $post;

$args = array(
  'post_type' => 'vehicle',
  'post_status' => 'publish',
  'orderby' => 'publish_date',
  'order' => 'DESC',
  'meta_query' => array(
    array(
      'key'     => 'price',
      'value' => array($min_price, $max_price),
      'compare' => 'BETWEEN',
      'type'    => 'numeric',
    )
  ),
);


$query = new WP_Query( $args );

if($query->have_posts() ) :
  while ( $query->have_posts() ) : $query->the_post();
    $price = get_field("price");
    $price = preg_replace("/[^0-9]/", "", $price);
      
    echo the_title();
  endwhile; wp_reset_postdata(); 
  
else : ?>
  <h2><?php _e("no posts found"); ?></h2>
<?php endif; ?>


?>

A var_dump($price) returns string(0) ""。我显然需要在 while 循环中定义 $price,但不确定在那种情况下我将如何在上面查询它?我只想 return 那个范围内的 post。

因为您将数字数据存储为字符串,所以随着时间的推移,您将 运行 在很多地方遇到问题。即使是像排序这样简单的事情现在也变得困难了。 (而且我不是在判断,它就是这样。)如果您可以将此数据存储为真正的数字并在其他地方处理格式,那么您的代码将更容易维护 运行.

不幸的是,由于上述限制,本机 WordPress 查询将无法让您到达目的地。然而,WordPress 有大量的钩子,特别是 post_clauses 允许您修改 SQL 查询的 WHERE 子句以满足您的需要。 (实际上有很多,我通常更喜欢在这个特定的地方做。)

首先,我将通过添加一个附加参数来调整您的 $args。只要不和别的东西冲突就行,我用的是FIX_RANGE_QUERY.

$args =
    [
        'FIX_RANGE_QUERY' => true,
        'post_type' => 'vehicle',
        'post_status' => 'publish',
        'orderby' => 'publish_date',
        'order' => 'DESC',
        'meta_query' => [
            [
                'key' => 'price',
                'value' => [$min_price, $max_price],
                'compare' => 'BETWEEN',
                'type' => 'numeric',
            ]
        ],
    ];

$query = new WP_Query($args);

这本身不会改变任何东西,你还需要一个过滤器,它可能应该在 之前 上面,最好在 functions.php 或类似的。我已经在这段代码中添加了一堆注释,希望能遍历它所做的一切,但要点是它修改了 SQL 查询的 WHERE 子句,以包含一个正则表达式来删除非数字在进行转换和比较之前。

add_filter(
    'posts_clauses',
    static function ($clauses, WP_Query $query) {
        // Look for our magic variable so that we only modify our certain query
        if (true === ($query->query['FIX_RANGE_QUERY'] ?? false)) {

            // Make sure that we've got something in the WHERE position
            $where = $clauses['where'] ?? null;
            if ($where) {

                // We need this in order to get the prefix so that we are safe if this code is migrated
                global $wpdb;

                // The CAST() stuff is WordPress because the query specifies a NUMERIC type
                $searchString = "CAST({$wpdb->prefix}postmeta.meta_value AS SIGNED)";

                // Replace the above with an inner regex that removes everything except digits
                $replaceString = "CAST(REGEXP_REPLACE({$wpdb->prefix}postmeta.meta_value, '[^0-9]+', '') AS SIGNED)";

                // Reset the main clause
                $clauses['where'] = str_replace($searchString, $replaceString, $where);
            }
        }

        // No matter what, we must return this, otherwise EVERY query will break.
        return $clauses;
    },
    9999,
    2
);

以上代码将 WordPress 查询更改为:

 AND ( 
  ( wp_postmeta.meta_key = 'price' AND CAST(wp_postmeta.meta_value AS SIGNED) BETWEEN '5' AND '100' )
) AND wp_posts.post_type = 'vehicle' AND ((wp_posts.post_status = 'publish'))

为此:

 AND ( 
  ( wp_postmeta.meta_key = 'price' AND CAST(REGEXP_REPLACE(wp_postmeta.meta_value, '[^0-9]+', '') AS SIGNED) BETWEEN '5' AND '100' )
) AND wp_posts.post_type = 'vehicle' AND ((wp_posts.post_status = 'publish'))

此代码有点脆弱,仅供参考。如果任何间距关闭,str_replace 将找不到它要查找的内容。但总的来说应该还是很一致的。