<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[BuddyPress][8087] trunk: BP_User_Query search_terms should match against user_login and user_nicename in addition to xprofile fields</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://buddypress.trac.wordpress.org/changeset/8087">8087</a></dd>
<dt>Author</dt> <dd>boonebgorges</dd>
<dt>Date</dt> <dd>2014-03-09 13:58:11 +0000 (Sun, 09 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>BP_User_Query search_terms should match against user_login and user_nicename in addition to xprofile fields

This changeset changes the search_terms logic in BP_User_Query so that it
matches user_login and user_nicename in the core users table. Then, via filter,
the xprofile component amends the WHERE clause so that search_terms are matched
in the users table OR the xprofile data table.

Clauses have been refactored to subqueries rather than storing the matched
user IDs in PHP, for improved performance.

See <a href="http://buddypress.trac.wordpress.org/ticket/5155">#5155</a></pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbpcorebpcoreclassesphp">trunk/bp-core/bp-core-classes.php</a></li>
<li><a href="#trunkbpxprofilebpxprofilefunctionsphp">trunk/bp-xprofile/bp-xprofile-functions.php</a></li>
<li><a href="#trunkteststestcasescoreclassbpuserqueryphp">trunk/tests/testcases/core/class-bp-user-query.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbpcorebpcoreclassesphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-core/bp-core-classes.php (8086 => 8087)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-core/bp-core-classes.php        2014-03-08 20:35:16 UTC (rev 8086)
+++ trunk/bp-core/bp-core-classes.php   2014-03-09 13:58:11 UTC (rev 8087)
</span><span class="lines">@@ -361,20 +361,11 @@
</span><span class="cx"> 
</span><span class="cx">          /** Search Terms ******************************************************/
</span><span class="cx"> 
</span><del>-               // 'search_terms' searches the xprofile fields
-               // To avoid global joins, do a separate query
-               // @todo remove need for bp_is_active() check
-               if ( false !== $search_terms && bp_is_active( 'xprofile' ) ) {
</del><ins>+                // 'search_terms' searches user_login and user_nicename
+               // xprofile field matches happen in bp_xprofile_bp_user_query_search()
+               if ( false !== $search_terms ) {
</ins><span class="cx">                   $search_terms_clean = esc_sql( esc_sql( $search_terms ) );
</span><del>-                       $search_terms_clean = like_escape( $search_terms_clean );
-                       $found_user_ids_query = "SELECT user_id FROM {$bp->profile->table_name_data} WHERE value LIKE '%" . $search_terms_clean . "%'";
-                       $found_user_ids = $wpdb->get_col( $found_user_ids_query );
-
-                       if ( ! empty( $found_user_ids ) ) {
-                               $sql['where'][] = "u.{$this->uid_name} IN (" . implode( ',', wp_parse_id_list( $found_user_ids ) ) . ")";
-                       } else {
-                               $sql['where'][] = $this->no_results['where'];
-                       }
</del><ins>+                        $sql['where']['search'] = "u.{$this->uid_name} IN ( SELECT ID FROM {$wpdb->users} WHERE ( user_login LIKE '%{$search_terms_clean}%' OR user_nicename LIKE '%{$search_terms_clean}%' ) )";
</ins><span class="cx">           }
</span><span class="cx"> 
</span><span class="cx">          // 'meta_key', 'meta_value' allow usermeta search
</span><span class="lines">@@ -400,6 +391,9 @@
</span><span class="cx">                  $sql['limit'] = '';
</span><span class="cx">          }
</span><span class="cx"> 
</span><ins>+               // Allow custom filters
+               $sql = apply_filters_ref_array( 'bp_user_query_uid_clauses', array( $sql, &$this ) );
+
</ins><span class="cx">           // Assemble the query chunks
</span><span class="cx">          $this->uid_clauses['select']  = $sql['select'];
</span><span class="cx">          $this->uid_clauses['where']   = ! empty( $sql['where'] ) ? 'WHERE ' . implode( ' AND ', $sql['where'] ) : '';
</span></span></pre></div>
<a id="trunkbpxprofilebpxprofilefunctionsphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-xprofile/bp-xprofile-functions.php (8086 => 8087)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-xprofile/bp-xprofile-functions.php      2014-03-08 20:35:16 UTC (rev 8086)
+++ trunk/bp-xprofile/bp-xprofile-functions.php 2014-03-09 13:58:11 UTC (rev 8087)
</span><span class="lines">@@ -454,6 +454,37 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /**
</span><ins>+ * When search_terms are passed to BP_User_Query, search against xprofile fields.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @param array $sql Clauses in the user_id SQL query.
+ * @param BP_User_Query User query object.
+ */
+function bp_xprofile_bp_user_query_search( $sql, BP_User_Query $query ) {
+       global $wpdb;
+
+       if ( empty( $query->query_vars['search_terms'] ) || empty( $sql['where']['search'] ) ) {
+               return $sql;
+       }
+
+       $bp = buddypress();
+
+       $search_terms_clean = esc_sql( esc_sql( $query->query_vars['search_terms'] ) );
+
+       // Combine the core search (against wp_users) into a single OR clause
+       // with the xprofile_data search
+       $search_core     = $sql['where']['search'];
+       $search_xprofile = "u.{$query->uid_name} IN ( SELECT user_id FROM {$bp->profile->table_name_data} WHERE value LIKE '%{$search_terms_clean}%' )";
+       $search_combined = "( {$search_xprofile} OR {$search_core} )";
+
+       $sql['where']['search'] = $search_combined;
+
+       return $sql;
+}
+add_action( 'bp_user_query_uid_clauses', 'bp_xprofile_bp_user_query_search', 10, 2 );
+
+/**
</ins><span class="cx">  * Syncs Xprofile data to the standard built in WordPress profile data.
</span><span class="cx">  *
</span><span class="cx">  * @package BuddyPress Core
</span></span></pre></div>
<a id="trunkteststestcasescoreclassbpuserqueryphp"></a>
<div class="modfile"><h4>Modified: trunk/tests/testcases/core/class-bp-user-query.php (8086 => 8087)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/tests/testcases/core/class-bp-user-query.php       2014-03-08 20:35:16 UTC (rev 8086)
+++ trunk/tests/testcases/core/class-bp-user-query.php  2014-03-09 13:58:11 UTC (rev 8087)
</span><span class="lines">@@ -202,6 +202,24 @@
</span><span class="cx">  }
</span><span class="cx"> 
</span><span class="cx">  /**
</span><ins>+        * @group search_terms
+        */
+       public function test_bp_user_query_search_core_fields() {
+               $user_id = $this->create_user( array(
+                       'user_login' => 'foo',
+               ) );
+               xprofile_set_field_data( 1, $user_id, "Bar" );
+               $q = new BP_User_Query( array( 'search_terms' => 'foo', ) );
+
+               $found_user_id = null;
+               if ( ! empty( $q->results ) ) {
+                       $found_user = array_pop( $q->results );
+                       $found_user_id = $found_user->ID;
+               }
+
+               $this->assertEquals( $user_id, $found_user_id );
+       }
+       /**
</ins><span class="cx">    * @group exclude
</span><span class="cx">   */
</span><span class="cx">  public function test_bp_user_query_with_exclude() {
</span></span></pre>
</div>
</div>

</body>
</html>