<!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][11087] trunk: Groups: Improve query efficiency for 'admins' and 'mods' properties of group objects.</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" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://buddypress.trac.wordpress.org/changeset/11087">11087</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://buddypress.trac.wordpress.org/changeset/11087","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>boonebgorges</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2016-09-13 04:05:07 +0000 (Tue, 13 Sep 2016)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Groups: Improve query efficiency for 'admins' and 'mods' properties of group objects.

Previously, the 'admins' and 'mods' property of `BP_Groups_Group`
objects were only populated when setting the 'populate_extras' flag.
Even then, the query used to populate these properties was uncached,
and required a join against a global table.

This changeset reworks the way that the 'admins' and 'mods' properties
are accessed and set. The properties are now marked `protected`, and
are accessible by magic `__get()`. When requested, the cache for the
both properties is set by a single pair of queries: one to fetch
membership data from the BP table, and one to get user objects from
WordPress. The BP table query is cached, and neither query takes place
if the property is never accessed.

This moves us a step closer to eliminating the `populate_extras` flag
on `BP_Groups_Group` objects.

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

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcbpgroupsbpgroupscachephp">trunk/src/bp-groups/bp-groups-cache.php</a></li>
<li><a href="#trunksrcbpgroupsclassesclassbpgroupsgroupphp">trunk/src/bp-groups/classes/class-bp-groups-group.php</a></li>
<li><a href="#trunksrcbpgroupsclassesclassbpgroupsmemberphp">trunk/src/bp-groups/classes/class-bp-groups-member.php</a></li>
<li><a href="#trunktestsphpunittestcasesgroupscachephp">trunk/tests/phpunit/testcases/groups/cache.php</a></li>
<li><a href="#trunktestsphpunittestcasesgroupsclassbpgroupsgroupphp">trunk/tests/phpunit/testcases/groups/class-bp-groups-group.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcbpgroupsbpgroupscachephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/bp-groups/bp-groups-cache.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/bp-groups/bp-groups-cache.php   2016-09-13 02:59:45 UTC (rev 11086)
+++ trunk/src/bp-groups/bp-groups-cache.php     2016-09-13 04:05:07 UTC (rev 11087)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -184,7 +184,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'groups_remove_member', 'groups_clear_group_user_object_cache', 10, 2 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Clear group administrator cache.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Clear group administrator and moderator cache.
</ins><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 2.1.0
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -192,15 +192,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> function groups_clear_group_administrator_cache( $group_id ) {
</span><span class="cx" style="display: block; padding: 0 10px">        wp_cache_delete( $group_id, 'bp_group_admins' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        wp_cache_delete( $group_id, 'bp_group_mods' );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'groups_promote_member', 'groups_clear_group_administrator_cache' );
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'groups_demote_member',  'groups_clear_group_administrator_cache' );
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'groups_delete_group',   'groups_clear_group_administrator_cache' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Clear group administrator cache when a group member is saved.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Clear group administrator and moderator cache when a group member is saved.
</ins><span class="cx" style="display: block; padding: 0 10px">  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * This accounts for situations where group administrators are added manually
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * This accounts for situations where group admins or mods are added manually
</ins><span class="cx" style="display: block; padding: 0 10px">  * using {@link BP_Groups_Member::save()}.  Usually via a plugin.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 2.1.0
</span></span></pre></div>
<a id="trunksrcbpgroupsclassesclassbpgroupsgroupphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/bp-groups/classes/class-bp-groups-group.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/bp-groups/classes/class-bp-groups-group.php     2016-09-13 02:59:45 UTC (rev 11086)
+++ trunk/src/bp-groups/classes/class-bp-groups-group.php       2016-09-13 04:05:07 UTC (rev 11087)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -89,7 +89,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 1.6.0
</span><span class="cx" style="display: block; padding: 0 10px">         * @var array
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public $admins;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ protected $admins;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Data about the group's moderators.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -97,7 +97,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 1.6.0
</span><span class="cx" style="display: block; padding: 0 10px">         * @var array
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public $mods;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ protected $mods;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Total count of group members.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -220,28 +220,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                // Are we getting extra group data?
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! empty( $this->args['populate_extras'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        /**
-                        * Filters the SQL prepared statement used to fetch group admins and mods.
-                        *
-                        * @since 1.5.0
-                        *
-                        * @param string $value SQL select statement used to fetch admins and mods.
-                        */
-                       $admin_mods = $wpdb->get_results( apply_filters( 'bp_group_admin_mods_user_join_filter', $wpdb->prepare( "SELECT u.ID as user_id, u.user_login, u.user_email, u.user_nicename, m.is_admin, m.is_mod FROM {$wpdb->users} u, {$bp->groups->table_name_members} m WHERE u.ID = m.user_id AND m.group_id = %d AND ( m.is_admin = 1 OR m.is_mod = 1 )", $this->id ) ) );
-
-                       // Add admins and moderators to their respective arrays.
-                       foreach ( (array) $admin_mods as $user ) {
-                               $user->user_id  = (int) $user->user_id;
-                               $user->is_admin = (int) $user->is_admin;
-                               $user->is_mod   = (int) $user->is_mod;
-
-                               if ( !empty( $user->is_admin ) ) {
-                                       $this->admins[] = $user;
-                               } else {
-                                       $this->mods[] = $user;
-                               }
-                       }
-
</del><span class="cx" style="display: block; padding: 0 10px">                         // Set user-specific data.
</span><span class="cx" style="display: block; padding: 0 10px">                        $user_id          = bp_loggedin_user_id();
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->is_member  = groups_is_user_member( $user_id, $this->id );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -440,6 +418,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'total_member_count' :
</span><span class="cx" style="display: block; padding: 0 10px">                                return (int) groups_get_groupmeta( $this->id, 'total_member_count' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        case 'admins' :
+                               return $this->get_admins();
+
+                       case 'mods' :
+                               return $this->get_mods();
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         default :
</span><span class="cx" style="display: block; padding: 0 10px">                        break;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -467,6 +451,85 @@
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * Get a list of the group's admins.
+        *
+        * Used to provide cache-friendly access to the 'admins' property of
+        * the group object.
+        *
+        * @since 2.7.0
+        *
+        * @return array
+        */
+       protected function get_admins() {
+               if ( isset( $this->admins ) ) {
+                       return $this->admins;
+               }
+
+               $this->set_up_admins_and_mods();
+               return $this->admins;
+       }
+
+       /**
+        * Get a list of the group's mods.
+        *
+        * Used to provide cache-friendly access to the 'mods' property of
+        * the group object.
+        *
+        * @since 2.7.0
+        *
+        * @return array
+        */
+       protected function get_mods() {
+               if ( isset( $this->mods ) ) {
+                       return $this->mods;
+               }
+
+               $this->set_up_admins_and_mods();
+               return $this->mods;
+       }
+
+       /**
+        * Set up admins and mods for the current group object.
+        *
+        * Called only when the 'admins' or 'mods' property is accessed.
+        *
+        * @since 2.7.0
+        */
+       protected function set_up_admins_and_mods() {
+               $admin_ids = BP_Groups_Member::get_group_administrator_ids( $this->id );
+               $admin_ids_plucked = wp_list_pluck( $admin_ids, 'user_id' );
+
+               $mod_ids = BP_Groups_Member::get_group_moderator_ids( $this->id );
+               $mod_ids_plucked = wp_list_pluck( $mod_ids, 'user_id' );
+
+               $admin_mod_users = get_users( array(
+                       'include' => array_merge( $admin_ids_plucked, $mod_ids_plucked ),
+               ) );
+
+               $admin_objects = $mod_objects = array();
+               foreach ( $admin_mod_users as $admin_mod_user ) {
+                       $obj = new stdClass();
+                       $obj->user_id = $admin_mod_user->ID;
+                       $obj->user_login = $admin_mod_user->user_login;
+                       $obj->user_email = $admin_mod_user->user_email;
+                       $obj->user_nicename = $admin_mod_user->user_nicename;
+
+                       if ( in_array( $admin_mod_user->ID, $admin_ids_plucked, true ) ) {
+                               $obj->is_admin = 1;
+                               $obj->is_mod = 0;
+                               $admin_objects[] = $obj;
+                       } else {
+                               $obj->is_admin = 0;
+                               $obj->is_mod = 1;
+                               $mod_objects[] = $obj;
+                       }
+               }
+
+               $this->admins = $admin_objects;
+               $this->mods   = $mod_objects;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         /** Static Methods ****************************************************/
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1029,7 +1092,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Prefetch all administrator IDs, if requested.
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $r['update_admin_cache'] ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        BP_Groups_Member::prime_group_administrator_ids_cache( $group_ids );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 BP_Groups_Member::prime_group_admins_mods_cache( $group_ids );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Set up integer properties needing casting.
</span></span></pre></div>
<a id="trunksrcbpgroupsclassesclassbpgroupsmemberphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/bp-groups/classes/class-bp-groups-member.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/bp-groups/classes/class-bp-groups-member.php    2016-09-13 02:59:45 UTC (rev 11086)
+++ trunk/src/bp-groups/classes/class-bp-groups-member.php      2016-09-13 04:05:07 UTC (rev 11087)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1043,7 +1043,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $group_admins = wp_cache_get( $group_id, 'bp_group_admins' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( false === $group_admins ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        self::prime_group_administrator_ids_cache( array( $group_id ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 self::prime_group_admins_mods_cache( array( $group_id ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                         $group_admins = wp_cache_get( $group_id, 'bp_group_admins' );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1063,7 +1063,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @param array $group_ids IDs of the groups.
</span><span class="cx" style="display: block; padding: 0 10px">         * @return bool True on success.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public static function prime_group_administrator_ids_cache( $group_ids ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public static function prime_group_admins_mods_cache( $group_ids ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 global $wpdb;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $uncached = bp_get_non_cached_ids( $group_ids, 'bp_group_admins' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1071,21 +1071,31 @@
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $uncached ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $bp = buddypress();
</span><span class="cx" style="display: block; padding: 0 10px">                        $uncached_sql = implode( ',', array_map( 'intval', $uncached ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $group_admins = $wpdb->get_results( "SELECT user_id, group_id, date_modified FROM {$bp->groups->table_name_members} WHERE group_id IN ({$uncached_sql}) AND is_admin = 1 AND is_banned = 0" );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $group_admin_mods = $wpdb->get_results( "SELECT user_id, group_id, date_modified, is_admin, is_mod FROM {$bp->groups->table_name_members} WHERE group_id IN ({$uncached_sql}) AND ( is_admin = 1 OR is_mod = 1 ) AND is_banned = 0" );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $groups = array();
-                       if ( $group_admins ) {
-                               foreach ( $group_admins as $group_admin ) {
-                                       $admin_obj = new stdClass();
-                                       $admin_obj->user_id = $group_admin->user_id;
-                                       $admin_obj->date_modified = $group_admin->date_modified;
-                                       $groups[ $group_admin->group_id ][] = $admin_obj;
-                               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $admins = $mods = array();
+                       if ( $group_admin_mods ) {
+                               foreach ( $group_admin_mods as $group_admin_mod ) {
+                                       $obj = new stdClass();
+                                       $obj->user_id = $group_admin_mod->user_id;
+                                       $obj->date_modified = $group_admin_mod->date_modified;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                foreach ( $groups as $this_group_id => $this_group_admins ) {
-                                       wp_cache_set( $this_group_id, $this_group_admins, 'bp_group_admins' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 if ( $group_admin_mod->is_admin ) {
+                                               $admins[ $group_admin_mod->group_id ][] = $obj;
+                                       } else {
+                                               $mods[ $group_admin_mod->group_id ][] = $obj;
+                                       }
</ins><span class="cx" style="display: block; padding: 0 10px">                                 }
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       // Prime cache for all groups, even those with no matches.
+                       foreach ( $uncached as $group_id ) {
+                               $group_admins = isset( $admins[ $group_id ] ) ? $admins[ $group_id ] : array();
+                               wp_cache_set( $group_id, $group_admins, 'bp_group_admins' );
+
+                               $group_mods = isset( $mods[ $group_id ] ) ? $mods[ $group_id ] : array();
+                               wp_cache_set( $group_id, $group_mods, 'bp_group_mods' );
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1100,9 +1110,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public static function get_group_moderator_ids( $group_id ) {
</span><span class="cx" style="display: block; padding: 0 10px">                global $wpdb;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $bp = buddypress();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $group_mods = wp_cache_get( $group_id, 'bp_group_mods' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $group_mods = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, date_modified FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_mod = 1 AND is_banned = 0", $group_id ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( false === $group_mods ) {
+                       self::prime_group_admins_mods_cache( array( $group_id ) );
+                       $group_mods = wp_cache_get( $group_id, 'bp_group_mods' );
+               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Integer casting.
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( (array) $group_mods as $key => $data ) {
</span></span></pre></div>
<a id="trunktestsphpunittestcasesgroupscachephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/testcases/groups/cache.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/testcases/groups/cache.php    2016-09-13 02:59:45 UTC (rev 11086)
+++ trunk/tests/phpunit/testcases/groups/cache.php      2016-09-13 04:05:07 UTC (rev 11087)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -181,6 +181,46 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @group groups_get_group_mods
+        */
+       public function test_groups_get_group_mods_cache() {
+               $u1 = $this->factory->user->create();
+               $u2 = $this->factory->user->create();
+               $g = $this->factory->group->create( array( 'creator_id' => $u1 ) );
+
+               // User 2 joins the group
+               groups_join_group( $g, $u2 );
+
+               // prime cache
+               groups_get_group_mods( $g );
+
+               // promote user 2 to an admin
+               bp_update_is_item_admin( true );
+               groups_promote_member( $u2, $g, 'mod' );
+
+               // assert new cached value
+               $this->assertEquals( 1, count( groups_get_group_mods( $g ) ) );
+       }
+
+       /**
+        * @group groups_get_group_mods
+        */
+       public function test_groups_get_group_mods_cache_on_member_save() {
+               $u1 = $this->factory->user->create();
+               $u2 = $this->factory->user->create();
+               $g = $this->factory->group->create( array( 'creator_id' => $u1 ) );
+
+               // prime cache
+               groups_get_group_mods( $g );
+
+               // promote user 2 to an admin via BP_Groups_Member::save()
+               self::add_user_to_group( $u2, $g, array( 'is_mod' => 1 ) );
+
+               // assert new cached value
+               $this->assertEquals( 1, count( groups_get_group_mods( $g ) ) );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * @group groups_get_group_admins
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_groups_get_group_admins_cache_on_member_save() {
</span></span></pre></div>
<a id="trunktestsphpunittestcasesgroupsclassbpgroupsgroupphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/testcases/groups/class-bp-groups-group.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/testcases/groups/class-bp-groups-group.php    2016-09-13 02:59:45 UTC (rev 11086)
+++ trunk/tests/phpunit/testcases/groups/class-bp-groups-group.php      2016-09-13 04:05:07 UTC (rev 11087)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1242,6 +1242,63 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @ticket BP5451
+        */
+       public function test_admins_property() {
+               $user_1 = $this->factory->user->create_and_get();
+               $g = $this->factory->group->create( array(
+                       'creator_id' => $user_1->ID,
+               ) );
+
+               $group = new BP_Groups_Group( $g );
+
+               $expected_admin_props = array(
+                       'user_id' => $user_1->ID,
+                       'user_login' => $user_1->user_login,
+                       'user_email' => $user_1->user_email,
+                       'user_nicename' => $user_1->user_nicename,
+                       'is_admin' => 1,
+                       'is_mod' => 0,
+               );
+
+               $found_admin = $group->admins[0];
+               foreach ( $expected_admin_props as $prop => $value ) {
+                       $this->assertEquals( $value, $found_admin->{$prop} );
+               }
+       }
+
+       /**
+        * @ticket BP5451
+        */
+       public function test_mods_property() {
+               $users = $this->factory->user->create_many( 2 );
+               $user_1 = new WP_User( $users[0] );
+               $user_2 = new WP_User( $users[1] );
+
+               $g = $this->factory->group->create( array(
+                       'creator_id' => $user_1->ID,
+               ) );
+
+               $this->add_user_to_group( $user_2->ID, $g, array( 'is_mod' => 1 ) );
+
+               $group = new BP_Groups_Group( $g );
+
+               $expected_mod_props = array(
+                       'user_id' => $user_2->ID,
+                       'user_login' => $user_2->user_login,
+                       'user_email' => $user_2->user_email,
+                       'user_nicename' => $user_2->user_nicename,
+                       'is_admin' => 0,
+                       'is_mod' => 1,
+               );
+
+               $found_mod = $group->mods[0];
+               foreach ( $expected_mod_props as $prop => $value ) {
+                       $this->assertEquals( $value, $found_mod->{$prop} );
+               }
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * @group group_types
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function test_group_type_single_value() {
</span></span></pre>
</div>
</div>

</body>
</html>