重要免责声明:正确的方法不是修改表结构,而是使用wp\\u usermeta。然后,您将不需要创建任何自定义SQL来查询您的帖子(尽管您仍然需要一些自定义SQL来获取向特定主管报告的所有人的列表,例如在管理部分)。然而,由于OP询问如何编写自定义SQL,下面是将自定义SQL注入现有WordPress查询的当前最佳实践。
如果要进行复杂的联接,不能只使用posts\\U where过滤器,因为还需要修改查询的联接、select以及group by或order by部分。
最好使用“posts\\u子句”过滤器。这是一个非常有用的过滤器(不应该滥用!)这允许您附加/修改由WordPress核心中的多行代码自动生成的SQL的各个部分。筛选器回调签名为:function posts_clauses_filter_cb( $clauses, $query_object ){ }
它希望你回来$clauses
.
条款
$clauses
是包含以下键的数组;每个键都是一个SQL字符串,将在发送到数据库的最终SQL语句中直接使用:
如果要向数据库中添加表(只有在绝对不能利用post\\u meta、user\\u meta或分类法的情况下才这样做),则可能需要接触这些子句中的多个,例如fields
(SQL语句的“SELECT”部分)join
(除了“FROM”子句中的表之外,您的所有表),以及orderby
.
修改子句最好的方法是从$clauses
从筛选器获得的数组:
$join = &$clauses[\'join\'];
现在,如果您修改
$join
, 实际上,您将直接修改
$clauses[\'join\']
所以这些变化将在
$clauses
当你归还它的时候。
保留原始子句很可能(不,说真的,仔细听)您希望保留WordPress为您生成的现有SQL。如果没有,您可能应该查看posts_request
而是过滤-这是在发送到数据库之前的完整mySQL查询,因此您可以完全用自己的查询进行过滤。你为什么要这样做?你可能不会。
因此,为了保留子句中现有的SQL,请记住附加到子句,而不是分配给它们(即:使用$join .= \' {NEW SQL STUFF}\';
不$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;
这是一种风格,胜过其他任何东西。要记住的重要一点是:
always leave a space BEFORE your string if you\'re appending to a clause that already has some SQL in it!WordPress开发的第一条规则是尽可能多地使用核心功能这是证明你的工作未来的最好方法。假设核心团队决定WordPress现在将使用SQLite、Oracle或其他数据库语言。任何手写的mySQL可能会变得无效,并破坏您的插件或主题!最好让WP自己生成尽可能多的SQL,只需添加所需的位。
因此,首要的业务是利用WP_Query
尽可能多地生成基本查询。我们使用的确切方法在很大程度上取决于这个帖子列表应该出现在哪里。如果它是页面的一个子部分(不是您的主查询),您将使用get_posts()
; 如果这是主查询,我想您可以使用query_posts()
但正确的方法是在主查询访问数据库(并消耗服务器周期)之前拦截它,所以使用request
滤器
好的,您已经生成了查询,即将创建SQL。事实上,它已经创建,只是没有发送到数据库。通过使用posts_clauses
过滤器中,您要将员工关系表添加到组合中。我们把这个表叫做{$wpdb->前缀}。”user\\u relationship\',它是一个交集表。(顺便说一句,我建议您将此表结构泛化,并将其转换为具有以下字段的适当交集表:“relationship\\u id”、“user\\u id”、“related\\u user\\u id”、“relationship\\u type”;这更灵活、更强大……但我离题了)。
如果我知道你想做什么,你就要传递一个领导者的ID,然后只看到该领导者追随者的帖子。我希望我没弄错。如果不正确,你必须接受我说的话,并根据你的需要加以调整。我会坚持你的桌子结构:我们有leader_id
和afollower_id
. 所以连接将打开{$wpdb->posts}.post_author
作为“user\\u relationship”表上“follower\\u 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;
}