以下是由 Jason Witt 发布的文章。 在这里,Jason 分享了一种方法,您可以认为这种方法非常简单,但事实证明它比您想要的要复杂一些。 幸运的是,借助 Jason 的代码和示例,它可以变得很容易。
最近,我需要创建一个自定义的 WordPress 查询,按字母顺序列出文章标题。 我从这样的基本查询开始。
$my_query = new WP_Query(array(
'post_type' => 'post',
'orderby' => 'title',
'order' => 'ASC'
));
我的结果是

从技术上讲,结果是按字母顺序排列的,但不是我想要的方式。 我希望如果标题以“the”开头,则忽略“the”,并改为按下一个单词进行字母排序。 期望的顺序应如下所示

“the”被称为 *首字母词*。 你知道:“A”、“An”和“The”,有时放在专有名词开头的东西。 如果您想国际化,可以包括“La”(西班牙语)和“Les”(法语)。 在经过大量搜索和梳理 WordPress Codex 之后,我找不到用于忽略首字母词的原生 WordPress WP_Query
解决方案。
我发现有两个非常有用的过滤器可以让我实现这一点,posts_fields
和 posts_orderby
。 通过一点 SQL 知识和 PHP 技巧。 我能够获得期望的结果。
也许不出所料,这是一个非常普遍的需求。 想象一个音乐库。 将所有以“The”开头的乐队都归类到“T”下会不必要地造成混淆。 相反,它应该是这样的

如何操作
让我与您分享我是如何做到的,以便您可以在任何需要对 WordPress 查询进行字母排序并忽略首字母词的 WordPress 项目中使用它。
在您的 `functions.php` 文件中,添加以下两个函数。
function wpcf_create_temp_column($fields) {
global $wpdb;
$matches = 'The';
$has_the = " CASE
WHEN $wpdb->posts.post_title regexp( '^($matches)[[:space:]]' )
THEN trim(substr($wpdb->posts.post_title from 4))
ELSE $wpdb->posts.post_title
END AS title2";
if ($has_the) {
$fields .= ( preg_match( '/^(\s+)?,/', $has_the ) ) ? $has_the : ", $has_the";
}
return $fields;
}
function wpcf_sort_by_temp_column ($orderby) {
$custom_orderby = " UPPER(title2) ASC";
if ($custom_orderby) {
$orderby = $custom_orderby;
}
return $orderby;
}
第一个函数 wpcf_create_temp_column
将在查询时创建一个名为“title2”的虚拟表列。 它使用文章的标题填充表列。 如果标题包含首字母词“The”,则将其删除。
第二个函数 wpcf_sort_by_temp_column
创建一个新的自定义 orderby
。 它使用 ASC
顺序按虚拟表列“title2”对文章进行排序。
要让这些函数在 *您的* 查询中发挥作用,您只需将它们添加到我之前提到的过滤器中即可。
add_filter('posts_fields', 'wpcf_create_temp_column');
add_filter('posts_orderby', 'wpcf_sort_by_temp_column');
现在,您的查询将按字母顺序对文章标题进行排序,同时忽略首字母词“The”。 如果您想添加其他要忽略的首字母词,您只需将它们包含到 wpcf_create_temp_column
函数中的 $matches
变量中,并用管道字符分隔即可。
$matches = 'A|An|The|La|Les';
现在假设您有多个按标题排序的 WordPress 查询,但您不想将这些过滤器应用于这些查询。 通过在 `functions.php` 文件中添加过滤器,它们将影响您网站上的每个查询,因此您可能不希望这样做。 更不用说它可能会通过使用不必要的过滤器来影响您网站的性能。 以下是如何将这些过滤器应用于特定查询。
首先从 `functions.php` 文件中删除 add_filter
函数。 然后在具有所需查询的模板文件中,像这样放置 add_filter
函数以及 remove_filter
函数。
add_filter('posts_fields', 'wpcf_create_temp_column'); // Add the temporary column filter
add_filter('posts_orderby', 'wpcf_sort_by_temp_column'); // Add the custom order filter
$query = new WP_Query(array('post_type' => 'post')); // Your custom query
remove_filter('posts_fields','wpcf_create_temp_column'); // Remove the temporary column filter
remove_filter('posts_orderby', 'wpcf_sort_by_temp_column'); // Remove the temporary order filter
if (have_posts()) : while ($query->have_posts()) : $query->the_post(); // The query output
the_title();
echo '<br/>';
endwhile; endif; wp_reset_postdata();
在上面的代码中,您正在添加过滤器、执行查询,然后立即删除过滤器。 通过将查询代码包装在过滤器中,您可以将这些过滤器隔离到该特定查询。
排序愉快!
非常有用的文章,感谢 Chris 分享 Jason 的作品。
如果有人需要使用西班牙语/法语完整文章,则为
$spanish_articles = ‘La|El|Las|Los|Una|Unas|Un|Unos’;
$french_articles = ‘La|Le|Les|Un|Une|Des’;
法语中的一个问题:当名词以元音开头时,冠词采用 L’ 形式,例如
L’Orchestre Philharmonique。
有趣的方法。 在不使用外部库的情况下;这真的是实现此结果的最优雅方法吗? 在没有在本地 WP 安装上实现此方法的情况下,我想知道此方法的性能。
研究了在 PHP 中不使用虚拟表来执行此操作的方法,想出了一个看起来相当丑陋但似乎可以正常工作的方法。 不是 WordPress 特定的,但可能可以通过一些操作来对查询结果进行排序。
我在客户端的一个实时网站上使用了它,没有任何性能问题。 我在其上使用的查询实际上是网站上加载速度最快的页面之一。
您的方法很有趣,并且似乎确实可以完成工作,但我认为它不属于“WordPress 方式”。 我在此处使用的过滤器仅用于此类情况。
我不建议删除过滤器,而是创建一个查询变量,这两个函数将检查该变量。
我同意。 这就是我为我的版本所做的。
作为 gist 实现(未经测试):https://gist.github.com/mcaskill/d3ada21f9ff0800a6970
这将严重影响性能。 一旦您获得 10,000 多行,您就会注意到大量问题。 最好将此作为查询的一部分交给 MySQL,您可以使用 CASE 或 IF 语句来完成。
当然,如果您查询 10,000 多行,您将遇到各种性能问题。 这时您将使用瞬态或缓存插件,例如 WP Super Cache。
您建议怎么做?
如果您真的认为在代码中排序比在数据库中排序速度更快,请不要构建任何复杂的东西。
我不确定我理解您是从哪里来的。 文章中的代码使用 CASE 方法创建了一个自定义 MySQL 查询。
如何在不编写代码的情况下创建要在模板文件中使用的 MySQL 查询?
另一种选择是在 MySQL 查询中使用
trim()
,例如...ORDER BY TRIM(LEADING 'a ' FROM TRIM(LEADING 'an ' FROM TRIM(LEADING 'the ' FROM LOWER(`post_title`))))
。一个简单的递归函数可以生成该 orderby 子句。
然后,在设计方面,将 CSS 应用于
$matches
以降低不透明度或更改颜色,以减少首字母词并向用户清楚地表明列表是按有效的第一个单词进行字母排序的。两个词:The The ;)
更多一些词:“在回复之前更仔细地阅读正则表达式” :)
很棒的分享 - 有趣的是,如此简单的事情,比如忽略“the”,看起来在实际实现时却如此复杂。