[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

/src/bp-core/classes/ -> class-bp-user-query.php (source)

   1  <?php
   2  /**
   3   * Core component classes.
   4   *
   5   * @package BuddyPress
   6   * @subpackage Core
   7   * @since 1.7.0
   8   */
   9  
  10  // Exit if accessed directly.
  11  defined( 'ABSPATH' ) || exit;
  12  
  13  /**
  14   * BuddyPress User Query class.
  15   *
  16   * Used for querying users in a BuddyPress context, in situations where WP_User_Query won't do the trick:
  17   * Member directories, the Friends component, etc.
  18   *
  19   * @since 1.7.0
  20   * @since 10.0.0 Added $date_query parameter.
  21   *
  22   * @param array $query {
  23   *     Query arguments. All items are optional.
  24   *     @type string            $type                Determines sort order. Select from 'newest', 'active', 'online',
  25   *                                                  'random', 'popular', 'alphabetical'. Default: 'newest'.
  26   *     @type int               $per_page            Number of results to return. Default: 0 (no limit).
  27   *     @type int               $page                Page offset (together with $per_page). Default: 1.
  28   *     @type int               $user_id             ID of a user. If present, and if the friends component is activated,
  29   *                                                  results will be limited to the friends of that user. Default: 0.
  30   *     @type string|bool       $search_terms        Terms to search by. Search happens across xprofile fields. Requires
  31   *                                                  XProfile component. Default: false.
  32   *     @type string            $search_wildcard     When searching with $search_terms, set where wildcards around the
  33   *                                                  term should be positioned. Accepts 'both', 'left', 'right'.
  34   *                                                  Default: 'both'.
  35   *     @type array|string|bool $include             An array or comma-separated list of user IDs to which query should
  36   *                                                  be limited. Default: false.
  37   *     @type array|string|bool $exclude             An array or comma-separated list of user IDs that will be excluded
  38   *                                                  from query results. Default: false.
  39   *     @type array|string|bool $user_ids            An array or comma-separated list of IDs corresponding to the users
  40   *                                                  that should be returned. When this parameter is passed, it will
  41   *                                                  override all others; BP User objects will be constructed using these
  42   *                                                  IDs only. Default: false.
  43   *     @type array|string      $member_type         Array or comma-separated list of member types to limit results to.
  44   *     @type array|string      $member_type__in     Array or comma-separated list of member types to limit results to.
  45   *     @type array|string      $member_type__not_in Array or comma-separated list of member types that will be
  46   *                                                         excluded from results.
  47   *     @type string|bool       $meta_key            Limit results to users that have usermeta associated with this meta_key.
  48   *                                                  Usually used with $meta_value. Default: false.
  49   *     @type string|bool       $meta_value          When used with $meta_key, limits results to users whose usermeta value
  50   *                                                  associated with $meta_key matches $meta_value. Default: false.
  51   *     @type array             $xprofile_query      Filter results by xprofile data. Requires the xprofile component.
  52   *                                                  See {@see BP_XProfile_Query} for details.
  53   *     @type array             $date_query          Filter results by member last activity date. See first parameter of
  54   *                                                  {@link WP_Date_Query::__construct()} for syntax. Only applicable if
  55   *                                                  $type is either 'active', 'random', 'newest', or 'online'.
  56   *     @type bool              $populate_extras     True if you want to fetch extra metadata
  57   *                                                  about returned users, such as total group and friend counts.
  58   *     @type string            $count_total         Determines how BP_User_Query will do a count of total users matching
  59   *                                                  the other filter criteria. Default value is 'count_query', which
  60   *                                                  does a separate SELECT COUNT query to determine the total.
  61   *                                                  'sql_count_found_rows' uses SQL_COUNT_FOUND_ROWS and
  62   *                                                  SELECT FOUND_ROWS(). Pass an empty string to skip the total user
  63   *                                                  count query.
  64   * }
  65   */
  66  class BP_User_Query {
  67  
  68      /** Variables *************************************************************/
  69  
  70      /**
  71       * Unaltered params as passed to the constructor.
  72       *
  73       * @since 1.8.0
  74       * @var array
  75       */
  76      public $query_vars_raw = array();
  77  
  78      /**
  79       * Array of variables to query with.
  80       *
  81       * @since 1.7.0
  82       * @var array
  83       */
  84      public $query_vars = array();
  85  
  86      /**
  87       * List of found users and their respective data.
  88       *
  89       * @since 1.7.0
  90       * @var array
  91       */
  92      public $results = array();
  93  
  94      /**
  95       * Total number of found users for the current query.
  96       *
  97       * @since 1.7.0
  98       * @var int
  99       */
 100      public $total_users = 0;
 101  
 102      /**
 103       * List of found user IDs.
 104       *
 105       * @since 1.7.0
 106       * @var array
 107       */
 108      public $user_ids = array();
 109  
 110      /**
 111       * SQL clauses for the user ID query.
 112       *
 113       * @since 1.7.0
 114       * @var array
 115       */
 116      public $uid_clauses = array();
 117  
 118      /**
 119       * SQL table where the user ID is being fetched from.
 120       *
 121       * @since 2.2.0
 122       * @var string
 123       */
 124      public $uid_table = '';
 125  
 126      /**
 127       * SQL database column name to order by.
 128       *
 129       * @since 1.7.0
 130       * @var string
 131       */
 132      public $uid_name = '';
 133  
 134      /**
 135       * Standard response when the query should not return any rows.
 136       *
 137       * @since 1.7.0
 138       * @var string
 139       */
 140      protected $no_results = array( 'join' => '', 'where' => '0 = 1' );
 141  
 142  
 143      /** Methods ***************************************************************/
 144  
 145      /**
 146       * Constructor.
 147       *
 148       * @since 1.7.0
 149       *
 150       * @param string|array|null $query See {@link BP_User_Query}.
 151       */
 152  	public function __construct( $query = null ) {
 153  
 154          // Store the raw query vars for later access.
 155          $this->query_vars_raw = $query;
 156  
 157          // Allow extending classes to register action/filter hooks.
 158          $this->setup_hooks();
 159  
 160          if ( ! empty( $this->query_vars_raw ) ) {
 161              $this->query_vars = bp_parse_args(
 162                  $this->query_vars_raw,
 163                  array(
 164                      'type'                => 'newest',
 165                      'per_page'            => 0,
 166                      'page'                => 1,
 167                      'user_id'             => 0,
 168                      'search_terms'        => false,
 169                      'search_wildcard'     => 'both',
 170                      'include'             => false,
 171                      'exclude'             => false,
 172                      'user_ids'            => false,
 173                      'member_type'         => '',
 174                      'member_type__in'     => '',
 175                      'member_type__not_in' => '',
 176                      'meta_key'            => false,
 177                      'meta_value'          => false,
 178                      'xprofile_query'      => false,
 179                      'date_query'          => false,
 180                      'populate_extras'     => true,
 181                      'count_total'         => 'count_query',
 182                  )
 183              );
 184  
 185              /**
 186               * Fires before the construction of the BP_User_Query query.
 187               *
 188               * @since 1.7.0
 189               *
 190               * @param BP_User_Query $this Current instance of the BP_User_Query. Passed by reference.
 191               */
 192              do_action_ref_array( 'bp_pre_user_query_construct', array( &$this ) );
 193  
 194              // Get user ids.
 195              // If the user_ids param is present, we skip the query.
 196              if ( false !== $this->query_vars['user_ids'] ) {
 197                  $this->user_ids = wp_parse_id_list( $this->query_vars['user_ids'] );
 198              } else {
 199                  $this->prepare_user_ids_query();
 200                  $this->do_user_ids_query();
 201              }
 202          }
 203  
 204          // Bail if no user IDs were found.
 205          if ( empty( $this->user_ids ) ) {
 206              return;
 207          }
 208  
 209          // Fetch additional data. First, using WP_User_Query.
 210          $this->do_wp_user_query();
 211  
 212          // Get BuddyPress specific user data.
 213          $this->populate_extras();
 214      }
 215  
 216      /**
 217       * Allow extending classes to set up action/filter hooks.
 218       *
 219       * When extending BP_User_Query, you may need to use some of its
 220       * internal hooks to modify the output. It's not convenient to call
 221       * add_action() or add_filter() in your class constructor, because
 222       * BP_User_Query::__construct() contains a fair amount of logic that
 223       * you may not want to override in your class. Define this method in
 224       * your own class if you need a place where your extending class can
 225       * add its hooks early in the query-building process. See
 226       * {@link BP_Group_Member_Query::setup_hooks()} for an example.
 227       *
 228       * @since 1.8.0
 229       */
 230  	public function setup_hooks() {}
 231  
 232      /**
 233       * Prepare the query for user_ids.
 234       *
 235       * @since 1.7.0
 236       */
 237  	public function prepare_user_ids_query() {
 238          global $wpdb;
 239  
 240          $bp = buddypress();
 241  
 242          // Default query variables used here.
 243          $type         = '';
 244          $per_page     = 0;
 245          $page         = 1;
 246          $user_id      = 0;
 247          $include      = false;
 248          $search_terms = false;
 249          $exclude      = false;
 250          $meta_key     = false;
 251          $meta_value   = false;
 252  
 253          extract( $this->query_vars );
 254  
 255          // Setup the main SQL query container.
 256          $sql = array(
 257              'select'  => '',
 258              'where'   => array('1=1'),
 259              'orderby' => '',
 260              'order'   => '',
 261              'limit'   => ''
 262          );
 263  
 264          /* TYPE **************************************************************/
 265  
 266          // Determines the sort order, which means it also determines where the
 267          // user IDs are drawn from (the SELECT and WHERE statements).
 268          switch ( $type ) {
 269  
 270              // 'online' query happens against the last_activity usermeta key
 271              // Filter 'bp_user_query_online_interval' to modify the
 272              // number of minutes used as an interval.
 273              case 'online' :
 274                  $this->uid_name = 'user_id';
 275                  $this->uid_table = $bp->members->table_name_last_activity;
 276                  $sql['select']  = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u";
 277                  $sql['where'][] = $wpdb->prepare( "u.component = %s AND u.type = 'last_activity'", buddypress()->members->id );
 278  
 279                  /**
 280                   * Filters the threshold for activity timestamp minutes since to indicate online status.
 281                   *
 282                   * @since 1.8.0
 283                   *
 284                   * @param int $value Amount of minutes for threshold. Default 15.
 285                   */
 286                  $sql['where'][] = $wpdb->prepare( "u.date_recorded >= DATE_SUB( UTC_TIMESTAMP(), INTERVAL %d MINUTE )", apply_filters( 'bp_user_query_online_interval', 15 ) );
 287                  $sql['orderby'] = "ORDER BY u.date_recorded";
 288                  $sql['order']   = "DESC";
 289  
 290                  // Date query.
 291                  $date_query = BP_Date_Query::get_where_sql( $date_query, 'u.date_recorded' );
 292                  if ( ! empty( $date_query ) ) {
 293                      $sql['where']['date_query'] = $date_query;
 294                  }
 295  
 296                  break;
 297  
 298              // 'active', 'newest', and 'random' queries
 299              // all happen against the last_activity usermeta key.
 300              case 'active' :
 301              case 'newest' :
 302              case 'random' :
 303                  $this->uid_name = 'user_id';
 304                  $this->uid_table = $bp->members->table_name_last_activity;
 305                  $sql['select']  = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u";
 306                  $sql['where'][] = $wpdb->prepare( "u.component = %s AND u.type = 'last_activity'", buddypress()->members->id );
 307  
 308                  if ( 'newest' == $type ) {
 309                      $sql['orderby'] = "ORDER BY u.user_id";
 310                      $sql['order'] = "DESC";
 311                  } elseif ( 'random' == $type ) {
 312                      $sql['orderby'] = "ORDER BY rand()";
 313                  } else {
 314                      $sql['orderby'] = "ORDER BY u.date_recorded";
 315                      $sql['order'] = "DESC";
 316                  }
 317  
 318                  // Date query.
 319                  $date_query = BP_Date_Query::get_where_sql( $date_query, 'u.date_recorded' );
 320                  if ( ! empty( $date_query ) ) {
 321                      $sql['where']['date_query'] = $date_query;
 322                  }
 323  
 324                  break;
 325  
 326              // 'popular' sorts by the 'total_friend_count' usermeta.
 327              case 'popular' :
 328                  $this->uid_name = 'user_id';
 329                  $this->uid_table = $wpdb->usermeta;
 330                  $sql['select']  = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u";
 331                  $sql['where'][] = $wpdb->prepare( "u.meta_key = %s", bp_get_user_meta_key( 'total_friend_count' ) );
 332                  $sql['orderby'] = "ORDER BY CONVERT(u.meta_value, SIGNED)";
 333                  $sql['order']   = "DESC";
 334  
 335                  break;
 336  
 337              // 'alphabetical' sorts depend on the xprofile setup.
 338              case 'alphabetical' :
 339  
 340                  // We prefer to do alphabetical sorts against the display_name field
 341                  // of wp_users, because the table is smaller and better indexed. We
 342                  // can do so if xprofile sync is enabled, or if xprofile is inactive.
 343                  //
 344                  // @todo remove need for bp_is_active() check.
 345                  if ( ! bp_disable_profile_sync() || ! bp_is_active( 'xprofile' ) ) {
 346                      $this->uid_name = 'ID';
 347                      $this->uid_table = $wpdb->users;
 348                      $sql['select']  = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u";
 349                      $sql['orderby'] = "ORDER BY u.display_name";
 350                      $sql['order']   = "ASC";
 351  
 352                  // When profile sync is disabled, alphabetical sorts must happen against
 353                  // the xprofile table.
 354                  } else {
 355                      $this->uid_name = 'user_id';
 356                      $this->uid_table = $bp->profile->table_name_data;
 357                      $sql['select']  = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u";
 358                      $sql['where'][] = $wpdb->prepare( "u.field_id = %d", bp_xprofile_fullname_field_id() );
 359                      $sql['orderby'] = "ORDER BY u.value";
 360                      $sql['order']   = "ASC";
 361                  }
 362  
 363                  // Alphabetical queries ignore last_activity, while BP uses last_activity
 364                  // to infer spam/deleted/non-activated users. To ensure that these users
 365                  // are filtered out, we add an appropriate sub-query.
 366                  $sql['where'][] = "u.{$this->uid_name} IN ( SELECT ID FROM {$wpdb->users} WHERE " . bp_core_get_status_sql( '' ) . " )";
 367  
 368                  break;
 369  
 370              // Any other 'type' falls through.
 371              default :
 372                  $this->uid_name = 'ID';
 373                  $this->uid_table = $wpdb->users;
 374                  $sql['select']  = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u";
 375  
 376                  // In this case, we assume that a plugin is
 377                  // handling order, so we leave those clauses
 378                  // blank.
 379                  break;
 380          }
 381  
 382          /* WHERE *************************************************************/
 383  
 384          // 'include' - User ids to include in the results.
 385          $include     = false !== $include ? wp_parse_id_list( $include ) : array();
 386          $include_ids = $this->get_include_ids( $include );
 387  
 388          // An array containing nothing but 0 should always fail.
 389          if ( 1 === count( $include_ids ) && 0 == reset( $include_ids ) ) {
 390              $sql['where'][] = $this->no_results['where'];
 391          } elseif ( ! empty( $include_ids ) ) {
 392              $include_ids    = implode( ',', wp_parse_id_list( $include_ids ) );
 393              $sql['where'][] = "u.{$this->uid_name} IN ({$include_ids})";
 394          }
 395  
 396          // 'exclude' - User ids to exclude from the results.
 397          if ( ! empty( $exclude ) ) {
 398              $exclude_ids    = implode( ',', wp_parse_id_list( $exclude ) );
 399              $sql['where'][] = "u.{$this->uid_name} NOT IN ({$exclude_ids})";
 400          }
 401  
 402          // 'user_id' - When a user id is passed, limit to the friends of the user
 403          // @todo remove need for bp_is_active() check.
 404          if ( ! empty( $user_id ) && bp_is_active( 'friends' ) ) {
 405              $friend_ids = friends_get_friend_user_ids( $user_id );
 406              $friend_ids = implode( ',', wp_parse_id_list( $friend_ids ) );
 407  
 408              if ( ! empty( $friend_ids ) ) {
 409                  $sql['where'][] = "u.{$this->uid_name} IN ({$friend_ids})";
 410  
 411              // If the user has no friends, the query should always
 412              // return no users.
 413              } else {
 414                  $sql['where'][] = $this->no_results['where'];
 415              }
 416          }
 417  
 418          /* Search Terms ******************************************************/
 419  
 420          // 'search_terms' searches user_login and user_nicename
 421          // xprofile field matches happen in bp_xprofile_bp_user_query_search().
 422          if ( false !== $search_terms ) {
 423              $search_terms = bp_esc_like( wp_kses_normalize_entities( $search_terms ) );
 424  
 425              if ( $search_wildcard === 'left' ) {
 426                  $search_terms_nospace = '%' . $search_terms;
 427                  $search_terms_space   = '%' . $search_terms . ' %';
 428              } elseif ( $search_wildcard === 'right' ) {
 429                  $search_terms_nospace =        $search_terms . '%';
 430                  $search_terms_space   = '% ' . $search_terms . '%';
 431              } else {
 432                  $search_terms_nospace = '%' . $search_terms . '%';
 433                  $search_terms_space   = '%' . $search_terms . '%';
 434              }
 435  
 436              $matched_user_ids = $wpdb->get_col( $wpdb->prepare(
 437                  "SELECT ID FROM {$wpdb->users} WHERE ( user_login LIKE %s OR user_login LIKE %s OR user_nicename LIKE %s OR user_nicename LIKE %s )",
 438                  $search_terms_nospace,
 439                  $search_terms_space,
 440                  $search_terms_nospace,
 441                  $search_terms_space
 442              ) );
 443  
 444              $match_in_clause = empty( $matched_user_ids) ? 'NULL' : implode( ',', $matched_user_ids );
 445              $sql['where']['search'] = "u.{$this->uid_name} IN ({$match_in_clause})";
 446          }
 447  
 448          // Only use $member_type__in if $member_type is not set.
 449          if ( empty( $member_type ) && ! empty( $member_type__in ) ) {
 450              $member_type = $member_type__in;
 451          }
 452  
 453          // Member types to exclude. Note that this takes precedence over inclusions.
 454          if ( ! empty( $member_type__not_in ) ) {
 455              $member_type_clause = $this->get_sql_clause_for_member_types( $member_type__not_in, 'NOT IN' );
 456  
 457          // Member types to include.
 458          } elseif ( ! empty( $member_type ) ) {
 459              $member_type_clause = $this->get_sql_clause_for_member_types( $member_type, 'IN' );
 460          }
 461  
 462          if ( ! empty( $member_type_clause ) ) {
 463              $sql['where']['member_type'] = $member_type_clause;
 464          }
 465  
 466          // 'meta_key', 'meta_value' allow usermeta search
 467          // To avoid global joins, do a separate query.
 468          if ( false !== $meta_key ) {
 469              $meta_sql = $wpdb->prepare( "SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = %s", $meta_key );
 470  
 471              if ( false !== $meta_value ) {
 472                  $meta_sql .= $wpdb->prepare( " AND meta_value = %s", $meta_value );
 473              }
 474  
 475              $found_user_ids = $wpdb->get_col( $meta_sql );
 476  
 477              if ( ! empty( $found_user_ids ) ) {
 478                  $sql['where'][] = "u.{$this->uid_name} IN (" . implode( ',', wp_parse_id_list( $found_user_ids ) ) . ")";
 479              } else {
 480                  $sql['where'][] = '1 = 0';
 481              }
 482          }
 483  
 484          // 'per_page', 'page' - handles LIMIT.
 485          if ( !empty( $per_page ) && !empty( $page ) ) {
 486              $sql['limit'] = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $per_page ), intval( $per_page ) );
 487          } else {
 488              $sql['limit'] = '';
 489          }
 490  
 491          /**
 492           * Filters the clauses for the user query.
 493           *
 494           * @since 2.0.0
 495           *
 496           * @param array         $sql  Array of SQL clauses to be used in the query.
 497           * @param BP_User_Query $this Current BP_User_Query instance.
 498           */
 499          $sql = apply_filters_ref_array( 'bp_user_query_uid_clauses', array( $sql, &$this ) );
 500  
 501          // Assemble the query chunks.
 502          $this->uid_clauses['select']  = $sql['select'];
 503          $this->uid_clauses['where']   = ! empty( $sql['where'] ) ? 'WHERE ' . implode( ' AND ', $sql['where'] ) : '';
 504          $this->uid_clauses['orderby'] = $sql['orderby'];
 505          $this->uid_clauses['order']   = $sql['order'];
 506          $this->uid_clauses['limit']   = $sql['limit'];
 507  
 508          /**
 509           * Fires before the BP_User_Query query is made.
 510           *
 511           * @since 1.7.0
 512           *
 513           * @param BP_User_Query $this Current BP_User_Query instance. Passed by reference.
 514           */
 515          do_action_ref_array( 'bp_pre_user_query', array( &$this ) );
 516      }
 517  
 518      /**
 519       * Query for IDs of users that match the query parameters.
 520       *
 521       * Perform a database query to specifically get only user IDs, using
 522       * existing query variables set previously in the constructor.
 523       *
 524       * Also used to quickly perform user total counts.
 525       *
 526       * @since 1.7.0
 527       */
 528  	public function do_user_ids_query() {
 529          global $wpdb;
 530  
 531          // If counting using SQL_CALC_FOUND_ROWS, set it up here.
 532          if ( 'sql_calc_found_rows' == $this->query_vars['count_total'] ) {
 533              $this->uid_clauses['select'] = str_replace( 'SELECT', 'SELECT SQL_CALC_FOUND_ROWS', $this->uid_clauses['select'] );
 534          }
 535  
 536          // Get the specific user ids.
 537          $this->user_ids = $wpdb->get_col( "{$this->uid_clauses['select']} {$this->uid_clauses['where']} {$this->uid_clauses['orderby']} {$this->uid_clauses['order']} {$this->uid_clauses['limit']}" );
 538  
 539          // Get the total user count.
 540          if ( 'sql_calc_found_rows' == $this->query_vars['count_total'] ) {
 541  
 542              /**
 543               * Filters the found user SQL statements before query.
 544               *
 545               * If "sql_calc_found_rows" is the provided count_total query var
 546               * then the value will be "SELECT FOUND_ROWS()". Otherwise it will
 547               * use a "SELECT COUNT()" query statement.
 548               *
 549               * @since 1.7.0
 550               *
 551               * @param string        $value SQL statement to select FOUND_ROWS().
 552               * @param BP_User_Query $this  Current BP_User_Query instance.
 553               */
 554              $this->total_users = $wpdb->get_var( apply_filters( 'bp_found_user_query', "SELECT FOUND_ROWS()", $this ) );
 555          } elseif ( 'count_query' == $this->query_vars['count_total'] ) {
 556              $count_select      = preg_replace( '/^SELECT.*?FROM (\S+) u/', "SELECT COUNT(u.{$this->uid_name}) FROM $1 u", $this->uid_clauses['select'] );
 557  
 558              /** This filter is documented in bp-core/classes/class-bp-user-query.php */
 559              $this->total_users = $wpdb->get_var( apply_filters( 'bp_found_user_query', "{$count_select} {$this->uid_clauses['where']}", $this ) );
 560          }
 561      }
 562  
 563      /**
 564       * Use WP_User_Query() to pull data for the user IDs retrieved in the main query.
 565       *
 566       * @since 1.7.0
 567       */
 568  	public function do_wp_user_query() {
 569          $fields = array( 'ID', 'user_login', 'user_pass', 'user_nicename', 'user_email', 'user_url', 'user_registered', 'user_activation_key', 'user_status', 'display_name' );
 570  
 571          if ( is_multisite() ) {
 572              $fields[] = 'spam';
 573              $fields[] = 'deleted';
 574          }
 575  
 576          /**
 577           * Filters the WP User Query arguments before passing into the class.
 578           *
 579           * @since 1.7.0
 580           *
 581           * @param array         $value Array of arguments for the user query.
 582           * @param BP_User_Query $this  Current BP_User_Query instance.
 583           */
 584          $wp_user_query = new WP_User_Query( apply_filters( 'bp_wp_user_query_args', array(
 585  
 586              // Relevant.
 587              'fields'      => $fields,
 588              'include'     => $this->user_ids,
 589  
 590              // Overrides
 591              'blog_id'     => 0,    // BP does not require blog roles.
 592              'count_total' => false // We already have a count.
 593  
 594          ), $this ) );
 595  
 596          /*
 597           * We calculate total_users using a standalone query, except
 598           * when a list of specific user_ids is passed to the constructor.
 599           * This clause covers the latter situation, and ensures that
 600           * pagination works when querying by $user_ids.
 601           */
 602          if ( empty( $this->total_users ) ) {
 603              $this->total_users = count( $wp_user_query->results );
 604          }
 605  
 606          // Reindex for easier matching.
 607          $r = array();
 608          foreach ( $wp_user_query->results as $u ) {
 609              $r[ $u->ID ] = $u;
 610          }
 611  
 612          // Match up to the user ids from the main query.
 613          foreach ( $this->user_ids as $key => $uid ) {
 614              if ( isset( $r[ $uid ] ) ) {
 615                  $r[ $uid ]->ID = (int) $uid;
 616                  $r[ $uid ]->user_status = (int) $r[ $uid ]->user_status;
 617  
 618                  $this->results[ $uid ] = $r[ $uid ];
 619  
 620                  // The BP template functions expect an 'id'
 621                  // (as opposed to 'ID') property.
 622                  $this->results[ $uid ]->id = (int) $uid;
 623  
 624              // Remove user ID from original user_ids property.
 625              } else {
 626                  unset( $this->user_ids[ $key ] );
 627              }
 628          }
 629      }
 630  
 631      /**
 632       * Fetch the IDs of users to put in the IN clause of the main query.
 633       *
 634       * By default, returns the value passed to it
 635       * ($this->query_vars['include']). Having this abstracted into a
 636       * standalone method means that extending classes can override the
 637       * logic, parsing together their own user_id limits with the 'include'
 638       * ids passed to the class constructor. See {@link BP_Group_Member_Query}
 639       * for an example.
 640       *
 641       * @since 1.8.0
 642       *
 643       * @param array $include Sanitized array of user IDs, as passed to the 'include'
 644       *                       parameter of the class constructor.
 645       * @return array The list of users to which the main query should be
 646       *               limited.
 647       */
 648  	public function get_include_ids( $include = array() ) {
 649          return $include;
 650      }
 651  
 652      /**
 653       * Perform a database query to populate any extra metadata we might need.
 654       *
 655       * Different components will hook into the 'bp_user_query_populate_extras'
 656       * action to loop in the things they want.
 657       *
 658       * @since 1.7.0
 659       *
 660       * @global WPDB $wpdb Global WordPress database access object.
 661       */
 662  	public function populate_extras() {
 663          global $wpdb;
 664  
 665          // Bail if no users.
 666          if ( empty( $this->user_ids ) || empty( $this->results ) ) {
 667              return;
 668          }
 669  
 670          // Bail if the populate_extras flag is set to false
 671          // In the case of the 'popular' sort type, we force
 672          // populate_extras to true, because we need the friend counts.
 673          if ( 'popular' == $this->query_vars['type'] ) {
 674              $this->query_vars['populate_extras'] = 1;
 675          }
 676  
 677          if ( ! (bool) $this->query_vars['populate_extras'] ) {
 678              return;
 679          }
 680  
 681          // Turn user ID's into a query-usable, comma separated value.
 682          $user_ids_sql = implode( ',', wp_parse_id_list( $this->user_ids ) );
 683  
 684          /**
 685           * Allows users to independently populate custom extras.
 686           *
 687           * Note that anything you add here should query using $user_ids_sql, to
 688           * avoid running multiple queries per user in the loop.
 689           *
 690           * Two BuddyPress components currently do this:
 691           * - XProfile: To override display names.
 692           * - Friends:  To set whether or not a user is the current users friend.
 693           *
 694           * @see bp_xprofile_filter_user_query_populate_extras()
 695           * @see bp_friends_filter_user_query_populate_extras()
 696           *
 697           * @since 1.7.0
 698           *
 699           * @param BP_User_Query $this         Current BP_User_Query instance.
 700           * @param string        $user_ids_sql Comma-separated string of user IDs.
 701           */
 702          do_action_ref_array( 'bp_user_query_populate_extras', array( $this, $user_ids_sql ) );
 703  
 704          // Fetch last_active data from the activity table.
 705          $last_activities = BP_Core_User::get_last_activity( $this->user_ids );
 706  
 707          // Set a last_activity value for each user, even if it's empty.
 708          foreach ( $this->results as $user_id => $user ) {
 709              $user_last_activity = isset( $last_activities[ $user_id ]['date_recorded'] ) ? $last_activities[ $user_id ]['date_recorded'] : '';
 710              $this->results[ $user_id ]->last_activity = $user_last_activity;
 711          }
 712  
 713          // Fetch usermeta data
 714          // We want the three following pieces of info from usermeta:
 715          // - friend count
 716          // - latest update.
 717          $total_friend_count_key = bp_get_user_meta_key( 'total_friend_count' );
 718          $bp_latest_update_key   = bp_get_user_meta_key( 'bp_latest_update'   );
 719  
 720          // Total_friend_count must be set for each user, even if its
 721          // value is 0.
 722          foreach ( $this->results as $uindex => $user ) {
 723              $this->results[$uindex]->total_friend_count = 0;
 724          }
 725  
 726          // Create, prepare, and run the separate usermeta query.
 727          $user_metas = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, meta_key, meta_value FROM {$wpdb->usermeta} WHERE meta_key IN (%s,%s) AND user_id IN ({$user_ids_sql})", $total_friend_count_key, $bp_latest_update_key ) );
 728  
 729          // The $members_template global expects the index key to be different
 730          // from the meta_key in some cases, so we rejig things here.
 731          foreach ( $user_metas as $user_meta ) {
 732              switch ( $user_meta->meta_key ) {
 733                  case $total_friend_count_key :
 734                      $key = 'total_friend_count';
 735                      break;
 736  
 737                  case $bp_latest_update_key :
 738                      $key = 'latest_update';
 739                      break;
 740              }
 741  
 742              if ( isset( $this->results[ $user_meta->user_id ] ) ) {
 743                  $this->results[ $user_meta->user_id ]->{$key} = $user_meta->meta_value;
 744              }
 745          }
 746  
 747          // When meta_key or meta_value have been passed to the query,
 748          // fetch the resulting values for use in the template functions.
 749          if ( ! empty( $this->query_vars['meta_key'] ) ) {
 750              $meta_sql = array(
 751                  'select' => "SELECT user_id, meta_key, meta_value",
 752                  'from'   => "FROM $wpdb->usermeta",
 753                  'where'  => $wpdb->prepare( "WHERE meta_key = %s", $this->query_vars['meta_key'] )
 754              );
 755  
 756              if ( false !== $this->query_vars['meta_value'] ) {
 757                  $meta_sql['where'] .= $wpdb->prepare( " AND meta_value = %s", $this->query_vars['meta_value'] );
 758              }
 759  
 760              $metas = $wpdb->get_results( "{$meta_sql['select']} {$meta_sql['from']} {$meta_sql['where']}" );
 761  
 762              if ( ! empty( $metas ) ) {
 763                  foreach ( $metas as $meta ) {
 764                      if ( isset( $this->results[ $meta->user_id ] ) ) {
 765                          $this->results[ $meta->user_id ]->meta_key = $meta->meta_key;
 766  
 767                          if ( ! empty( $meta->meta_value ) ) {
 768                              $this->results[ $meta->user_id ]->meta_value = $meta->meta_value;
 769                          }
 770                      }
 771                  }
 772              }
 773          }
 774      }
 775  
 776      /**
 777       * Get a SQL clause representing member_type include/exclusion.
 778       *
 779       * @since 2.4.0
 780       *
 781       * @param string|array $member_types Array or comma-separated list of member types.
 782       * @param string       $operator     'IN' or 'NOT IN'.
 783       * @return string
 784       */
 785  	protected function get_sql_clause_for_member_types( $member_types, $operator ) {
 786          global $wpdb;
 787  
 788          // Sanitize.
 789          if ( 'NOT IN' !== $operator ) {
 790              $operator = 'IN';
 791          }
 792  
 793          // Parse and sanitize types.
 794          if ( ! is_array( $member_types ) ) {
 795              $member_types = preg_split( '/[,\s+]/', $member_types );
 796          }
 797  
 798          $types = array();
 799          foreach ( $member_types as $mt ) {
 800              if ( bp_get_member_type_object( $mt ) ) {
 801                  $types[] = $mt;
 802              }
 803          }
 804  
 805          $tax_query = new WP_Tax_Query( array(
 806              array(
 807                  'taxonomy' => bp_get_member_type_tax_name(),
 808                  'field'    => 'name',
 809                  'operator' => $operator,
 810                  'terms'    => $types,
 811              ),
 812          ) );
 813  
 814          // Switch to the root blog, where member type taxonomies live.
 815          $site_id  = bp_get_taxonomy_term_site_id( bp_get_member_type_tax_name() );
 816          $switched = false;
 817          if ( $site_id !== get_current_blog_id() ) {
 818              switch_to_blog( $site_id );
 819              $switched = true;
 820          }
 821  
 822          $sql_clauses = $tax_query->get_sql( 'u', $this->uid_name );
 823  
 824          $clause = '';
 825  
 826          // The no_results clauses are the same between IN and NOT IN.
 827          if ( false !== strpos( $sql_clauses['where'], '0 = 1' ) ) {
 828              $clause = $this->no_results['where'];
 829  
 830          // The tax_query clause generated for NOT IN can be used almost as-is. We just trim the leading 'AND'.
 831          } elseif ( 'NOT IN' === $operator ) {
 832              $clause = preg_replace( '/^\s*AND\s*/', '', $sql_clauses['where'] );
 833  
 834          // IN clauses must be converted to a subquery.
 835          } elseif ( preg_match( '/' . $wpdb->term_relationships . '\.term_taxonomy_id IN \([0-9, ]+\)/', $sql_clauses['where'], $matches ) ) {
 836              $clause = "u.{$this->uid_name} IN ( SELECT object_id FROM $wpdb->term_relationships WHERE {$matches[0]} )";
 837          }
 838  
 839          if ( $switched ) {
 840              restore_current_blog();
 841          }
 842  
 843          return $clause;
 844      }
 845  }


Generated: Wed May 25 01:00:54 2022 Cross-referenced by PHPXref 0.7.1