重要的免责声明:执行此操作的正确方法不是修改表结构,而是使用wp_usermeta。然后,您将不需要创建任何自定义SQL来查询您的帖子(例如,您仍然需要一些自定义SQL来获取向特定主管报告的每个人的列表-例如在“管理”部分)。但是,由于OP询问编写自定义SQL的问题,因此这是将自定义SQL注入现有WordPress查询的当前最佳实践。
如果您要进行复杂的联接,则不能仅使用posts_where过滤器,因为您将需要修改联接,选择以及可能的查询部分的分组或排序。
最好的选择是使用“ posts_clauses”过滤器。这是一个非常有用的过滤器(请勿滥用!),它使您可以附加/修改由WordPress核心内的许多代码行自动生成的SQL的各个部分。过滤器回调签名为:
function posts_clauses_filter_cb( $clauses, $query_object ){ }
并且希望您返回$clauses
。
条款
$clauses
是包含以下键的数组;每个键都是一个SQL字符串,将直接在发送到数据库的最终SQL语句中使用:
- 哪里
- 通过...分组
- 加入
- 订购
- 不同
- 领域
- 极限
如果您要向数据库中添加表(仅在绝对不能利用post_meta,user_meta或分类法的情况下才这样做),您可能需要触摸多个这些子句,例如,fields
(“ SQL语句的一部分)join
(所有表,“ FROM”子句中的表除外),也许还有orderby
。
修改条款
最好的方法是从$clauses
您从过滤器获得的数组中引用相关的键:
$join = &$clauses['join'];
现在,如果您修改$join
,那么您实际上将直接进行修改,$clauses['join']
因此更改将在$clauses
您返回时进行。
保留原始条款
您可能会想要(不,认真地听着),保留WordPress为您生成的现有SQL。如果没有,您可能应该看看posts_request
改为过滤器-这是在将mySQL查询发送到数据库之前的完整查询,因此您可以完全用自己过滤器对其进行处理。你为什么想做这个?你可能不会。
因此,为了保留子句中的现有SQL,请记住要追加到子句中,而不要分配给子句(即:使用$join .= ' {NEW SQL STUFF}';
not $join = '{CLOBBER SQL STUFF}';
。请注意,由于$clauses
数组的每个元素都是字符串,因此如果要附加到它,您可能要在任何其他字符标记之前插入一个空格,否则可能会创建一些SQL语法错误。
您可以假设每个子句中总会有东西,因此请记住,每个新字符串都以空格开头,例如:$join .= ' my_table
,或者,您总可以添加一行,仅在需要时才添加一个空格:
$join = &$clauses['join'];
if (! empty( $join ) ) $join .= ' ';
$join .= "JOIN my_table... "; // <-- note the space at the end
$join .= "JOIN my_other_table... ";
return $clauses;
这是一件风格上的事情,比什么都重要。要记住的重要一点是:如果要追加到已经包含一些SQL的子句中,请务必在字符串之前留一个空格!
把它放在一起
WordPress开发的第一条规则是尝试使用尽可能多的核心功能。这是将来证明您的工作的最佳方法。假设核心团队决定WordPress现在将使用SQLite或Oracle或其他某种数据库语言。任何手写的mySQL都可能无效并破坏您的插件或主题!最好让WP自己生成尽可能多的SQL,然后添加所需的位。
因此,第一笔业务是利用WP_Query
生成尽可能多的基本查询。我们用它来做到这一点的确切的方法在很大程度上取决于地方帖子这个名单是应该出现。如果它是页面的一部分(不是您的主要查询),则应使用get_posts()
; 如果它是主要查询,我想您可以使用query_posts()
并完成它,但是正确的方法是在主要查询到达数据库(并消耗服务器周期)之前对其进行拦截,因此请使用request
过滤器。
好的,因此您已经生成了查询,并且即将创建SQL。好吧,实际上,它已经创建,只是没有发送到数据库。通过使用posts_clauses
过滤器,您将把员工关系表添加到组合中。我们将此表称为{$ wpdb-> prefix}。'user_relationship',它是一个交集表。(顺便说一句,我建议您通用化此表结构并将其转换为具有以下字段的正确交集表:'relationship_id','user_id','related_user_id','relationship_type';这更加灵活和强大。 ..但我离题了。
如果我了解您要执行的操作,则需要传递领导者ID,然后仅查看该领导者关注者的帖子。我希望我没错。如果不正确,您将不得不接受我所说的话,并使其适应您的需求。我会坚持使用您的表结构:我们有一个leader_id
和一个follower_id
。因此,JOIN将{$wpdb->posts}.post_author
作为“ user_relationship”表上“ follower_id”的外键打开。
add_filter( 'posts_clauses', 'filter_by_leader_id', 10, 2 ); // we need the 2 because we want to get all the arguments
function filter_by_leader_id( $clauses, $query_object ){
// I don't know how you intend to pass the leader_id, so let's just assume it's a global
global $leader_id;
// In this example I only want to affect a query on the home page.
// This is where the $query_object is used, to help us avoid affecting
// ALL queries (since ALL queries pass through this filter)
if ( $query_object->is_home() ){
// Now, let's add your table into the SQL
$join = &$clauses['join'];
if (! empty( $join ) ) $join .= ' '; // add a space only if we have to (for bonus marks!)
$join .= "JOIN {$wpdb->prefix}employee_relationship EMP_R ON EMP_R.follower_id = {$wpdb->posts}.author_id";
// And make sure we add it to our selection criteria
$where = &$clauses['where'];
// Regardless, you always start with AND, because there's always a '1=1' statement as the first statement of the WHERE clause that's added in by WP/
// Just don't forget the leading space!
$where .= " AND EMP_R.leader_id={$leader_id}"; // assuming $leader_id is always (int)
// And I assume you'll want the posts "grouped" by user id, so let's modify the groupby clause
$groupby = &$clauses['groupby'];
// We need to prepend, so...
if (! empty( $groupby ) ) $groupby = ' ' . $groupby; // For the show-offs
$groupby = "{$wpdb->posts}.post_author" . $groupby;
}
// Regardless, we need to return our clauses...
return $clauses;
}