在数据库查询中匹配“卷曲”的引号和标准引号

5

我有一些文本存储在post_title列中:

you’re

使用以下查询:
function ma_post_title_filter($where, &$wp_query) {
    global $wpdb;
    if ( $search_term = $wp_query->get( 'ma_search_post_title' ) ) {
        $search_term = sanitize_text_field($search_term);
        $where .= ' AND ' . $wpdb->posts . '.post_title LIKE \'%' . $wpdb->esc_like( $search_term ) . '%\'';
    }
    return $where;
}

如果$search_termyou're,它可以找到该列并工作。但是,如果它是you're,它将无法工作(因为这是一个不同的单引号标记)。
我该如何改进查询,以便you'reyou’re都匹配you’re
我已经尝试过以下方法:
  1. $search_term使用str_replace(),用替换'。示例:str_replace('\'','’',$wpdb->esc_like( $search_term ))...这种方法的问题在于,如果存储的列是you're,我们就永远也无法匹配。
  2. '.$wpdb->posts.'.post_title上执行AND REPLACE,将'替换为...但似乎不起作用。
我知道理想的解决方案是调整所有单引号的数据库实例以达到标准化,但在我的情况下这是不可能的。
那么问题的适当解决方案是什么?

5
“我已尝试的部分”写得很好,应该成为标准。+1 - Gary Woods
由于您无法确定每次搜索的搜索术语,因此该内容可能不适合。因此我已删除了该评论 ;) - Adam
你尝试过将 替换为 ' 吗?因为你的模式是 '.post_title LIKE \'%\' - forpas
@forpas,请问什么时候进行替换?请查看我的两个示例方法以及有关为什么它不起作用的说明。谢谢。 - Henrik Petterson
你的模式是这样的:.post_title LIKE \'%' 这意味着你想匹配 ',对吗?因此,你要应用 str_replace('’', ''', $search_term) - forpas
3个回答

2
您可以使用MySQL的RLIKE:
$where .= ' AND '. $wpdb->posts .'.post_title RLIKE \'.\*'. $wpdb->esc_like(str_replace(["’","'"],"['’]", $search_term) ) .'.\*\' ';

2
OP已经说过他们无法更改现有数据,但使用RLIKE是个好主意;你应该编辑你的帖子,使你的代码更易读,这样就会得到一些赞。 - miken32

2

我想指出,在LIKE查询中,%并不是唯一可用的占位符。您还可以使用下划线_来表示单个字符。一种方法是将某个类别中的每个标点符号字符替换为下划线。这将允许“you're”与“you’re”匹配,反之亦然。(当然,这也会允许“youvre”匹配!)

<?php
function ma_post_title_filter($where, &$wp_query) {
    global $wpdb;
    $punct = ["’", "‘", "”", "“"];
    if ( $search_term = $wp_query->get( 'ma_search_post_title' ) ) {
        // escape any % or _ characters
        $search_term = $wpdb->esc_like($search_term);
        // replace desired punctuation characters with _
        $search_term = str_replace($punct, "_", $search_term);
        // do final escaping for safe queries
        $search_term = $wpdb->_real_escape($search_term);
        // this looks much neater with string interpolation vs concatenation
        $where .= " AND $wpdb->posts.post_title LIKE '%$search_term%'";
    }
    return $where;
}

当你向LIKE子句传递值时,应使用wpdb::esc_like()确保任何用户提供的百分号或下划线不会妨碍查询。sanitize_text_field()用于输出到HTML之前,对于数据库查询无法使文本安全。这就是wpdb::_real_escape()的作用。

1
谢谢你的优秀方法,但它不起作用。$where 里面有错别字吗?这里是一个示例代码,你可以用来测试。我感觉问题很可能是拼写错误,因为代码本身非常合乎逻辑! - Henrik Petterson
我正在使用最新版本。 - Henrik Petterson
好的,刚刚意识到一个打字错误。在 _real_escape() 之前漏掉了下划线。 - miken32
谢谢,那解决了错误。但是 $string_term = "you're"; 仍然无法与 you’re 匹配。也许这只是替换了搜索术语变量中的引号,而不是数据库中存储的标题?这只是一个猜测。 - Henrik Petterson
您的搜索词需要反斜杠来转义特殊字符,这就是 _real_escape() 的作用。当我提到“youvre”这样的虚假匹配时,我指的是数据库内容,而不是搜索词。 - miken32
显示剩余5条评论

0

我将使用两次LIKE语句,一次使用原始搜索词,第二次使用将'替换为的搜索词。

您可以使用参数化查询。例如,使用 wpdb :: prepare()

$searchTerm = "You're"; //Don't sanitize using a function, this will by done with parameters.
// query
$where .= ' AND ('. $wpdb->posts .'.post_title LIKE %s OR '. $wpdb->posts .'.post_title LIKE %s)'; //Notice the double %s
//I suppose $where contains your whole query
$sth = $wpdb->prepare($where, '%' . $searchTerm . '%', '%' . str_replace('’', '\'', $search_term) . '%'); //This will associate the %s (a string) in the query by the value indicated in the function
$wpdb->query($sth);

%s 是查询中的 占位符,用于告诉 $wpdb->prepare() 在此处将有一个字符串。


1
谢谢,但您能否编辑您的答案,并提供如何使用您的方法调整我的特定代码的示例? - Henrik Petterson
1
当然,稍等一下。 - Cid
1
@Cid,FYI,OP正在使用WordPress wpdb - Gary Woods
1
@GaryWoods 谢谢,我已经根据这种情况调整了我的答案。 - Cid
1
是的,使用预处理语句始终是最佳方法,但WordPress使其非常难以实现。原始问题中的函数在涉及搜索查询时由WP动态应用,因此实际执行在其他地方处理。 - miken32
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接