在全局搜索中包括来自自定义帖子类型的元数据--续

时间:2019-01-21 作者:amacgill

我看到了一些关于在wordpress搜索中包含元字段内容的问题。我的问题不是这些问题的重复。

背景我遇到的问题源于如何将元查询附加到搜索中。由于查询是附加到全局查询的,而不是单独查询metabox值,因此它由AND运算符连接。

For the security-conscious -- 请注意,在每个“LIKE‘user input’”语句中,用户的查询周围都有一个相同的nonce,但为了便于阅读,这些nonce已被删除。

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts 
LEFT JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) 
LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = \'stc_make\' ) 
WHERE 1=1 
AND 
(
    (
        ( wp_posts.post_title LIKE \'ADS-B\' ) OR 
        ( wp_posts.post_excerpt LIKE \'ADS-B\' ) OR 
        ( wp_posts.post_content LIKE \'ADS-B\' )
    )
) 
AND 
( 
    ( wp_postmeta.meta_key = \'stc_make\' AND wp_postmeta.meta_value LIKE \'ADS-B\' ) OR 
    ( wp_postmeta.meta_key = \'stc_models\' AND wp_postmeta.meta_value LIKE \'ADS-B\' ) OR 
    ( wp_postmeta.meta_key = \'stc_desc\' AND wp_postmeta.meta_value LIKE \'ADS-B\' ) 
) 
AND wp_posts.post_type IN (

    \'post\', 
    \'page\', 
    \'attachment\', 
    \'employees\', /* custom post type */
    \'certificates\' /* custom post type */

)
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 wp_posts.post_title LIKE \'ADS-B\' DESC, wp_posts.post_date DESC LIMIT 0, 10
这有效地防止了搜索包含来自自定义帖子类型的帖子,即使查询与元框中的内容匹配也是如此。

我解决了这个问题a solution from Naji Amer on stackexchange:

function filter_meta_query($sql){

    if (!(is_admin()) && (is_search())) {
        $pos = strpos($sql[\'where\'], \'AND\');

        if ($pos !== false) {
            $sql[\'where\'] = substr_replace($sql[\'where\'], \'OR\', $pos, strlen(\'AND\'));
        }

    }

    return $sql;
}
add_filter(\'get_meta_sql\', \'filter_meta_query\', 10, 1);
(添加条件(!(is\\u admin()),否则将阻止您更改管理菜单中的菜单项…)

代码将“AND”(在元数据库内容的条件之前)更改为OR。This works, but only if there is a query \'s\'. 当没有查询时,定义标准wordpress查询的第一个AND语句:

WHERE 1=1 
AND 
( 
    ( 
        ( wp_posts.post_title LIKE \'ADS-B\') OR 
        ( wp_posts.post_excerpt LIKE \'ADS-B\' ) OR 
        ( wp_posts.post_content LIKE \'ADS-B\' ) 
    )
) 
已删除。这给我们带来了一个问题,因为剩下的查询

其中1=1或。。。(条件)

换句话说,其中true=trueOR 其他条件返回true。查询将返回wp\\U posts表中的所有post类型。

我做了一些挖掘,在网上发现了以下内容27262809 属于class-wp-query.php, 分别为:

$this->request = $old_request = "SELECT $found_rows $distinct $fields FROM {$wpdb->posts} $join WHERE 1=1 $where $groupby $orderby $limits";
(第2726行)

$this->request = "SELECT $found_rows $distinct {$wpdb->posts}.ID FROM {$wpdb->posts} $join WHERE 1=1 $where $groupby $orderby $limits";
(第2809行)

据我所知,其中1=1是硬编码的。

问题是,我当前的wordpress项目包括两种自定义帖子类型,可以公开搜索(“员工”和“证书”)。我想使用插件来允许通过他们在global $wp\\U查询。

为此,我为使用该标准的每个类添加了一个函数

set_query_var(\'meta_query\', array(...))
定义应搜索的元数据库。

为什么我不只是使用自定义的书面查询,我想保留随时添加或删除插件/帖子类型的能力,这意味着不需要使用自定义SQL字符串(除非我在theme\'s functions.php中编写自己的函数来生成它)。

但是,由于“where 1=1”子句,使用默认查询生成器会给我带来问题。

到目前为止,我一直在关注的是,我还没有找到一个钩子,以便在发送最终查询之前进行修改(我更改“and”to“OR”的函数是修改元查询对象,而不是查询字符串)。

我还没有找到一种将元查询参数与默认wordpress搜索参数分组的方法,看起来是这样的:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts 
LEFT JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) 
LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = \'stc_make\' ) 
WHERE 1=1 
AND 
(
    (
        ( wp_posts.post_title LIKE \'ADS-B\' ) OR 
        ( wp_posts.post_excerpt LIKE \'ADS-B\' ) OR
        ( wp_posts.post_content LIKE \'ADS-B\' )
    ) OR ( 
        ( wp_postmeta.meta_key = \'stc_make\' AND wp_postmeta.meta_value LIKE \'ADS-B\' ) OR
        ( wp_postmeta.meta_key = \'stc_models\' AND wp_postmeta.meta_value LIKE \'ADS-B\' ) OR 
        ( wp_postmeta.meta_key = \'stc_desc\' AND wp_postmeta.meta_value LIKE \'ADS-B\' ) 
    ) 
)

...
我原以为我可以通过检测$wp\\u query->s何时不为空来解决这个问题,但看起来即使是已定义的查询,当它们与搜索字符串匹配时,也会返回不需要的帖子类型(例如,搜索中显示的导航菜单项,只要它们的标题与搜索匹配,即使它们不符合

IN ([... post types]) 
查询中的参数)。

问题:

Am I going about this all wrong? 是否有更简单的方法以我想要的格式进行查询,还是应该生成多个查询并合并结果?(对于后者,我如何将结果附加到默认搜索,而不要求主题了解插件的一些信息?)

如果我能进一步澄清,请告诉我。

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

我已经找到了解决办法。

首先,为了修复空搜索的错误结果,我需要

(!empty(get\\u query\\u var(\'s\'))

在我的插件中设置“meta\\u query”查询变量之前。

我以为解决方法是posts_request 过滤器,但仍在更换

select SQL_CALC_GET_ROWS wp_posts.ID 
使用

select SQL_CALC_GET_ROWS wp_posts.*
即使我在过滤器中将*替换为“ID”,它也不会返回正确的结果。我有一种感觉,如果我进一步查看代码,我可以找到原因,但我已经到了最后期限,所以我没有费心。

相反,我意识到正确的过滤器是posts_request_ids, 在wp类查询的第2819行调用。在发送查询之前,Wordpress 4.9.8中的php。有了这个更改,代码就可以正常工作了。

自定义筛选器如下所示:

/**
 * FIX QUERIES 
 *
 * Adds metadata "where" conditions to the "where" conditions of the main query,
 * by shifting a few parentheses. 
 *
 * @param   string $request          The SQL query as a string
 * @param   object $query_instance   The user\'s search as string
 * @return  string $request
 * @author  Andrew MacGillivray
 */
function fixQuery($request, $query_instance) {

    if (!empty(get_query_var(\'s\'))) {

        // remove line breaks so stripos and substr_replace will work
        $request = trim(preg_replace(\'/\\s+/\', \' \', $request));

        // If you\'re copying this, remember to replace "wp_" with your table prefix.  
        // Or rewrite to use $wpdb...
        $replace = array(

            "))) OR ( ( wp_postmeta.meta_key" => array(
                "))) OR ( (" => ") ) OR ( ( ",
            ),

            ") ) AND wp_posts.post_type IN (" => ") ) ) AND wp_posts.post_type IN (",

        );

        foreach ($replace as $key => $value) {

            $pos = stripos($request, $key);

            if (is_array($value)) {
                foreach ($value as $orig => $replace) {
                    $request = substr_replace($request, $replace, $pos, strlen($orig));
                }
            } else {
                $request = substr_replace($request, $value, $pos, strlen($key));
            }
        }

    }

    return $request;
}
add_filter(\'posts_request_ids\', \'fixQuery\', 10, 2);
在该代码中,我们在与主查询相同的“AND”子句中包含了“OR…metadata LIKE\'\'%user query%\')”。上述筛选器查找由以下筛选器添加的“OR”子句:

/**
 * In Global WP_Query, replace first "And" with "OR" to allow meta-queries (when present)
 * to function properly.
 *
 * @see https://stackoverflow.com/questions/43734845/how-to-give-or-relation-in-tax-query-and-meta-query-wordpress
 * @author StackOverflow: Naji Amer, 2018-02-09
 */
function filter_meta_query($sql){

    if (!(is_admin()) && (is_search()) && !empty(get_query_var(\'s\'))) {
        $pos = strpos($sql[\'where\'], \'AND\');

        if ($pos !== false) {
            $sql[\'where\'] = substr_replace($sql[\'where\'], \'OR\', $pos, strlen(\'AND\'));
        }

    }

    return $sql;
}
add_filter(\'get_meta_sql\', \'filter_meta_query\', 10, 1);
添加这两个过滤器(并修改我的插件,以便只在搜索查询不为空时添加元查询)可以使一切都按预期进行。

希望这可以帮助其他人。