【问题标题】:Complex custom_field search with meta_query使用 meta_query 进行复杂的 custom_field 搜索
【发布时间】:2018-02-06 06:57:02
【问题描述】:

背景

我正在使用 Advanced Custom Fields Pro 来管理我的自定义字段,它们有一个“中继器”字段,其中包含存储为 repeatername_X_fieldname 的子字段,其中 X 是中继器的行号。

我有一个自定义帖子类型student,它的转发器attendance 包含dateclass

因此,当学生上课时,它将按如下方式存储他们的出勤率

  • meta_key:'attendance_X_date' meta_value:'20170701'
  • meta_key:'attendance_X_class' meta_value:'History 101'

为了搜索上过某个班级或某个日期范围内的任何学生,我必须连接到get_meta_sql 并将我的meta_query 转换为使用LIKE 而不是=当值包含%

function key_rewrite($parts){
    foreach($parts as &$part){
        $part = preg_replace("/(meta_key = )(\'[^']*[%][^']*\')/", "meta_key LIKE $2", $part);
    }
    return $parts;
}
add_action( 'get_meta_sql', 'key_rewrite');

这让我可以做类似的事情

$args = array(
    'post_type' => 'student',
    'meta_query' => array(
        array(
            'key'=>'attendance_%_class',
            'compare'=>'=',
            'value'=>'History 101'
        )
    )
);
$my_query = new WP_Query($args);

为了搜索参加过历史 101 的任何人或

$args = array(
    'post_type' => 'student',
    'meta_query' => array(
        array(
            'key'=>'attendance_%_date',
            'compare'=>'>=',
            'value'=>'20170101'
        )
    )
);

搜索今年参加过的人。

问题第 1 部分

我需要能够搜索今年参加过“历史 101”的任何人。

最初,meta_query 上的简单 AND 似乎可以解决问题:

$args = array(
    'post_type' => 'student',
    'meta_query' => array(
        'relation' => 'AND',
        array(
            'key'=>'attendance_%_class',
            'compare'=>'=',
            'value'=>'History 101'
        ),
        array(
            'key'=>'attendance_%_date',
            'compare'=>'>=',
            'value'=>'20170101'
        )
    )
);

但是,由于通配符没有关联,这实际上可能会返回去年参加“历史 101”但今年参加不同课程的人。

问题第 2 部分

实际上需要能够获得一份名单,列出今年参加过“历史 101”但在过去一周根本没有来上课的每个人。这使问题进一步复杂化,因为我需要将 meta_query 的 EXISTSNOT EXISTS 与附加条件结合起来。同样,从表面上看,使用嵌套的 meta_queries 听起来相当简单:

$args = array(
    'post_type' => 'student',
    'meta_query' => array(
        'relation' => 'AND',
        array(
            'relation' => 'AND',
            array(
                //Just assume by some magic we resolved Problem part 1
                'key'=>'attendance_%_class',
                'compare'=>'=',
                'value'=>'History 101'
            ),
            array(
                'key'=>'attendance_%_date',
                'compare'=>'>=',
                'value'=>'20170101'
            )
        ),
        array(
            'relation' => 'AND',
            array(
                //again... magic!
                'key'=>'attendance_%_class',
                'compare'=>'=',
                'value'=>'History 101'
            ),
            array(
                'key'=>'attendance_%_date',
                'compare'=>'>',
                'value'=>'20170821'
            ),
            array(
                'key'=>'attendance_%_date',
                'compare'=>'NOT EXISTS'
            )
        )
    )
);

显然这是由逻辑问题造成的,但令人印象深刻的是,WordPress 通过在元查询中每次使用一次加入 postmeta 表来解决其中的大部分问题。不幸的是,这意味着 NOT EXISTS ON 中没有使用 > 日期部分,因此不能使用 IS NULL 来测试它不存在。

我知道这非常复杂,如果您关注我,我会印象深刻。如果不是,请提出问题,以便我帮助澄清。

是的,我知道我可以完全编写自己的查询,但我正在尝试坚持使用内置的 WordPress 工具。

帮助!

【问题讨论】:

    标签: wordpress advanced-custom-fields custom-fields


    【解决方案1】:

    我去过那里...试图将所有内容放在一个复杂的元查询中。最后需要手动处理生成的SQL——移动括号、替换运算符、引号等。

    最后,查询非常复杂,并且有多个 JOINS 到 postmeta 表,它变得过于昂贵和缓慢。

    我选择通过选择稍微不同的方法来实现这一点。 所以,你可以做的是将查询分成几个子查询,稍后将它们与post__inpost__not_in结合起来,

    例如问题第 1 部分

    /* Filter by class */
    $history_students_ids = get_posts(array(
        'post_type'      => 'student',
        'fields'         => 'ids',
        'posts_per_page' => -1,
        'meta_query'     => array(
            array(
                'key'=>'attendance_%_class',
                'compare'=>'=',
                'value'=>'History 101'
            ),
        )
    ));
    
    /* Filter by date */
    $students = get_posts(array(
        'post_type' => 'student',
        'post__in' => $history_students_ids,
        'meta_query' => array(
            array(
                'key'=>'attendance_%_date',
                'compare'=>'>=',
                'value'=>'20170101'
            )
        )
    ));
    

    问题第 2 部分

    也是如此
    $not_attended_history_students_ids = get_posts(array(
        'post_type' => 'student',
        'post__in' => $history_students_ids,
        'fields'         => 'ids',
        'posts_per_page' => -1,
        'meta_query' => array(
            array(
                'key'=>'attendance_%_date',
                'compare'=>'NOT EXISTS'
            )
        )
    ));
    
    $students = get_posts(array(
        'post_type' => 'student',
        'post__in' => $not_attended_history_students_ids,
        'meta_query' => array(
            array(
                'key'=>'attendance_%_date',
                'compare'=>'>=',
                'value'=>'20170101'
            )
        )
    )); 
    

    您可以将出勤条件反转为 EXISTS 并使用post__not_in...希望您已经理解了这个想法。

    【讨论】:

    • 乍一看,这似乎是一个很好的解决方案。一有机会我会测试一下并告诉你。
    • 好的。这对第 2 部分非常有效。但是第 1 部分仍然存在相同的问题。例如,如果“学生 1”去年参加了“历史 101”,他将出现在第一个 get_posts 结果中。那么,如果他今年参加“历史102”,他将在第二名get_posts
    • 在第二个循环中尝试 Wp_query post__not_in (codex.wordpress.org/Class_Reference/WP_Query)
    猜你喜欢
    • 2018-02-28
    • 1970-01-01
    • 2011-10-12
    • 1970-01-01
    • 2013-08-26
    • 1970-01-01
    • 1970-01-01
    • 2011-06-15
    • 1970-01-01
    相关资源
    最近更新 更多