Skip to:
Content

BuddyPress.org

Changeset 11087


Ignore:
Timestamp:
09/13/2016 04:05:07 AM (10 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.