Как найти всю пользовательскую мету из users.php в админке


14

Форма поиска в верхней части списка пользователей в области администрирования (wp-admin / users.php) ограничена и не выполняет поиск по всем мета-полям пользователя, таким как био, маркеры мгновенных сообщений и т. Д. Я не удалось найти плагин, который может добавить это.

Кто-нибудь знает о плагине или функции, которую я мог бы создать, которая могла бы расширить этот поиск для всей даты в базе данных _usermeta - в идеале даже дополнительные поля, создаваемые плагином или функцией.

Ответы:


24

Привет, @ user2041:

Понятно, что вам нужно изменить выполненный поиск, что можно сделать, изменив значения в экземпляре WP_User_Searchкласса, используемого для поиска (исходный код можно найти по адресу, /wp-admin/includes/user.phpесли вы хотите его изучить).

WP_User_Searchобъекта

Вот как print_r()выглядит этот объект с WordPress 3.0.3 при поиске термина " TEST" и без каких-либо других плагинов, которые могут повлиять на него:

WP_User_Search Object
(
  [results] => 
  [search_term] => TEST
  [page] => 1
  [role] => 
  [raw_page] => 
  [users_per_page] => 50
  [first_user] => 0
  [last_user] => 
  [query_limit] =>  LIMIT 0, 50
  [query_orderby] =>  ORDER BY user_login
  [query_from] =>  FROM wp_users
  [query_where] =>  WHERE 1=1 AND (user_login LIKE '%TEST%' OR user_nicename LIKE '%TEST%' OR user_email LIKE '%TEST%' OR user_url LIKE '%TEST%' OR display_name LIKE '%TEST%')
  [total_users_for_query] => 0
  [too_many_total_users] => 
  [search_errors] => 
  [paging_text] => 
)

pre_user_searchHook

Чтобы изменить значения WP_User_Searchобъекта, вы будете использовать 'pre_user_search'ловушку, которая получает текущий экземпляр объекта; Я вызвал print_r()этот хук, чтобы получить доступ к его значениям, которые я отображал выше.

Следующий пример, который вы можете скопировать в functions.phpфайл вашей темы или использовать в файле PHP для плагина, который вы пишете, добавляет возможность поиска по описанию пользователя в дополнение к возможности поиска по другим полям. Функция изменяет свойства query_fromи query_whereсвойства $user_searchобъекта, которые вам необходимы для понимания SQL.

Тщательная модификация SQL в хуках

Код в yoursite_pre_user_search()функции предполагает, что ни один другой плагин не изменил query_whereпредложение до него; если другой плагин изменил предложение where так, что замена 'WHERE 1=1 AND ('на "WHERE 1=1 AND ({$description_where} OR"больше не работает, то это тоже сломается. Гораздо сложнее написать надежное дополнение, которое не может быть нарушено другим плагином при такой модификации SQL, но это так и есть.

Добавьте начальные и конечные пробелы при вставке SQL в хуки

Также обратите внимание , что при использовании SQL , как это в WordPress , это всегда хорошая идея , чтобы включать в себя начальные и конечные пробелы такой с " INNER JOIN {$wpdb->usermeta} ON "иным ваш запрос может содержать следующую команду, где нет пространства перед тем "INNER", что будет, конечно , не в состоянии : " FROM wp_postsINNER JOIN {$wpdb->usermeta} ON ".

Используйте "{$wpdb->table_name"}вместо имен таблиц жесткого кодирования

Затем обязательно всегда используйте $wpdbсвойства для ссылки на имена таблиц в случае, если сайт изменил префикс таблицы с 'wp_'чего-то другого. Таким образом "{$wpdb->users}.ID" , вместо жесткого кодирования лучше обращаться к (с двойными кавычками, а не с одинарными)"wp_users.ID" .

Ограничьте запрос только при наличии условий поиска

И наконец, нужно изменять запрос только при наличии поискового запроса, который вы можете проверить, проверив search_termсвойство WP_User_Searchобъекта.

yoursite_pre_user_search()Функция для'pre_user_search'

add_action('pre_user_search','yoursite_pre_user_search');
function yoursite_pre_user_search($user_search) {
  global $wpdb;
  if (!is_null($user_search->search_term)) {
    $user_search->query_from .= " INNER JOIN {$wpdb->usermeta} ON " . 
      "{$wpdb->users}.ID={$wpdb->usermeta}.user_id AND " .
      "{$wpdb->usermeta}.meta_key='description' ";
    $description_where = $wpdb->prepare("{$wpdb->usermeta}.meta_value LIKE '%s'",
      "%{$user_search->search_term}%");
    $user_search->query_where = str_replace('WHERE 1=1 AND (',
      "WHERE 1=1 AND ({$description_where} OR ",$user_search->query_where);    
  }
}

Поиск каждой пары мета-ключ-значение требует SQL JOIN

Конечно, вероятная причина, по которой WordPress не позволяет выполнять поиск по полям usermeta, заключается в том, что каждое из них добавляет SQL JOINк запросу, а запрос с слишком большим количеством объединений может быть действительно медленным. Если вам действительно нужно выполнить поиск по многим полям, я бы создал '_search_cache'поле в usermeta, которое собирает всю остальную информацию в одно поле usermeta, чтобы требовать только одно объединение для его поиска.

Ведущие подчеркивания в мета-ключах говорят WordPress не отображать

Обратите внимание, что подчеркивание в '_search_cache'начале говорит WordPress, что это внутреннее значение, а не то, что когда-либо показывать пользователю.

Создайте кеш поиска с помощью 'profile_update'и 'user_register'хуков

Таким образом, вам нужно подключить 'profile_update'и 'user_register'то и другое , которые запускаются при сохранении пользователя и регистрации нового пользователя, соответственно. Вы можете получить все мета-ключи и их значения в этих хуках (но не указывать их со значениями, сериализированными или URL-кодированными массивами), а затем объединить их для хранения как одного длинного мета-значения с помощью '_search_cache'ключа.

Хранить мета как '|'разделенные парой ключ-значение

Я решил собрать все имена ключей и все их значения и объединить их в одну большую строку с двоеточиями (":"), отделяющими ключи от значений, и вертикальными чертами ("|"), разделяющими пары ключ-значение, как это (I обернул их в несколько строк, чтобы их можно было без прокрутки вправо):

nickname:mikeschinkel|first_name:mikeschinkel|description:This is my bio|
rich_editing:true|comment_shortcuts:false|admin_color:fresh|use_ssl:null|
wp_user_level:10|last_activity:2010-07-28 01:25:46|screen_layout_dashboard:2|
plugins_last_view:recent|screen_layout_post:2|screen_layout_page:2|
business_name:NewClarity LLC|business_description:WordPress Plugin Consulting|
phone:null|last_name:null|aim:null|yim:null|jabber:null|
people_lists_linkedin_url:null

Включает специализированный поиск по мета-использованию key:value

Добавление ключа и значений, как мы это сделали, позволяет выполнять поиск, например, " rich_editing:true", чтобы найти всех, у кого есть богатое редактирование, или искать " phone:null", чтобы найти тех, у кого нет номера телефона.

Но остерегайтесь артефактов поиска

Конечно, использование этой техники создает возможные нежелательные артефакты поиска, такие как поиск «бизнес», и все будут перечислены. Если это проблема, то вы можете не использовать такой сложный кеш.

yoursite_profile_update()Функция для 'profile_update'и'user_register'

Для функции yoursite_profile_update(), как yoursite_pre_user_search()описано выше, можно скопировать в functions.phpфайл вашей темы или вы можете использовать в файле PHP для плагина, который вы пишете:

add_action('profile_update','yoursite_profile_update');
add_action('user_register','yoursite_profile_update');
function yoursite_profile_update($user_id) {
  $metavalues = get_user_metavalues(array($user_id));
  $skip_keys = array(
    'wp_user-settings-time',
    'nav_menu_recently_edited',
    'wp_dashboard_quick_press_last_post_id',
  );
  foreach($metavalues[$user_id] as $index => $meta) {
    if (preg_match('#^a:[0-9]+:{.*}$#ms',$meta->meta_value))
      unset($metavalues[$index]); // Remove any serialized arrays
    else if (preg_match_all('#[^=]+=[^&]\&#',"{$meta->meta_value}&",$m)>0)
      unset($metavalues[$index]); // Remove any URL encoded arrays
    else if (in_array($meta->meta_key,$skip_keys))
      unset($metavalues[$index]); // Skip and uninteresting keys
    else if (empty($meta->meta_value)) // Allow searching for empty
      $metavalues[$index] = "{$meta->meta_key }:null";
    else if ($meta->meta_key!='_search_cache') // Allow searching for everything else
      $metavalues[$index] = "{$meta->meta_key }:{$meta->meta_value}";
  }
  $search_cache = implode('|',$metavalues);
  update_user_meta($user_id,'_search_cache',$search_cache);
}

Обновленная yoursite_pre_user_search()функция, включающая единый SQL JOINдля поиска всех интересных мета-значений

Конечно, yoursite_profile_update()чтобы иметь какой-либо эффект, вам нужно изменить, yoursite_pre_user_search()чтобы использовать '_search_cache'мета-ключ вместо описания, которое мы здесь приводим (с теми же оговорками, что и упомянутые выше):

add_action('pre_user_search','yoursite_pre_user_search');
function yoursite_pre_user_search($user_search) {
  global $wpdb;
  if (!is_null($user_search->search_term)) {
    $user_search->query_from .= " INNER JOIN {$wpdb->usermeta} ON " . 
      "{$wpdb->users}.ID={$wpdb->usermeta}.user_id AND " . 
      "{$wpdb->usermeta}.meta_key='_search_cache' ";
    $meta_where = $wpdb->prepare("{$wpdb->usermeta}.meta_value LIKE '%s'",
      "%{$user_search->search_term}%");
    $user_search->query_where = str_replace('WHERE 1=1 AND (',
      "WHERE 1=1 AND ({$meta_where} OR ",$user_search->query_where);
  }
}

@MikeSchinkel Отлично, полный ответ! Это один из многих случаев на этом сайте, где я хотел бы отдать несколько голосов за такой хорошо изученный ответ на нестандартный вопрос.
MathSmath

Спасибо @MathSmath - Знание того, что люди ценят это, помогает мне двигаться вперед. :)
MikeSchinkel

Майк, спасибо за подробный ответ! Я включу это в мою тему позже сегодня и посмотрю, к кому это приведет.
Джон Чендлер

Майк, мы рядом, но ..! Ясно, что это заставляет меня начать с нуля. Использование единственной функции, которую вы упомянули первой, или две функции для использования profile_update работают с точки зрения возможности поиска и получения правильных результатов. К сожалению, эти функции путаются с листингом, когда я впервые открываю users.php (в котором не указан поисковый запрос). Это не показывает всех пользователей. Когда я нажимаю на фильтр «Все», он показывает только два (из четырех), один из которых - я, а когда я нажимаю на фильтр «Администраторы», у него нет пользователей - даже меня! Есть идеи?
Джон Чендлер

Немного больше информации. Я отредактировал первую функцию для поиска в поле под названием «компания», которое я добавил через плагин дополнительных сведений о пользователе. Это работает, когда я ищу пользователя с названием компании. Но, похоже, что сортировка по All без поиска только возвращает два результата, которые имеют данные в поле компании, а не возвращает пользователей, у которых нет данных в поле компании.
Джон Чендлер

5

Я действительно ценю подход MikeSchinkel и подробное объяснение выше. Это было супер-полезно. Я не мог заставить его работать на меня, так как pre_user_search устарел и фактически не работает в 3.2. Я попытался просто переключить его с помощью pre_user_query, но это тоже не сработало. Дело в том, что кажется, что $ user_search-> search_term больше не работает, поэтому я просто использовал $ _GET ['s']. Я немного взломал и смог заставить это работать в 3.2. Единственное, что вам нужно установить, это ваш массив метаданных с возможностью поиска.

//Searching Meta Data in Admin
add_action('pre_user_query','yoursite_pre_user_search');
function yoursite_pre_user_search($user_search) {
    global $wpdb;
    if (!isset($_GET['s'])) return;

    //Enter Your Meta Fields To Query
    $search_array = array("customer_id", "postal_code", "churchorganization_name", "first_name", "last_name");

    $user_search->query_from .= " INNER JOIN {$wpdb->usermeta} ON {$wpdb->users}.ID={$wpdb->usermeta}.user_id AND (";
    for($i=0;$i<count($search_array);$i++) {
        if ($i > 0) $user_search->query_from .= " OR ";
            $user_search->query_from .= "{$wpdb->usermeta}.meta_key='" . $search_array[$i] . "'";
        }
    $user_search->query_from .= ")";        
    $custom_where = $wpdb->prepare("{$wpdb->usermeta}.meta_value LIKE '%s'", "%" . $_GET['s'] . "%");
    $user_search->query_where = str_replace('WHERE 1=1 AND (', "WHERE 1=1 AND ({$custom_where} OR ",$user_search->query_where);    
}

Надеюсь, это кому-нибудь поможет.


У кого-нибудь есть недавний опыт с этим? Лично я не могу заставить работать ни один из этих кусков кода, включая самые последние. Я пробовал некоторые другие варианты, но нашел проблемы с нумерацией результатов .
Роберт Эндрюс


1

Вот решение для новейшей версии WordPress.

add_action( 'pre_user_query', 'yoursite_pre_user_search'  );
    function yoursite_pre_user_search( $query ) {
        $query->query_where .= "YOUR QUERY '" . str_replace("*", "%", $query->query_vars[ 'search' ] ) . "')";
    }

-1

Это то, что я придумал для WordPress 4.7.1, который добавляет поиск по шаблону ко всем метаданным пользователя.


add_action( 'pre_user_query', 'ds_pre_user_search'  );
function ds_pre_user_search( $query ) {
    global $wpdb;

    if( empty($_REQUEST['s']) ){return;}
    $query->query_from .= ' LEFT JOIN '.$wpdb->usermeta.' ON '.$wpdb->usermeta.'.user_id = '.$wpdb->users.'.ID';
    $query->query_where = "WHERE 1=1 AND (user_login LIKE '%".$_REQUEST['s']."%' OR ID = '".$_REQUEST['s']."' OR meta_value LIKE '%".$_REQUEST['s']."%')";
    return $query;
}

По сути, мы просто соединяем таблицы users и user_meta с идентификатором пользователя и перестраиваем предложение WHERE, чтобы включить поиск по столбцу meta_value.


1
То, что вы предлагаете здесь, очень опасно ! Вы никогда не должны передавать то, что предоставлено пользователем, в БД. Они могут отбросить ваши таблицы, взломать вашу базу данных (см. SQL-инъекцию как поисковую фразу) или просто зашифровать все ваши данные. Сделай "%".like_escape( $_GET['s'] )."%"вместо этого. То же самое касается всех других предоставленных пользователем данных . В противном случае ваше поле поиска становится открытыми воротами для ваших данных.
Кайзер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.