Skip to:
Content

BuddyPress.org


Ignore:
Timestamp:
12/07/2018 06:41:16 PM (6 years ago)
Author:
boonebgorges
Message:

XProfile: Add caching to group and field queries.

See #7435.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/bp-xprofile/classes/class-bp-xprofile-group.php

    r12210 r12317  
    289289        );
    290290
    291         // WHERE.
     291        $bp = buddypress();
     292
     293        $group_ids = self::get_group_ids( $r );
     294
     295        // Get all group data.
     296        $groups = self::get_group_data( $group_ids );
     297
     298        // Bail if not also getting fields.
     299        if ( empty( $r['fetch_fields'] ) ) {
     300            return $groups;
     301        }
     302
     303        // Get the group ids from the groups we found.
     304        $group_ids = wp_list_pluck( $groups, 'id' );
     305
     306        // Store for meta cache priming.
     307        $object_ids['group'] = $group_ids;
     308
     309        // Bail if no groups found.
     310        if ( empty( $group_ids ) ) {
     311            return $groups;
     312        }
     313
     314        $field_ids = self::get_group_field_ids( $group_ids, $r );
     315
     316        foreach( $groups as $group ) {
     317            $group->fields = array();
     318        }
     319
     320        // Bail if no fields.
     321        if ( empty( $field_ids ) ) {
     322            return $groups;
     323        }
     324
     325        $field_ids = array_map( 'intval', $field_ids );
     326
     327        // Prime the field cache.
     328        $uncached_field_ids = bp_get_non_cached_ids( $field_ids, 'bp_xprofile_fields' );
     329        if ( ! empty( $uncached_field_ids ) ) {
     330            $_uncached_field_ids = implode( ',', array_map( 'intval', $uncached_field_ids ) );
     331            $uncached_fields = $wpdb->get_results( "SELECT * FROM {$bp->profile->table_name_fields} WHERE id IN ({$_uncached_field_ids})" );
     332            foreach ( $uncached_fields as $uncached_field ) {
     333                $fid = intval( $uncached_field->id );
     334                wp_cache_set( $fid, $uncached_field, 'bp_xprofile_fields' );
     335            }
     336        }
     337
     338        // Pull field objects from the cache.
     339        $fields = array();
     340        foreach ( $field_ids as $field_id ) {
     341            $fields[] = xprofile_get_field( $field_id, null, false );
     342        }
     343
     344        // Store field IDs for meta cache priming.
     345        $object_ids['field'] = $field_ids;
     346
     347        // Maybe fetch field data.
     348        if ( ! empty( $r['fetch_field_data'] ) ) {
     349
     350            // Get field data for user ID.
     351            if ( ! empty( $field_ids ) && ! empty( $r['user_id'] ) ) {
     352                $field_data = BP_XProfile_ProfileData::get_data_for_user( $r['user_id'], $field_ids );
     353            }
     354
     355            // Remove data-less fields, if necessary.
     356            if ( ! empty( $r['hide_empty_fields'] ) && ! empty( $field_ids ) && ! empty( $field_data ) ) {
     357
     358                // Loop through the results and find the fields that have data.
     359                foreach( (array) $field_data as $data ) {
     360
     361                    // Empty fields may contain a serialized empty array.
     362                    $maybe_value = maybe_unserialize( $data->value );
     363
     364                    // Valid field values of 0 or '0' get caught by empty(), so we have an extra check for these. See #BP5731.
     365                    if ( ( ! empty( $maybe_value ) || '0' == $maybe_value ) && false !== $key = array_search( $data->field_id, $field_ids ) ) {
     366
     367                        // Fields that have data get removed from the list.
     368                        unset( $field_ids[ $key ] );
     369                    }
     370                }
     371
     372                // The remaining members of $field_ids are empty. Remove them.
     373                foreach( $fields as $field_key => $field ) {
     374                    if ( in_array( $field->id, $field_ids ) ) {
     375                        unset( $fields[ $field_key ] );
     376                    }
     377                }
     378
     379                // Reset indexes.
     380                $fields = array_values( $fields );
     381            }
     382
     383            // Field data was found.
     384            if ( ! empty( $fields ) && ! empty( $field_data ) && ! is_wp_error( $field_data ) ) {
     385
     386                // Loop through fields.
     387                foreach( (array) $fields as $field_key => $field ) {
     388
     389                    // Loop through the data in each field.
     390                    foreach( (array) $field_data as $data ) {
     391
     392                        // Assign correct data value to the field.
     393                        if ( $field->id == $data->field_id ) {
     394                            $fields[ $field_key ]->data        = new stdClass;
     395                            $fields[ $field_key ]->data->value = $data->value;
     396                            $fields[ $field_key ]->data->id    = $data->id;
     397                        }
     398
     399                        // Store for meta cache priming.
     400                        $object_ids['data'][] = $data->id;
     401                    }
     402                }
     403            }
     404        }
     405
     406        // Prime the meta cache, if necessary.
     407        if ( ! empty( $r['update_meta_cache'] ) ) {
     408            bp_xprofile_update_meta_cache( $object_ids );
     409        }
     410
     411        // Maybe fetch visibility levels.
     412        if ( ! empty( $r['fetch_visibility_level'] ) ) {
     413            $fields = self::fetch_visibility_level( $r['user_id'], $fields );
     414        }
     415
     416        // Merge the field array back in with the group array.
     417        foreach( (array) $groups as $group ) {
     418            // Indexes may have been shifted after previous deletions, so we get a
     419            // fresh one each time through the loop.
     420            $index = array_search( $group, $groups );
     421
     422            foreach( (array) $fields as $field ) {
     423                if ( $group->id === $field->group_id ) {
     424                    $groups[ $index ]->fields[] = $field;
     425                }
     426            }
     427
     428            // When we unset fields above, we may have created empty groups.
     429            // Remove them, if necessary.
     430            if ( empty( $group->fields ) && ! empty( $r['hide_empty_groups'] ) ) {
     431                unset( $groups[ $index ] );
     432            }
     433
     434            // Reset indexes.
     435            $groups = array_values( $groups );
     436        }
     437
     438        return $groups;
     439    }
     440
     441    /**
     442     * Gets group IDs, based on passed parameters.
     443     *
     444     * @since 5.0.0
     445     *
     446     * @param array $args {
     447     *    Array of optional arguments:
     448     *    @type int   $profile_group_id  Limit results to a single profile group. Default false.
     449     *    @type array $exclude_groups    Comma-separated list or array of group IDs to exclude. Default false.
     450     *    @type bool  $hide_empty_groups True to hide groups that don't have any fields. Default: false.
     451     * }
     452     * @return array
     453     */
     454    public static function get_group_ids( $args = array() ) {
     455        global $wpdb;
     456
     457        $r = array_merge(
     458            array(
     459                'profile_group_id'  => false,
     460                'exclude_groups'    => false,
     461                'hide_empty_groups' => false,
     462            ),
     463            $args
     464        );
     465
     466        $bp = buddypress();
     467
    292468        if ( ! empty( $r['profile_group_id'] ) ) {
    293469            $where_sql = $wpdb->prepare( 'WHERE g.id = %d', $r['profile_group_id'] );
     
    299475        }
    300476
    301         $bp = buddypress();
    302 
    303477        // Include or exclude empty groups.
    304478        if ( ! empty( $r['hide_empty_groups'] ) ) {
    305             $group_ids = $wpdb->get_col( "SELECT DISTINCT g.id FROM {$bp->profile->table_name_groups} g INNER JOIN {$bp->profile->table_name_fields} f ON g.id = f.group_id {$where_sql} ORDER BY g.group_order ASC" );
     479            $group_ids_sql = "SELECT DISTINCT g.id FROM {$bp->profile->table_name_groups} g INNER JOIN {$bp->profile->table_name_fields} f ON g.id = f.group_id {$where_sql} ORDER BY g.group_order ASC";
    306480        } else {
    307             $group_ids = $wpdb->get_col( "SELECT DISTINCT g.id FROM {$bp->profile->table_name_groups} g {$where_sql} ORDER BY g.group_order ASC" );
    308         }
    309 
    310         // Get all group data.
    311         $groups = self::get_group_data( $group_ids );
    312 
    313         // Bail if not also getting fields.
    314         if ( empty( $r['fetch_fields'] ) ) {
    315             return $groups;
    316         }
    317 
    318         // Get the group ids from the groups we found.
    319         $group_ids = wp_list_pluck( $groups, 'id' );
    320 
    321         // Store for meta cache priming.
    322         $object_ids['group'] = $group_ids;
    323 
    324         // Bail if no groups found.
     481            $group_ids_sql = "SELECT DISTINCT g.id FROM {$bp->profile->table_name_groups} g {$where_sql} ORDER BY g.group_order ASC";
     482        }
     483
     484        $cached = bp_core_get_incremented_cache( $group_ids_sql, 'bp_xprofile_groups' );
     485        if ( false === $cached ) {
     486            $group_ids = $wpdb->get_col( $group_ids_sql );
     487            bp_core_set_incremented_cache( $group_ids_sql, 'bp_xprofile_groups', $group_ids );
     488        } else {
     489            $group_ids = $cached;
     490        }
     491
     492        return array_map( 'intval', $group_ids );
     493    }
     494
     495    /**
     496     * Gets group field IDs, based on passed parameters.
     497     *
     498     * @since 5.0.0
     499     *
     500     * @param array $group_ids Array of group IDs.
     501     * @param array $args {
     502     *    Array of optional arguments:
     503     *      @type array        $exclude_fields    Comma-separated list or array of field IDs to exclude.
     504     *                                            Default empty.
     505     *      @type int          $user_id           Limit results to fields associated with a given user's
     506     *                                            member type. Default empty.
     507     *      @type array|string $member_type       Limit fields by those restricted to a given member type, or array of
     508     *                                            member types. If `$user_id` is provided, the value of `$member_type`
     509     *                                            is honored.
     510     * }
     511     * @return array
     512     */
     513    public static function get_group_field_ids( $group_ids, $args = array() ) {
     514        global $wpdb;
     515
     516        $r = array_merge(
     517            array(
     518                'exclude_fields' => false,
     519                'user_id' => false,
     520                'member_type' => false,
     521            ),
     522            $args
     523        );
     524
     525        $bp = buddypress();
     526
     527        // Setup IN query from group IDs.
    325528        if ( empty( $group_ids ) ) {
    326             return $groups;
    327         }
    328 
    329         // Setup IN query from group IDs.
    330         $group_ids_in = implode( ',', (array) $group_ids );
     529            $group_ids = array( 0 );
     530        }
     531        $group_ids_in = implode( ',', array_map( 'intval', $group_ids ) );
    331532
    332533        // Support arrays and comma-separated strings.
     
    371572        }
    372573
    373         // Fetch the fields.
    374         $field_ids = $wpdb->get_col( "SELECT id FROM {$bp->profile->table_name_fields} WHERE group_id IN ( {$group_ids_in} ) AND parent_id = 0 {$exclude_fields_sql} {$in_sql} ORDER BY field_order" );
    375 
    376         foreach( $groups as $group ) {
    377             $group->fields = array();
    378         }
    379 
    380         // Bail if no fields.
    381         if ( empty( $field_ids ) ) {
    382             return $groups;
    383         }
    384 
    385         $field_ids = array_map( 'intval', $field_ids );
    386 
    387         // Prime the field cache.
    388         $uncached_field_ids = bp_get_non_cached_ids( $field_ids, 'bp_xprofile_fields' );
    389         if ( ! empty( $uncached_field_ids ) ) {
    390             $_uncached_field_ids = implode( ',', array_map( 'intval', $uncached_field_ids ) );
    391             $uncached_fields = $wpdb->get_results( "SELECT * FROM {$bp->profile->table_name_fields} WHERE id IN ({$_uncached_field_ids})" );
    392             foreach ( $uncached_fields as $uncached_field ) {
    393                 $fid = intval( $uncached_field->id );
    394                 wp_cache_set( $fid, $uncached_field, 'bp_xprofile_fields' );
    395             }
    396         }
    397 
    398         // Pull field objects from the cache.
    399         $fields = array();
    400         foreach ( $field_ids as $field_id ) {
    401             $fields[] = xprofile_get_field( $field_id, null, false );
    402         }
    403 
    404         // Store field IDs for meta cache priming.
    405         $object_ids['field'] = $field_ids;
    406 
    407         // Maybe fetch field data.
    408         if ( ! empty( $r['fetch_field_data'] ) ) {
    409 
    410             // Get field data for user ID.
    411             if ( ! empty( $field_ids ) && ! empty( $r['user_id'] ) ) {
    412                 $field_data = BP_XProfile_ProfileData::get_data_for_user( $r['user_id'], $field_ids );
    413             }
    414 
    415             // Remove data-less fields, if necessary.
    416             if ( ! empty( $r['hide_empty_fields'] ) && ! empty( $field_ids ) && ! empty( $field_data ) ) {
    417 
    418                 // Loop through the results and find the fields that have data.
    419                 foreach( (array) $field_data as $data ) {
    420 
    421                     // Empty fields may contain a serialized empty array.
    422                     $maybe_value = maybe_unserialize( $data->value );
    423 
    424                     // Valid field values of 0 or '0' get caught by empty(), so we have an extra check for these. See #BP5731.
    425                     if ( ( ! empty( $maybe_value ) || '0' == $maybe_value ) && false !== $key = array_search( $data->field_id, $field_ids ) ) {
    426 
    427                         // Fields that have data get removed from the list.
    428                         unset( $field_ids[ $key ] );
    429                     }
    430                 }
    431 
    432                 // The remaining members of $field_ids are empty. Remove them.
    433                 foreach( $fields as $field_key => $field ) {
    434                     if ( in_array( $field->id, $field_ids ) ) {
    435                         unset( $fields[ $field_key ] );
    436                     }
    437                 }
    438 
    439                 // Reset indexes.
    440                 $fields = array_values( $fields );
    441             }
    442 
    443             // Field data was found.
    444             if ( ! empty( $fields ) && ! empty( $field_data ) && ! is_wp_error( $field_data ) ) {
    445 
    446                 // Loop through fields.
    447                 foreach( (array) $fields as $field_key => $field ) {
    448 
    449                     // Loop through the data in each field.
    450                     foreach( (array) $field_data as $data ) {
    451 
    452                         // Assign correct data value to the field.
    453                         if ( $field->id == $data->field_id ) {
    454                             $fields[ $field_key ]->data        = new stdClass;
    455                             $fields[ $field_key ]->data->value = $data->value;
    456                             $fields[ $field_key ]->data->id    = $data->id;
    457                         }
    458 
    459                         // Store for meta cache priming.
    460                         $object_ids['data'][] = $data->id;
    461                     }
    462                 }
    463             }
    464         }
    465 
    466         // Prime the meta cache, if necessary.
    467         if ( ! empty( $r['update_meta_cache'] ) ) {
    468             bp_xprofile_update_meta_cache( $object_ids );
    469         }
    470 
    471         // Maybe fetch visibility levels.
    472         if ( ! empty( $r['fetch_visibility_level'] ) ) {
    473             $fields = self::fetch_visibility_level( $r['user_id'], $fields );
    474         }
    475 
    476         // Merge the field array back in with the group array.
    477         foreach( (array) $groups as $group ) {
    478             // Indexes may have been shifted after previous deletions, so we get a
    479             // fresh one each time through the loop.
    480             $index = array_search( $group, $groups );
    481 
    482             foreach( (array) $fields as $field ) {
    483                 if ( $group->id === $field->group_id ) {
    484                     $groups[ $index ]->fields[] = $field;
    485                 }
    486             }
    487 
    488             // When we unset fields above, we may have created empty groups.
    489             // Remove them, if necessary.
    490             if ( empty( $group->fields ) && ! empty( $r['hide_empty_groups'] ) ) {
    491                 unset( $groups[ $index ] );
    492             }
    493 
    494             // Reset indexes.
    495             $groups = array_values( $groups );
    496         }
    497 
    498         return $groups;
     574        $field_ids_sql = "SELECT id FROM {$bp->profile->table_name_fields} WHERE group_id IN ( {$group_ids_in} ) AND parent_id = 0 {$exclude_fields_sql} {$in_sql} ORDER BY field_order";
     575
     576        $cached = bp_core_get_incremented_cache( $field_ids_sql, 'bp_xprofile_groups' );
     577        if ( false === $cached ) {
     578            $field_ids = $wpdb->get_col( $field_ids_sql );
     579            bp_core_set_incremented_cache( $field_ids_sql, 'bp_xprofile_groups', $field_ids );
     580        } else {
     581            $field_ids = $cached;
     582        }
     583
     584        return array_map( 'intval', $field_ids );
    499585    }
    500586
     
    614700        }
    615701
    616         // Purge profile field group cache.
     702        // Purge profile field group and group query caches.
    617703        wp_cache_delete( 'all', 'bp_xprofile_groups' );
     704        bp_core_reset_incrementor( 'bp_xprofile_groups' );
    618705
    619706        $bp = buddypress();
Note: See TracChangeset for help on using the changeset viewer.