元比较与日期(存储为字符串)不起作用

时间:2017-01-06 作者:Stephen

我有一个自定义的帖子类型,里面有很多元框。其中一个是expiration\\u date字段,它将其信息以字符串格式存储在db中,格式如下

Feb 1, 2017
我正在尝试编写一个查询,将筛选出那些日期在过去的项目,只显示未来的项目。我尝试了以下查询的多个版本,但均无效:

$myquery = array (
\'post_type\' => $cpt,
    \'numberposts\'=>-1,
    \'meta_key\' => \'expiration_date\',
    \'meta_value\' => date(\'M j, Y\'),
    \'meta_compare\' => \'<\'
    ),
 );
查询将返回所有帖子,无论日期如何,如果我反转carot,则返回零篇帖子(<;)

我知道为了使用日期字段,WP希望它采用不同的格式(YYYY-MM-DD),但这不是这个应用程序(由其他人)构建的方式,所以我正在使用上面的格式。

所以问题是:有没有办法在比较之前转换日期时间?如果我在任何记录中单独提取字段,我可以转换它(使用strotime),但我当然会尝试根据显示前的日期筛选出值,以便在开始时在集合中找到正确的数字。

6 个回复
最合适的回答,由SO网友:gmazzap 整理而成

介绍STR_TO_DATE

MySQL具有STR_TO_DATE 您可以为范围利用的功能。

要使用它,您需要过滤WHERE a条款WP_Query, 可能使用posts_where 滤器

该函数允许使用特定格式将列值格式化为日期。

默认情况下,WordPress使用CAST MySQL函数,该函数期望值的格式为Y-m-d H:i:s (根据PHPdate 参数)。

STR_TO_DATE, 相反,允许传递特定格式。“占位符”格式与PHP使用的格式不同,您可以在此处看到支持的MySQL格式占位符:https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_date-format.

工作流

作为工作流,我可以建议:

创建WP_Query 对象使用posts_where 添加WHERE 使用的子句STR_TO_DATESTR_TO_DATE 也可用于排序值。如果您想这样做,您需要使用posts_orderby 滤器

过滤类

这里有一个类(PHP 7+)包装了上述工作流,以便于重用:

class DateFieldQueryFilter {

    const COMPARE_TYPES = [ \'<\', \'>\', \'=\', \'<=\', \'>=\' ];
    private $date_key;
    private $date_value;
    private $format;
    private $compare = \'=\';
    private $order_by_meta = \'\';

    public function __construct(
        string $date_key,
        string $date_value,
        string $mysql_format,
        string $compare = \'=\'
    ) {
        $this->date_key   = $date_key;
        $this->date_value = $date_value;
        $this->format     = $mysql_format;
        in_array($compare, self::COMPARE_TYPES, TRUE) and $this->compare = $compare;
    }

    public function orderByMeta(string $direction = \'DESC\'): DateFieldQueryFilter {
        if (in_array(strtoupper($direction), [\'ASC\', \'DESC\'], TRUE)) {
            $this->order_by_meta = $direction;
        }
        return $this;
    }

    public function createWpQuery(array $args = []): \\WP_Query {
        $args[\'meta_key\'] = $this->date_key;
        $this->whereFilter(\'add\');
        $this->order_by_meta and $this->orderByFilter(\'add\');
        $query = new \\WP_Query($args);
        $this->whereFilter(\'remove\');
        $this->order_by_meta and $this->orderByFilter(\'remove\');
        return $query;
    }

    private function whereFilter(string $action) {
        static $filter;
        if (! $filter && $action === \'add\') {
            $filter = function ($where) {
                global $wpdb;
                $where and $where .= \' AND \';
                $sql = "STR_TO_DATE({$wpdb->postmeta}.meta_value, %s) ";
                $sql .= "{$this->compare} %s";
                return $where . $wpdb->prepare($sql, $this->format, $this->date_value);
            };
        }
        $action === \'add\'
            ? add_filter(\'posts_where\', $filter)
            : remove_filter(\'posts_where\', $filter);
    }

    private function orderByFilter(string $action) {
        static $filter;
        if (! $filter && $action === \'add\') {
            $filter = function () {
                global $wpdb;
                $sql = "STR_TO_DATE({$wpdb->postmeta}.meta_value, %s) ";
                $sql .= $this->order_by_meta;
                return $wpdb->prepare($sql, $this->format);
            };
        }
        $action === \'add\'
            ? add_filter(\'posts_orderby\', $filter)
            : remove_filter(\'posts_orderby\', $filter);
    }
}
抱歉,如果这里没有太多注释或解释,那么很难在这里编写代码。

在“神奇”发生的地方,类的“核心”是添加到posts_where 在查询运行之前,然后删除。它是:

function ($where) {
    global $wpdb;
    $where and $where .= \' AND \';
    // something like: STR_TO_DATE(wp_postmeta.meta_value,\'%b %e, %Y\') < \'Feb 1, 2017\'
    $sql = "STR_TO_DATE({$wpdb->postmeta}.meta_value, %s) {$this->compare} %s";

    return $where . $wpdb->prepare($sql, $this->format, $this->date_value);
};
你可以在这里看到我们是如何告诉WordPress查询帖子的。请注意STR_TO_DATE 是一种并非免费的计算,如果有数千(或数百万)行,计算量将相当大(尤其是CPU)。

如何使用它顺便提一下,这个类可以这样使用:

$query_filter = new DateFieldQueryFilter (
    \'expiration_date\', // meta key
    date(\'M j, Y\'),    // meta value
    \'%b %e, %Y\',       // date format using MySQL placeholders
    \'<\'                // comparison to use
);

$query = $query_filter->createWpQuery([\'posts_per_page\' => - 1]);

while($query->have_posts()) {
     $query->the_post();
     // rest of the loop here
}
如果您想按标准WordPress字段排序,例如,发布日期、发布标题。。。,你可以简单地通过相关的orderorderby 的查询参数DateFieldQueryFilter::createWpQuery() 方法

如果您想使用相同的元值进行订购,该类将提供DateFieldQueryFilter::orderByMeta() 对于范围。

E、 g.在最后一段代码中,您将创建如下查询:

$query = $query_filter
             ->orderByMeta(\'DESC\')
             ->createWpQuery([\'posts_per_page\' => - 1]);
结论即使这样做有效,在可能的情况下,日期也应该使用MySQL格式或时间戳保存在数据库中,因为这大大简化了任何过滤或排序操作。

事实上,从数据库中检索日期后,以所需格式将日期转换为输出要容易得多。

即使以任何一种方式进行计算,在返回的记录之后进行计算,但让MySQL执行它需要对**所有*记录执行的计算,需要做更多的工作。

如果以MySQL格式存储日期,WordPress提供了一个函数,mysql2date(), 它可以用于将它们转换为所需的格式,并且还支持区域设置。

SO网友:Stephen

我最终解决了初始日期格式的问题,将expiration\\u date字段格式更改为YYYY-MM-DD(然后在其他地方修改该字段的显示,以便最终用户查看时保持原始格式)。因此,现在可以使用DATETIME进行常规meta\\u查询:

$myquery = array (
\'post_type\' => $cpt,
    \'numberposts\'=>-1,
    \'meta_key\' => \'expiration_date\',
    \'type\'=> \'DATETIME\',
    \'meta_value\' => date("Y-m-d"),
    \'meta_compare\' => \'<\'
    ),
 );
如果存在上述问题,我仍然希望得到答案,在比较之前,可以动态地对值进行字符串转换,因此我想我会推迟接受自己的答案,希望得到这个答案。

SO网友:Aamer Shahzad

您是否尝试在WP\\u query中使用meta\\u query?

$args = array(
    \'post_type\'  => $cpt,
    \'meta_query\' => array(
        array(
            \'key\'     => \'expiration_date\',
            \'value\'   => date(\'M j, Y\'),
            \'compare\' => \'<\',
        ),
    ),
);
$query = new WP_Query( $args );
此外,请确保Posteta表中的日期的格式与在meta\\u查询中作为值传递的格式相同。

SO网友:Hamsterdrache

也许你可以试试这样的?

$args2 = array( 
            \'post_type\'     => $cpt,
            \'posts_per_page\'=> -1, 
            \'meta-key\' => \'expiration_date\',
            \'meta_query\' => Array ( 
                                Array (\'key\' => \'expiration_date\', 
                                        \'compare\' => \'>=\', 
                                        \'value\' => date(\'M j, Y\',strtotime(\'now\')), //strtotime for value for now, date() to format the timestamp
                                            \'type\' => \'DATETIME\'
                                            ) 
                                            ),
            \'orderby\'       =>   \'meta_value\', 
            \'order\'         =>   \'ASC\' //or DESC
            );
对于我来说,这只适用于获取仍在未来的事件。

编辑:我必须道歉,这行不通。我开车去约会,这时我意识到分拣实际上是在哪里进行的。在我的文件中,有一条if语句是我上次尝试时留下的,它完成了排序和排序。删除了那条线,它停止了工作。。。

SO网友:Mudy S

您可以执行date\\u parse\\u from\\u format来转换日期格式:

date_parse_from_format(\'M j, Y\', \'Feb 1, 2017\');
然后可以将数组改为date(“Y-m-d”)。

完整的阵列结构为:

[year] =>  
[month] => 
[day] =>  
[hour] =>  
[minute] => 
[second] =>
[fraction] =>  
[warning_count] =>  
[warnings] => Array() 
[error_count] => 
[errors] => Array() 
[is_localtime] => 
[zone_type] => 
[zone] => 
[is_dst] =>

SO网友:prosti

明智的解决方案是使用expiration_date 字段格式为YYYY-MM-DD.

这样你就能创造未来WordPress meta queries 没有问题。

转换应该是轻而易举的事。

相关推荐

列出分类法:如果分类法没有POST,就不要列出分类法--取决于定制的POST-META?

这可能很难解释,我不知道是否有解决办法!?我有一个名为“wr\\u event”的自定义帖子类型和一个名为“event\\u type”的分层自定义分类法。自定义帖子类型有一个元框,用于event_date 并且与此帖子类型关联的所有帖子都按以下方式排序event_date. 我在循环中有一个特殊的条件来查询event_date 已经发生了-在这种情况下,它没有显示,但只列在我的档案中。就像你可以使用wp_list_categories() 我编写了一个自定义函数,它以完全相同的方式列出所有分类术语。现在