<!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][7322] trunk: Fixes groups meta_query for multiple clauses</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/7322">7322</a></dd>
<dt>Author</dt> <dd>boonebgorges</dd>
<dt>Date</dt> <dd>2013-07-27 18:06:17 +0000 (Sat, 27 Jul 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>Fixes groups meta_query for multiple clauses

The 1.8 implementation of meta_query for BP_Groups_Group::get() did not
properly handle multiple clauses, because of certain necessary
transformations of the SQL syntax to match the peculiar syntax in the
groups component. This changeset fixes the problem by improving the
logic used to parse the SQL returned from the WP_Meta_Query object.

A related included fix is that a DISTINCT keyword has been added to the
main SELECT clause of the groups total sql statement. This ensures that
multi-clause meta_query queries will not return duplicate values.

Also adds an automated test for the situation described above.

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

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbpgroupsbpgroupsclassesphp">trunk/bp-groups/bp-groups-classes.php</a></li>
<li><a href="#trunkteststestcasesgroupsclassbpgroupsgroupphp">trunk/tests/testcases/groups/class-bp-groups-group.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbpgroupsbpgroupsclassesphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-groups/bp-groups-classes.php (7321 => 7322)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-groups/bp-groups-classes.php    2013-07-27 18:06:14 UTC (rev 7321)
+++ trunk/bp-groups/bp-groups-classes.php       2013-07-27 18:06:17 UTC (rev 7322)
</span><span class="lines">@@ -353,7 +353,7 @@
</span><span class="cx">          $sql       = array();
</span><span class="cx">          $total_sql = array();
</span><span class="cx"> 
</span><del>-               $sql['select'] = "SELECT g.*, gm1.meta_value AS total_member_count, gm2.meta_value AS last_activity";
</del><ins>+                $sql['select'] = "SELECT DISTINCT g.id, g.*, gm1.meta_value AS total_member_count, gm2.meta_value AS last_activity";
</ins><span class="cx">           $sql['from']   = " FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2,";
</span><span class="cx"> 
</span><span class="cx">          if ( ! empty( $r['user_id'] ) ) {
</span><span class="lines">@@ -468,11 +468,10 @@
</span><span class="cx">          // See #5099
</span><span class="cx">          if ( ! empty( $meta_query_sql['where'] ) ) {
</span><span class="cx">                  // Join the groupmeta table
</span><del>-                       $total_sql['select'] .= ", {$bp->groups->table_name_groupmeta} gmmq";
</del><ins>+                        $total_sql['select'] .= ", ". substr( $meta_query_sql['join'], 0, -2 );
</ins><span class="cx"> 
</span><span class="cx">                  // Modify the meta_query clause from paged_sql for our syntax
</span><span class="cx">                  $meta_query_clause = preg_replace( '/^\s*AND/', '', $meta_query_sql['where'] );
</span><del>-                       $meta_query_clause = str_replace( $bp->groups->table_name_groupmeta, 'gmmq', $meta_query_clause );
</del><span class="cx">                   $total_sql['where'][] = $meta_query_clause;
</span><span class="cx">          }
</span><span class="cx"> 
</span><span class="lines">@@ -560,11 +559,19 @@
</span><span class="cx">                  // @todo It may be better in the long run to refactor
</span><span class="cx">                  // the more general query syntax to accord better with
</span><span class="cx">                  // BP/WP convention
</span><del>-                       preg_match( '/INNER JOIN (.*) ON/', $meta_sql['join'], $matches_a );
-                       preg_match( '/ON \((.*)\)$/', $meta_sql['join'], $matches_b );
</del><ins>+                        preg_match_all( '/INNER JOIN (.*) ON/', $meta_sql['join'], $matches_a );
+                       preg_match_all( '/ON \((.*)\)/', $meta_sql['join'], $matches_b );
+
</ins><span class="cx">                   if ( ! empty( $matches_a[1] ) && ! empty( $matches_b[1] ) ) {
</span><del>-                               $sql_array['join']  = $matches_a[1] . ', ';
-                               $sql_array['where'] = preg_replace( '/^(\sAND\s+[\(\s]+)/', '$1' . $matches_b[1] . ' AND ', $meta_sql['where'] );
</del><ins>+                                $sql_array['join']  = implode( ',', $matches_a[1] ). ', ';
+
+                               $sql_array['where'] = '';
+
+                               $meta_query_where_clauses = explode( "\n", $meta_sql['where'] );
+                               foreach( $matches_b[1] as $key => $group_id_clause ) {
+                                       $sql_array['where'] .= ' ' . preg_replace( '/^(AND\s+[\(\s]+)/', '$1' . $group_id_clause . ' AND ', ltrim( $meta_query_where_clauses[ $key ] ) );
+                               }
+
</ins><span class="cx">                   }
</span><span class="cx">          }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkteststestcasesgroupsclassbpgroupsgroupphp"></a>
<div class="modfile"><h4>Modified: trunk/tests/testcases/groups/class-bp-groups-group.php (7321 => 7322)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/tests/testcases/groups/class-bp-groups-group.php   2013-07-27 18:06:14 UTC (rev 7321)
+++ trunk/tests/testcases/groups/class-bp-groups-group.php      2013-07-27 18:06:17 UTC (rev 7322)
</span><span class="lines">@@ -99,7 +99,44 @@
</span><span class="cx"> 
</span><span class="cx">  /**
</span><span class="cx">   * @group get
</span><ins>+        * @group group_meta_query
</ins><span class="cx">    */
</span><ins>+       public function test_get_with_meta_query_multiple_clauses() {
+               $now = time();
+               $g1 = $this->factory->group->create( array(
+                       'last_activity' => date( 'Y-m-d H:i:s', $now - 60*60 ),
+               ) );
+               $g2 = $this->factory->group->create( array(
+                       'last_activity' => date( 'Y-m-d H:i:s', $now - 60*60*2 ),
+               ) );
+               $g3 = $this->factory->group->create( array(
+                       'last_activity' => date( 'Y-m-d H:i:s', $now - 60*60*3 ),
+               ) );
+               groups_update_groupmeta( $g1, 'foo', 'bar' );
+               groups_update_groupmeta( $g2, 'foo', 'bar' );
+               groups_update_groupmeta( $g1, 'bar', 'barry' );
+
+               $groups = BP_Groups_Group::get( array(
+                       'meta_query' => array(
+                               'relation' => 'AND',
+                               array(
+                                       'key' => 'foo',
+                                       'value' => 'bar',
+                               ),
+                               array(
+                                       'key' => 'bar',
+                                       'value' => 'barry',
+                               ),
+                       ),
+               ) );
+               $ids = wp_list_pluck( $groups['groups'], 'id' );
+               $this->assertEquals( $ids, array( $g1 ) );
+               $this->assertEquals( 1, $groups['total'] );
+       }
+
+       /**
+        * @group get
+        */
</ins><span class="cx">   public function test_get_normal_search() {
</span><span class="cx">          $g1 = $this->factory->group->create( array(
</span><span class="cx">                  'name' => 'Cool Group',
</span></span></pre>
</div>
</div>

</body>
</html>