From b2bd45e7a4cd50c7b46b797c5c3ecda012eeaec0 Mon Sep 17 00:00:00 2001 From: jaswsinc Date: Thu, 22 Jan 2015 23:28:33 -0900 Subject: [PATCH] Improving `[s2Member-List /]` search functionality. Closes websharks/s2member#155, closes websharks/s2member#394 --- .../includes/classes/member-list.inc.php | 135 +++++++++--------- .../classes/sc-member-list-in.inc.php | 8 +- 2 files changed, 68 insertions(+), 75 deletions(-) diff --git a/s2member-pro/includes/classes/member-list.inc.php b/s2member-pro/includes/classes/member-list.inc.php index 073c985b..af884e80 100644 --- a/s2member-pro/includes/classes/member-list.inc.php +++ b/s2member-pro/includes/classes/member-list.inc.php @@ -86,6 +86,9 @@ public static function query($args = array()) } unset($_key, $_value); // Housekeeping. + if(strlen($args['search']) >= 2 && strpos($args['search'], '*') === FALSE && strpos($args['search'], '"') === FALSE) + $args['search'] = '*'.$args['search'].'*'; + if(!$args['search_columns']) // Use defaults? $args['search_columns'] = $default_args['search_columns']; @@ -96,25 +99,76 @@ public static function query($args = array()) if($args['number'] < 1) $args['number'] = 1; // Make sure this is always >= 1. $args['offset'] = ($page - 1) * $args['number']; // Calculate dynamically. - // Run search, returning only User IDs in the result. - $search_query = new WP_User_Query($args); // See: - $user_ids = $search_query->get_results(); - - // Also search s2Member Custom Fields, if necessary. Returns array of User IDs. - $search_query_s2custom = self::search_s2_custom_fields($args, $original_args); + $user_ids_query = new WP_User_Query($args); + $user_ids = $user_ids_query->get_results(); + $user_ids_from_s2_custom_fields = self::search_s2_custom_fields($args, $original_args); - if(!empty($search_query_s2custom)) + if(!empty($user_ids_from_s2_custom_fields)) { - $user_ids = array_merge($user_ids, $search_query_s2custom); + $user_ids = array_merge($user_ids, $user_ids_from_s2_custom_fields); $user_ids = array_unique($user_ids); } - $args['fields'] = 'all_with_meta'; - // See: + if(!$user_ids) // The search is already known to be empty? + return array('query' => $user_ids_query, 'pagination' => self::paginate($page, 0, $args['number'])); + + $args['include'] = $user_ids; + $args['fields'] = 'all_with_meta'; + unset($args['search'], $args['search_columns']); $query = new WP_User_Query(array('fields' => 'all_with_meta', 'include' => $user_ids)); return array('query' => $query, 'pagination' => self::paginate($page, (integer)$query->get_total(), $args['number'])); } + /** + * Searches s2Member Custom Fields; an extension to the self::query() method. + * + * @param array $args Arguments passed to self::query() after self::query() merged the defaults. + * @param array $original_args Original arguments passed by the shortcode before self::query() merged the defaults. + * + * @return array An array of User IDs. + */ + protected static function search_s2_custom_fields($args, $original_args) + { + global $wpdb; // Global database object reference. + /** @var \wpdb $wpdb For IDEs that need a reference. */ + + if(empty($args['search'])) + return array(); // Nothing to do. + + if(!empty($original_args['search_columns'])) + if(!preg_grep('/(?:^|\W)s2member_custom_field_\w+/', $args['search_columns'])) + return array(); // Nothing to do. + + $matching_custom_fields_regex_frag = ''; + $include_user_ids = array(); + + if(empty($original_args['search_columns'])) + $matching_custom_fields_regex_frag = '.*'; + + else if(($custom_field_columns = preg_grep('/(?:^|\W)s2member_custom_field_\w+/', $args['search_columns']))) + { + foreach($custom_field_columns as $_column) + if(preg_match('/(?:^|\W)s2member_custom_field_(?P\w+)/', $_column, $_m)) + $matching_custom_fields_regex_frag .= preg_quote(trim($_m['field_id'])).'|'; + $matching_custom_fields_regex_frag = rtrim($matching_custom_fields_regex_frag, '|'); + unset($_column, $_m); // Housekeeping. + } + if($matching_custom_fields_regex_frag) + { + $search_regex_frag = preg_quote($args['search']); + $search_regex_frag = str_replace('"', '', $search_regex_frag); + $search_regex_frag = str_replace('\\*', '[^"]*', $search_regex_frag); + $regex = '(^|\{)s\:[0-9]+\:"('.$matching_custom_fields_regex_frag.')";s\:[0-9]+\:"'.$search_regex_frag.'"'; // e.g. `a:1:{s:12:"country_code";s:3:"USA";}`. + $_users = $wpdb->get_results("SELECT `user_id` as `ID` FROM `".$wpdb->usermeta."` WHERE `meta_key` = '".$wpdb->prefix."s2member_custom_fields' AND `meta_value` REGEXP '".esc_sql($regex)."'"); + + if($_users && is_array($_users)) + foreach($_users as $_user) + $include_user_ids[] = $_user->ID; + unset($_user); // Housekeeping. + } + return $include_user_ids; + } + /** * Pagination handler. * @@ -176,66 +230,5 @@ protected static function paginate($current_page, $total_results, $per_page, $cu return $pagination; } - - /** - * Searches s2Member Custom Fields; an extension to the self::query() method. - * - * @param array $args Arguments passed to self::query() after self::query() merged the defaults - * @param array $original_args Original arguments passed by the shortcode before self::query() merged the defaults - * - * @return array An array of User IDs - */ - protected static function search_s2_custom_fields($args, $original_args) - { - if(empty($args['search'])) - return array(); // Nothing to do. - - if(!empty($original_args['search_columns']) && !preg_grep('/(?:^|\W)s2member_custom_field_\w+/', $args['search_columns'])) - return array(); // Nothing to do. - - $s2custom_fields_sql = ''; - $include_user_ids = array(); - - if(empty($original_args['search_columns'])) - { - // Search all custom fields since there no search columns supplied - $s2custom_fields_sql = '.*'; - } - elseif($s2custom_field_columns = preg_grep('/(?:^|\W)s2member_custom_field_\w+/', $args['search_columns'])) - { - foreach($s2custom_field_columns as $_column) - { - // There are s2Member Custom Field columns to search. Let's extract the field ids - preg_match('/(?:^|\W)s2member_custom_field_(?P\w+)/', $_column, $matches); - - if(!empty($matches['field_id'])) - { - // Build a pipe-limited string of field ids to use as part of the SQL REGEXP - $s2custom_fields_sql .= preg_quote(trim($matches['field_id'])).'|'; - } - } - // Strip trailing pipe character - $s2custom_fields_sql = rtrim($s2custom_fields_sql, '|'); - } - if(!empty($s2custom_fields_sql)) - { - // Build the regex to find users who have s2Member Custom Fields that contain the search term (or any value if no search term is provided) - $regex = '(^|\{)s\:[0-9]+\:"('.$s2custom_fields_sql.')";s\:[0-9]+\:"'.preg_quote($args['search']).'"'; // Example database data: a:1:{s:12:"country_code";s:3:"USA";} - - // Run the database search - global $wpdb; - /** @var \wpdb $wpdb This line for IDEs that need a reference. */ - $_users = $wpdb->get_results("SELECT `user_id` as `ID` FROM `".$wpdb->usermeta."` WHERE `meta_key` = '".$wpdb->prefix."s2member_custom_fields' AND `meta_value` REGEXP '".esc_sql($regex)."'"); - - // Did we find any matches? - if(is_array($_users) && count($_users) > 0) - { - // Build an array of User IDs to be included in the search results - foreach($_users as $_user) - $include_user_ids[] = $_user->ID; - } - } - return !empty($include_user_ids) ? $include_user_ids : array(); - } } } \ No newline at end of file diff --git a/s2member-pro/includes/classes/sc-member-list-in.inc.php b/s2member-pro/includes/classes/sc-member-list-in.inc.php index 59a269a7..8c26d362 100644 --- a/s2member-pro/includes/classes/sc-member-list-in.inc.php +++ b/s2member-pro/includes/classes/sc-member-list-in.inc.php @@ -86,7 +86,7 @@ public static function shortcode($attr = array(), $content = '', $shortcode = '' 'show_display_name' => 'yes', 'link_display_name' => '', // /members/%%nicename%%/ - 'show_fields' => '' + 'show_fields' => '', ); if(!empty($attr['orderby']) && in_array($attr['orderby'], array('login', 'nicename', 'email', 'url', 'display_name'), TRUE)) $defaults['order'] = 'ASC'; // A more logical default when dealing with alphabetic ordering. @@ -127,7 +127,7 @@ public static function shortcode($attr = array(), $content = '', $shortcode = '' $args['meta_query'][] = array( 'key' => $wpdb->get_blog_prefix().'capabilities', 'value' => '"'.$_role.'"', - 'compare' => 'LIKE' + 'compare' => 'LIKE', ); if($attr['rlc_satisfy'] === 'ANY') // Default is `ALL` (i.e. `AND`). $args['meta_query']['relation'] = 'OR'; @@ -140,7 +140,7 @@ public static function shortcode($attr = array(), $content = '', $shortcode = '' $args['meta_query'][] = array( 'key' => $wpdb->get_blog_prefix().'capabilities', 'value' => '"s2member_level'.$_level.'"', - 'compare' => 'LIKE' + 'compare' => 'LIKE', ); if($attr['rlc_satisfy'] === 'ANY') // Default is `ALL` (i.e. `AND`). $args['meta_query']['relation'] = 'OR'; @@ -153,7 +153,7 @@ public static function shortcode($attr = array(), $content = '', $shortcode = '' $args['meta_query'][] = array( 'key' => $wpdb->get_blog_prefix().'capabilities', 'value' => '"access_s2member_ccap_'.$_ccap.'"', - 'compare' => 'LIKE' + 'compare' => 'LIKE', ); if($attr['rlc_satisfy'] === 'ANY') // Default is `ALL` (i.e. `AND`). $args['meta_query']['relation'] = 'OR';