Jak przeszukać wszystkie meta użytkowników z users.php w adminie

14

Formularz wyszukiwania u góry listy użytkowników w obszarze administracyjnym (wp-admin / users.php) jest ograniczony i nie przeszukuje wszystkich meta pól użytkownika, takich jak bio, uchwyty komunikatorów internetowych itp. Nie mam udało mi się znaleźć wtyczkę, która może to dodać.

Czy ktoś wie o wtyczce lub funkcji, którą mógłbym stworzyć, która mogłaby rozszerzyć to wyszukiwanie dla całej daty w _usermeta DB - idealnie nawet dodatkowe pola utworzone przez wtyczkę lub funkcję.

John Chandler
źródło

Odpowiedzi:

24

Cześć @ user2041:

Oczywiście, jak wiadomo, należy zmodyfikować wyszukiwanie, które można wykonać, modyfikując wartości w instancji WP_User_Searchklasy użytej do wyszukiwania (kod źródłowy można znaleźć, /wp-admin/includes/user.phpjeśli chcesz go przestudiować).

WP_User_Searchobiektu

Oto co print_r() jak wygląda ten obiekt w WordPress 3.0.3 podczas wyszukiwania terminu TEST i bez innych wtyczek, które mogłyby na niego wpływać:

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

Aby zmodyfikować wartości WP_User_Search obiektu, użyjesz 'pre_user_search'haka, który odbiera bieżącą instancję obiektu; Zadzwoniłem print_r()z tego haka, aby uzyskać dostęp do jego wartości, które pokazałem powyżej.

Poniższy przykład, który możesz skopiować do functions.phppliku motywu lub użyć w pliku PHP wtyczki, którą piszesz, dodaje możliwość wyszukiwania według opisu użytkownika a także możliwość wyszukiwania w innych polach. Funkcja modyfikuje query_fromi query_wherewłaściwości $user_searchobiektu, który należy rozumieć w języku SQL.

Ostrożnie modyfikuj SQL w hakach

Kod w yoursite_pre_user_search()funkcji zakłada, że ​​żadna inna wtyczka nie zmodyfikowała query_whereklauzuli przed nią; jeśli inna wtyczka zmodyfikowała klauzulę where, zastępując ją'WHERE 1=1 AND (' ze "WHERE 1=1 AND ({$description_where} OR"nie działa to będzie złamać zbyt. Znacznie trudniej jest napisać solidny dodatek, który nie może zostać uszkodzony przez inną wtyczkę podczas modyfikowania SQL w ten sposób, ale tak właśnie jest.

Dodaj wiodące i końcowe spacje podczas wstawiania SQL w hakach

Zauważ również, że używając SQL w ten sposób w WordPress, zawsze dobrym pomysłem jest dołączenie spacji wiodących i końcowych, w " INNER JOIN {$wpdb->usermeta} ON "przeciwnym razie zapytanie SQL może zawierać następujące elementy, w których wcześniej nie było miejsca "INNER", co oczywiście nie powiedzie się:" FROM wp_postsINNER JOIN {$wpdb->usermeta} ON " .

Posługiwać się "{$wpdb->table_name"} zamiast na stałe nazw tabel

Następnie pamiętaj, aby zawsze używać $wpdbwłaściwości do odwoływania się do nazw tabel, na wypadek gdyby strona zmieniła prefiks tabeli z 'wp_'czegoś innego. Dlatego lepiej jest odwoływać się do nich "{$wpdb->users}.ID" (z podwójnymi cudzysłowami, a nie pojedynczymi) zamiast na stałe "wp_users.ID".

Ogranicz zapytanie do tylko wtedy, gdy istnieją wyszukiwane hasła

Na koniec należy zmodyfikować zapytanie tylko wtedy, gdy istnieje wyszukiwane hasło, które można przetestować, sprawdzając search_termwłaściwość plikuWP_User_Search obiektu.

yoursite_pre_user_search()Funkcyjny'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);    
  }
}

Wyszukiwanie każdej pary meta klucz-wartość wymaga kodu SQL JOIN

Oczywiście prawdopodobnym powodem, dla którego WordPress nie pozwala na wyszukiwanie w polach usermeta, jest to, że każde z nich dodaje JOINzapytanie SQL do zapytania, a zapytanie zawierające zbyt wiele połączeń może być naprawdę powolne. Jeśli naprawdę potrzebujesz przeszukiwać wiele pól, utworzę '_search_cache'pole w usermeta, które zbiera wszystkie inne informacje w jedno pole usermeta, aby wymagać tylko jednego połączenia, aby przeszukać wszystko.

Wiodące podkreślenia w klawiszach meta informują WordPress, aby się nie wyświetlał

Zauważ, że wiodący znak podkreślenia '_search_cache'informuje WordPress, że jest to wartość wewnętrzna, a nie coś, co można kiedykolwiek wyświetlić użytkownikowi.

Utwórz pamięć podręczną wyszukiwania za pomocą haczyków 'profile_update'i'user_register'

Więc trzeba się podłączyć zarówno 'profile_update'i 'user_register'które są wyzwalane na oszczędność użytkownika i rejestracji nowego użytkownika, odpowiednio. Możesz chwycić wszystkie klucze meta i ich wartości w tych punktach zaczepienia (ale pominąć te z wartościami, które są serializowane lub tablice kodowane w adresach URL), a następnie połączyć je w celu przechowywania jako jednej długiej meta wartości za pomocą '_search_cache'klucza.

Przechowuj Meta jako '|'rozdzielane pary klucz-wartość

Postanowiłem pobrać wszystkie nazwy kluczy i wszystkie ich wartości i połączyć je w jeden duży ciąg z dwukropkami („:”) oddzielającymi klucze od wartości i pionowymi słupkami („|”) oddzielającymi takie pary klucz-wartość (I zawinąłem je w wiele wierszy, aby można je było przewijać bez przewijania w prawo):

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

Umożliwia wyszukiwanie specjalistyczne w Meta za pomocą key:value

Dodanie klucza i wartości, tak jak to zrobiliśmy, umożliwia wyszukiwanie takie jak „ rich_editing:true” w celu znalezienia każdego, kto ma bogatą edycję lub wyszukiwanie „phone:null ”, aby znaleźć osoby bez numeru telefonu.

Ale uważaj na artefakty wyszukiwania

Oczywiście użycie tej techniki tworzy potencjalnie niechciane artefakty wyszukiwania, takie jak wyszukiwanie „firmy” i wszyscy będą na liście. Jeśli jest to problem, możesz nie chcieć używać tak skomplikowanej pamięci podręcznej.

yoursite_profile_update()Funkcyjny 'profile_update'i'user_register'

Dla funkcji yoursite_profile_update(), jak yoursite_pre_user_search()wyżej, można skopiować do functions.phppliku motywu lub możesz użyć w pliku PHP wtyczki, którą piszesz:

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);
}

Zaktualizowana yoursite_pre_user_search()funkcja umożliwiająca pojedynczy SQL JOINdo wyszukiwania wszystkich interesujących wartości meta

Oczywiście, yoursite_profile_update()aby uzyskać jakikolwiek efekt, musisz zmodyfikować, yoursite_pre_user_search()aby używać '_search_cache'meta klucza zamiast opisu, który mamy tutaj (z tymi samymi zastrzeżeniami, jak wspomniano powyżej):

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
źródło
@MikeSchinkel Świetna, dokładna odpowiedź! To jeden z wielu razy na tej stronie, w których chciałbym wyrazić wiele pozytywnych opinii na tak dobrze zbadaną odpowiedź na pytanie, które nie jest dostępne.
MathSmath,
Dzięki @MathSmath - Uczenie się, że ludzie to doceniają, sprawia, że ​​pracuję. :)
MikeSchinkel,
Mike, dzięki za dokładną odpowiedź! Później będę pracować nad tym tematem i zobaczę, kto to będzie.
John Chandler,
Mike, jesteśmy blisko, ale ...! Najwyraźniej jest to dobry początek. Użycie albo jednej funkcji, o której wspomniasz jako pierwszej, albo dwóch funkcji do wykorzystania profilu_update działa pod względem możliwości wyszukiwania i uzyskiwania właściwych wyników. Niestety, te funkcje mają problemy z listowaniem, gdy po raz pierwszy pobieram users.php (który nie został określony). Nie pokazuje wszystkich użytkowników. Kiedy klikam filtr Wszystkie, pokazuje tylko dwa (z czterech), z których jeden to ja, a kiedy klikam filtr Administratorzy, nie ma żadnych użytkowników - nawet mnie! Jakieś pomysły?
John Chandler,
Trochę więcej informacji. Zedytowałem pierwszą funkcję do wyszukiwania w polu o nazwie „firma”, którą dodałem za pomocą wtyczki dodatkowych danych użytkownika. Działa, gdy szukam użytkownika o nazwie firmy. Wygląda jednak na to, że sortowanie według Wszystkich, bez wyszukiwania, zwraca tylko dwa wyniki zawierające dane w polu firmy i nie zwraca użytkowników, którzy nie mają danych w polu firmy.
John Chandler,
5

Naprawdę doceniam podejście MikeSchinkela i dokładne wyjaśnienie powyżej. To było bardzo pomocne. Nie mogłem zmusić go do działania, ponieważ pre_user_search jest przestarzałe i tak naprawdę nie działa w 3.2. Próbowałem po prostu wyłączyć to z pre_user_query, ale to też nie działało. Chodzi o to, że wydaje się, że $ user_search-> search_term już nie działa, więc właśnie użyłem $ _GET ['s']. Zrobiłem trochę hakowania i udało mi się to uruchomić w 3.2. Jedyne, co musisz ustawić, to tablica metadanych, które można przeszukiwać.

//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);    
}

Mam nadzieję, że to komuś pomoże.

David
źródło
Czy ktoś ma jakieś niedawne doświadczenia z tym? Osobiście nie mogę uruchomić żadnego z tych fragmentów kodu, w tym najnowszych. Próbowałem innych opcji, ale znalazłem problemy z paginacją wyników .
Robert Andrews,
1

Oto rozwiązanie najnowszej wersji 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' ] ) . "')";
    }
Patrik Grinsvall
źródło
-1

Właśnie to wymyśliłem dla WordPress 4.7.1, który dodaje wyszukiwanie symboli wieloznacznych do wszystkich metadanych użytkowników.


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;
}

Zasadniczo łączymy tabele użytkowników i user_meta z identyfikatorem użytkownika i przebudowujemy klauzulę WHERE, aby uwzględnić wyszukiwanie w kolumnie meta_value.

wagontrader
źródło
1
To, co tu sugerujesz, jest bardzo niebezpieczne ! Nigdy nie powinieneś nigdy przekazywać niczego, co zapewnia użytkownik do bazy danych. Mogą upuścić twoje tabele, przejąć bazę danych - zobaczyć iniekcję SQL jako wyszukiwane wyrażenie - lub po prostu zaszyfrować wszystkie dane. Zrób "%".like_escape( $_GET['s'] )."%"zamiast tego. To samo dotyczy wszystkich innych danych dostarczonych przez użytkownika . W przeciwnym razie pole wyszukiwania stanie się otwartą bramą do danych.
kaiser