Skip to:
Content

BuddyPress.org

Changeset 11087


Ignore:
Timestamp:
09/13/2016 04:05:07 AM (8 years ago)
Author:
boonebgorges
Message:

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 #5451.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/bp-groups/bp-groups-cache.php

    r11075 r11087  
    185185
    186186/**
    187  * Clear group administrator cache.
     187 * Clear group administrator and moderator cache.
    188188 *
    189189 * @since 2.1.0
     
    193193function groups_clear_group_administrator_cache( $group_id ) {
    194194    wp_cache_delete( $group_id, 'bp_group_admins' );
     195    wp_cache_delete( $group_id, 'bp_group_mods' );
    195196}
    196197add_action( 'groups_promote_member', 'groups_clear_group_administrator_cache' );
     
    199200
    200201/**
    201  * Clear group administrator cache when a group member is saved.
    202  *
    203  * This accounts for situations where group administrators are added manually
     202 * Clear group administrator and moderator cache when a group member is saved.
     203 *
     204 * This accounts for situations where group admins or mods are added manually
    204205 * using {@link BP_Groups_Member::save()}.  Usually via a plugin.
    205206 *
  • trunk/src/bp-groups/classes/class-bp-groups-group.php

    r11086 r11087  
    9090     * @var array
    9191     */
    92     public $admins;
     92    protected $admins;
    9393
    9494    /**
     
    9898     * @var array
    9999     */
    100     public $mods;
     100    protected $mods;
    101101
    102102    /**
     
    220220        // Are we getting extra group data?
    221221        if ( ! empty( $this->args['populate_extras'] ) ) {
    222 
    223             /**
    224              * Filters the SQL prepared statement used to fetch group admins and mods.
    225              *
    226              * @since 1.5.0
    227              *
    228              * @param string $value SQL select statement used to fetch admins and mods.
    229              */
    230             $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 ) ) );
    231 
    232             // Add admins and moderators to their respective arrays.
    233             foreach ( (array) $admin_mods as $user ) {
    234                 $user->user_id  = (int) $user->user_id;
    235                 $user->is_admin = (int) $user->is_admin;
    236                 $user->is_mod   = (int) $user->is_mod;
    237 
    238                 if ( !empty( $user->is_admin ) ) {
    239                     $this->admins[] = $user;
    240                 } else {
    241                     $this->mods[] = $user;
    242                 }
    243             }
    244222
    245223            // Set user-specific data.
     
    441419                return (int) groups_get_groupmeta( $this->id, 'total_member_count' );
    442420
     421            case 'admins' :
     422                return $this->get_admins();
     423
     424            case 'mods' :
     425                return $this->get_mods();
     426
    443427            default :
    444428            break;
     
    466450                return false;
    467451        }
     452    }
     453
     454    /**
     455     * Get a list of the group's admins.
     456     *
     457     * Used to provide cache-friendly access to the 'admins' property of
     458     * the group object.
     459     *
     460     * @since 2.7.0
     461     *
     462     * @return array
     463     */
     464    protected function get_admins() {
     465        if ( isset( $this->admins ) ) {
     466            return $this->admins;
     467        }
     468
     469        $this->set_up_admins_and_mods();
     470        return $this->admins;
     471    }
     472
     473    /**
     474     * Get a list of the group's mods.
     475     *
     476     * Used to provide cache-friendly access to the 'mods' property of
     477     * the group object.
     478     *
     479     * @since 2.7.0
     480     *
     481     * @return array
     482     */
     483    protected function get_mods() {
     484        if ( isset( $this->mods ) ) {
     485            return $this->mods;
     486        }
     487
     488        $this->set_up_admins_and_mods();
     489        return $this->mods;
     490    }
     491
     492    /**
     493     * Set up admins and mods for the current group object.
     494     *
     495     * Called only when the 'admins' or 'mods' property is accessed.
     496     *
     497     * @since 2.7.0
     498     */
     499    protected function set_up_admins_and_mods() {
     500        $admin_ids = BP_Groups_Member::get_group_administrator_ids( $this->id );
     501        $admin_ids_plucked = wp_list_pluck( $admin_ids, 'user_id' );
     502
     503        $mod_ids = BP_Groups_Member::get_group_moderator_ids( $this->id );
     504        $mod_ids_plucked = wp_list_pluck( $mod_ids, 'user_id' );
     505
     506        $admin_mod_users = get_users( array(
     507            'include' => array_merge( $admin_ids_plucked, $mod_ids_plucked ),
     508        ) );
     509
     510        $admin_objects = $mod_objects = array();
     511        foreach ( $admin_mod_users as $admin_mod_user ) {
     512            $obj = new stdClass();
     513            $obj->user_id = $admin_mod_user->ID;
     514            $obj->user_login = $admin_mod_user->user_login;
     515            $obj->user_email = $admin_mod_user->user_email;
     516            $obj->user_nicename = $admin_mod_user->user_nicename;
     517
     518            if ( in_array( $admin_mod_user->ID, $admin_ids_plucked, true ) ) {
     519                $obj->is_admin = 1;
     520                $obj->is_mod = 0;
     521                $admin_objects[] = $obj;
     522            } else {
     523                $obj->is_admin = 0;
     524                $obj->is_mod = 1;
     525                $mod_objects[] = $obj;
     526            }
     527        }
     528
     529        $this->admins = $admin_objects;
     530        $this->mods   = $mod_objects;
    468531    }
    469532
     
    10301093        // Prefetch all administrator IDs, if requested.
    10311094        if ( $r['update_admin_cache'] ) {
    1032             BP_Groups_Member::prime_group_administrator_ids_cache( $group_ids );
     1095            BP_Groups_Member::prime_group_admins_mods_cache( $group_ids );
    10331096        }
    10341097
  • trunk/src/bp-groups/classes/class-bp-groups-member.php

    r11086 r11087  
    10441044
    10451045        if ( false === $group_admins ) {
    1046             self::prime_group_administrator_ids_cache( array( $group_id ) );
     1046            self::prime_group_admins_mods_cache( array( $group_id ) );
    10471047            $group_admins = wp_cache_get( $group_id, 'bp_group_admins' );
    10481048        }
     
    10641064     * @return bool True on success.
    10651065     */
    1066     public static function prime_group_administrator_ids_cache( $group_ids ) {
     1066    public static function prime_group_admins_mods_cache( $group_ids ) {
    10671067        global $wpdb;
    10681068
     
    10721072            $bp = buddypress();
    10731073            $uncached_sql = implode( ',', array_map( 'intval', $uncached ) );
    1074             $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" );
    1075 
    1076             $groups = array();
    1077             if ( $group_admins ) {
    1078                 foreach ( $group_admins as $group_admin ) {
    1079                     $admin_obj = new stdClass();
    1080                     $admin_obj->user_id = $group_admin->user_id;
    1081                     $admin_obj->date_modified = $group_admin->date_modified;
    1082                     $groups[ $group_admin->group_id ][] = $admin_obj;
    1083                 }
    1084 
    1085                 foreach ( $groups as $this_group_id => $this_group_admins ) {
    1086                     wp_cache_set( $this_group_id, $this_group_admins, 'bp_group_admins' );
     1074            $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" );
     1075
     1076            $admins = $mods = array();
     1077            if ( $group_admin_mods ) {
     1078                foreach ( $group_admin_mods as $group_admin_mod ) {
     1079                    $obj = new stdClass();
     1080                    $obj->user_id = $group_admin_mod->user_id;
     1081                    $obj->date_modified = $group_admin_mod->date_modified;
     1082
     1083                    if ( $group_admin_mod->is_admin ) {
     1084                        $admins[ $group_admin_mod->group_id ][] = $obj;
     1085                    } else {
     1086                        $mods[ $group_admin_mod->group_id ][] = $obj;
     1087                    }
    10871088                }
    10881089            }
     1090
     1091            // Prime cache for all groups, even those with no matches.
     1092            foreach ( $uncached as $group_id ) {
     1093                $group_admins = isset( $admins[ $group_id ] ) ? $admins[ $group_id ] : array();
     1094                wp_cache_set( $group_id, $group_admins, 'bp_group_admins' );
     1095
     1096                $group_mods = isset( $mods[ $group_id ] ) ? $mods[ $group_id ] : array();
     1097                wp_cache_set( $group_id, $group_mods, 'bp_group_mods' );
     1098            }
    10891099        }
    10901100    }
     
    11011111        global $wpdb;
    11021112
    1103         $bp = buddypress();
    1104 
    1105         $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 ) );
     1113        $group_mods = wp_cache_get( $group_id, 'bp_group_mods' );
     1114
     1115        if ( false === $group_mods ) {
     1116            self::prime_group_admins_mods_cache( array( $group_id ) );
     1117            $group_mods = wp_cache_get( $group_id, 'bp_group_mods' );
     1118        }
    11061119
    11071120        // Integer casting.
  • trunk/tests/phpunit/testcases/groups/cache.php

    r9819 r11087  
    182182
    183183    /**
     184     * @group groups_get_group_mods
     185     */
     186    public function test_groups_get_group_mods_cache() {
     187        $u1 = $this->factory->user->create();
     188        $u2 = $this->factory->user->create();
     189        $g = $this->factory->group->create( array( 'creator_id' => $u1 ) );
     190
     191        // User 2 joins the group
     192        groups_join_group( $g, $u2 );
     193
     194        // prime cache
     195        groups_get_group_mods( $g );
     196
     197        // promote user 2 to an admin
     198        bp_update_is_item_admin( true );
     199        groups_promote_member( $u2, $g, 'mod' );
     200
     201        // assert new cached value
     202        $this->assertEquals( 1, count( groups_get_group_mods( $g ) ) );
     203    }
     204
     205    /**
     206     * @group groups_get_group_mods
     207     */
     208    public function test_groups_get_group_mods_cache_on_member_save() {
     209        $u1 = $this->factory->user->create();
     210        $u2 = $this->factory->user->create();
     211        $g = $this->factory->group->create( array( 'creator_id' => $u1 ) );
     212
     213        // prime cache
     214        groups_get_group_mods( $g );
     215
     216        // promote user 2 to an admin via BP_Groups_Member::save()
     217        self::add_user_to_group( $u2, $g, array( 'is_mod' => 1 ) );
     218
     219        // assert new cached value
     220        $this->assertEquals( 1, count( groups_get_group_mods( $g ) ) );
     221    }
     222
     223    /**
    184224     * @group groups_get_group_admins
    185225     */
  • trunk/tests/phpunit/testcases/groups/class-bp-groups-group.php

    r11074 r11087  
    12431243
    12441244    /**
     1245     * @ticket BP5451
     1246     */
     1247    public function test_admins_property() {
     1248        $user_1 = $this->factory->user->create_and_get();
     1249        $g = $this->factory->group->create( array(
     1250            'creator_id' => $user_1->ID,
     1251        ) );
     1252
     1253        $group = new BP_Groups_Group( $g );
     1254
     1255        $expected_admin_props = array(
     1256            'user_id' => $user_1->ID,
     1257            'user_login' => $user_1->user_login,
     1258            'user_email' => $user_1->user_email,
     1259            'user_nicename' => $user_1->user_nicename,
     1260            'is_admin' => 1,
     1261            'is_mod' => 0,
     1262        );
     1263
     1264        $found_admin = $group->admins[0];
     1265        foreach ( $expected_admin_props as $prop => $value ) {
     1266            $this->assertEquals( $value, $found_admin->{$prop} );
     1267        }
     1268    }
     1269
     1270    /**
     1271     * @ticket BP5451
     1272     */
     1273    public function test_mods_property() {
     1274        $users = $this->factory->user->create_many( 2 );
     1275        $user_1 = new WP_User( $users[0] );
     1276        $user_2 = new WP_User( $users[1] );
     1277
     1278        $g = $this->factory->group->create( array(
     1279            'creator_id' => $user_1->ID,
     1280        ) );
     1281
     1282        $this->add_user_to_group( $user_2->ID, $g, array( 'is_mod' => 1 ) );
     1283
     1284        $group = new BP_Groups_Group( $g );
     1285
     1286        $expected_mod_props = array(
     1287            'user_id' => $user_2->ID,
     1288            'user_login' => $user_2->user_login,
     1289            'user_email' => $user_2->user_email,
     1290            'user_nicename' => $user_2->user_nicename,
     1291            'is_admin' => 0,
     1292            'is_mod' => 1,
     1293        );
     1294
     1295        $found_mod = $group->mods[0];
     1296        foreach ( $expected_mod_props as $prop => $value ) {
     1297            $this->assertEquals( $value, $found_mod->{$prop} );
     1298        }
     1299    }
     1300
     1301    /**
    12451302     * @group group_types
    12461303     */
Note: See TracChangeset for help on using the changeset viewer.