WP_Query får de fleste tutorialene. WP_User_Query er det medlemsportaler, ansattkataloger og fellesskapssider faktisk kjører på. Trenger du bare et «Vårt team»-rutenett, slår en lett spørring i temaet en medlemsplugin som dra med egne maler, REST-ruter og CSS på hvert request.
Denne guiden holder seg til det problemet: filtrere brukere med role__in, meta_query og paginering uten å steke databasen eller lekke påloggingsnavn. Eksemplene er skrevet for PHP-temaer og mu-plugins, ikke for page-builder-shortcodes. Offisiell referanse: WP_User_Query og get_users().
get_users() vs WP_User_Query
get_users() er en tynn wrapper rundt WP_User_Query. Den tar samme argument-array og returnerer WP_User-objekter (eller feltutvalg når du setter fields).
Bruk get_users() når du vil ha en kort liste og ikke noe mer - for eksempel fem redaktører til en byline-widget.
Bruk WP_User_Query når du trenger:
get_results()plussget_total()til paginerte kataloger- inspeksjon av
$query->requestmens du debugger SQL - samme args-form som
get_users(), men med eksplisitt spørringsobjekt
Argument-arrayen er kontrakten. Eksemplene under bruker WP_User_Query fordi kataloger trenger totaler. Begge API-ene leser samme dokumentasjon; forskjellen er kontrollen du får over resultatet etter spørringen.
Bygg en teamsiden med role__in
Redaktører og forfattere, sortert etter visningsnavn, tolv per side - typisk «ansatte»-side for et norsk byrå:
$paged = max( 1, (int) get_query_var( 'paged', 1 ) );
$args = [
'role__in' => [ 'editor', 'author' ],
'orderby' => 'display_name',
'order' => 'ASC',
'number' => 12,
'paged' => $paged,
'fields' => [ 'ID', 'display_name' ],
];
$user_query = new WP_User_Query( $args );
$results = $user_query->get_results();
if ( ! empty( $results ) ) {
echo '<div class="team-grid">';
foreach ( $results as $user ) {
$avatar = get_avatar( $user->ID, 128 );
$name = esc_html( $user->display_name );
$bio = esc_html( get_user_meta( $user->ID, 'description', true ) );
echo "<article class='team-member'>
<figure>{$avatar}</figure>
<h3>{$name}</h3>
<p>{$bio}</p>
</article>";
}
echo '</div>';
}I produksjon:
role__inmatcher hvilken som helst av rollene. Foretrekk det fremfor flere spørringer medrole.fieldsgirstdClass-rader i stedet for fulleWP_User-objekter.- Bio krever fortsatt meta-lesing. Er rutenettet hot, cache HTML eller lag en denormalisert katalograd.
Paginering uten å laste alle brukere
Args: number (sidestørrelse) og paged (1-basert). get_total() er antall treff på tvers av sider - ikke bare inneværende side.
$per_page = 24;
$paged = max( 1, (int) ( $_GET['udir_page'] ?? 1 ) );
$query = new WP_User_Query(
[
'role__in' => [ 'subscriber', 'contributor' ],
'number' => $per_page,
'paged' => $paged,
'fields' => 'ID',
'orderby' => 'registered',
'order' => 'DESC',
]
);
$total_users = (int) $query->get_total();
$total_pages = (int) ceil( $total_users / $per_page );
$user_ids = $query->get_results();Unngå:
'number' => -1ogarray_slicei PHP- ekstra full spørring bare for å telle når
get_total()allerede finnes - usikre
$_GET-verdier rett inn iorderbyuten allow-list
På offentlige kataloger: egen query var (udir_page) så du ikke kolliderer med bloggens paged.
Avansert filtrering med meta_query
Tenk medlemskatalog for behandlere: abonnenter i Oslo, offentlig profil, ferdighet «PHP» (eller «fysioterapi» i en helseportal).
$args = [
'role' => 'subscriber',
'number' => 20,
'paged' => 1,
'fields' => [ 'ID', 'display_name' ],
'meta_query' => [
'relation' => 'AND',
[
'key' => 'city',
'value' => 'Oslo',
'compare' => '=',
],
[
'key' => 'is_public_profile',
'value' => '1',
'compare' => '=',
],
[
'key' => 'skills',
'value' => 'PHP',
'compare' => 'LIKE', // dyrt på serialiserte arrays
],
],
];
$directory = new WP_User_Query( $args );Praktiske regler:
- Eksakt
=på egne nøkler (city,is_public_profile) slårLIKEpå serialiserte blobs. - Lagre ofte brukte ferdigheter som booleans (
skill_php=1) hvis du filtrerer dem mye. - Hver klausul er en join på
wp_usermeta- holdrelation-trær korte. - Kombiner alltid offentlige filtre med et synlighetsflagg, slik at private profiler aldri dukker opp for anonyme.
For kataloger over ca. ti tusen aktive profiler: vurder Elasticsearch (ElasticPress) eller egen oppslagstabell. Hold WP_User_Query til å hydrere en liten ID-liste.
Ytelse: fields, telling, cache
Når katalogen er offentlig og filtrert, blir brukerspørringer dyre fort. Et ansatt-rutenett med tolv kort er ufarlig. Et «finn behandler»-søk med by, spesialitet og språk mot titusenvis av wp_usermeta-rader er en annen historie - særlig på delt hosting der MySQL allerede deler CPU med naboene.
Begrens feltene
$args = [
'role' => 'subscriber',
'number' => 100,
'fields' => [ 'ID', 'display_name' ],
];Dropp user_email fra offentlige maler. Behold det bak current_user_can( 'list_users' ) i admin-verktøy.
Tell uten å hydrere profiler
$query = new WP_User_Query(
[
'role' => 'subscriber',
'fields' => 'ID',
]
);
$count = $query->get_total();Uten filtre: count_users() er billigere fordi WordPress allerede holder rolletellinger. Bruk det til «Vi har X medlemmer»-statistikk, ikke til filtrerte katalogtreff.
Cache stabile resultatsett
Endres katalogen sjelden, lagre ID-er (eller ferdig HTML) i en transient nøklet på filter-hash. Invalider når relevante meta-nøkler oppdateres (updated_user_meta). Cache aldri HTML med kapasitetsstyrte felt for anonyme. Transient-TTL på 15-60 minutter er ofte nok for medlemskataloger som oppdateres i arbeidstiden.
Unngå N+1
I løkken multipliserer get_avatar() og gjentatte get_user_meta(). Prefetch kjente nøkler for ID-settet på siden, eller skriv en katalograd ved profil-lagring. Gravatar-kall ute kan også dominere TTFB - vurder lokal avatar-meta hvis katalogen er hot.
Sikkerhet: user enumeration
En medlemskatalog er en offentlig kontoliste. Behandle den deretter - særlig med personvernregler i Norge/EU. Botene som treffer /?author=1 bryr seg ikke om at katalogen din «bare er for medlemmer» hvis HTML-en allerede lekker login-navn.
- Aldri
echo $user->user_logini frontend. Brukdisplay_nameeller et offentlig kallenavn. - Aldri
user_emailuten innlogging og kapasitet. - Forfatterarkiv (
/?author=1) lekker ofte login. Trenger du dem ikke:
add_action(
'template_redirect',
static function () {
if ( is_author() ) {
wp_safe_redirect( home_url( '/' ), 301 );
exit;
}
}
);- Escape alt:
esc_html(),esc_url(),esc_attr(). - Admin-kataloger bak
list_users, ikke bak «hemmelig» URL.
Sette sammen en katalogmal
En offentlig katalog som tåler drift har vanligvis fire lag:
- Input - allow-list for sortering, heltall for side, normalisert by-slug (
oslo, ikke fri tekst rett i SQL). - Spørring -
role__in/meta_query/fields/number/paged. - Presentasjon - kort fra ID-er og trygge visningsfelt.
- Cache-invalidering - knyttet til meta-nøklene som styrer synlighet.
La plugin håndtere påmelding og meldinger hvis du trenger det. La listing og filtrering ligge i temaet eller en liten mu-plugin du eier. Det skillet holder katalogen rask når markedet ber om «ett filter til» neste kvartal.
På norske medlemsportaler (idrettslag, fagforeninger, behandlerkataloger) ser vi ofte at BuddyBoss eller Ultimate Member eier profilskjemaet, mens den offentlige søkesiden er custom PHP. Det er en sunn deling: tung UI for kontoen, lett spørring for listen.
Personvern og synlighet (Norge / GDPR)
En katalog over personer er personopplysninger. Praktisk i kode:
- Standard for nye kontoer:
is_public_profile=0til brukeren aktivt krysser av. - Egne felt for «vis telefon» / «vis e-post» - ikke anta at profil = offentlig kontaktinfo.
- Logg hvem som eksporterer medlemslister fra wp-admin (capability + audit hvis volumet er stort).
- Staging-databaser med ekte medlemmer skal anonymiseres før de deles med frilansere.
WP_User_Query filtrerer teknisk. Den erstatter ikke samtykke, formål eller slettpolicy. Men uten synlighetsflagg i meta_query lekker du profiler uansett hvor fin personvernerklæringen er.
Når du bør droppe plugin-katalogen
Tunge medlemsplugins løser påmelding, betalingsnivåer og meldinger. De er sjeldent optimale som den offentlige søkesiden:
- ekstra HTTP-assets på forsiden
- egne shortcodes som kjører brede spørringer uten
fields - REST-endepunkter som eksponerer mer enn kort-gridet trenger
Behold plugin for kontoflyt. Bygg listen selv med WP_User_Query. Hvis du allerede er låst til pluginens grid, mål SQL med Query Monitor før du godtar «det er greit nok» - ofte er det én SELECT uten LIMIT gjemt bak et fancy filter-UI.
Multisite og delte brukertabeller
På multisite er brukere delt, roller er per site. For nettverksomfattende ansatte-lister: nettverksmeta (wpp_show_in_directory) i stedet for bare role__in. Sett blog_id når roller skal scopes til ett nettsted.
Staging-kopier av multisite kutter ofte brukertabellen. Mål spørringskostnad på et dump som matcher produksjonsvolum - ellers ser meta_query-planen fin ut til lanseringsdagen, og så tar forsiden 800 ms med 40 000 rader i wp_usermeta.
Test SQL-en du faktisk shipper
- Query Monitor på staging.
- Åpne katalog-URL med tyngste filterkombinasjon markedet bruker (by + fag + språk er en klassiker).
- Bekreft én primær brukerspørring, ikke én meta-spørring per kort.
- Lagre
$query->requesti PR-notater så neste person ser regresjoner.
Fritekst-søk på tvers av navn: dedikert søkeindeks eller stramt search-arg på WP_User_Query. Ikke LIKE mot user_email på offentlig endepunkt. Logg trege katalog-requests med filter-payload så du kan gjenskape dem.
Sjekkliste før lansering
fieldssatt - ikke fulle objekter til kort-grid.- Paginering med
number+pagedogget_total(). - Eksakte meta-nøkler fremfor
LIKEpå serialiserte arrays. - Cache av ID-lister der filtrene er stabile; invalider ved profilskriving.
- Bare
display_namei offentlig HTML; e-post bak kapasitet. - Synlighetsflagg i
meta_queryfor alle offentlige lister. - Mål
$query->requestmed realistisk bruker- og metvolum. - Forfatterarkiv redirectet hvis produktet ikke trenger dem.
Brukere er førstklassige spørringsmål i WordPress. Behandle dem med samme disiplin som WP_Query.
Trenger du en skreddersydd medlemskatalog eller ytelsesgjennomgang av brukerspørringer, ta kontakt - vi bygger og drifter slike løsninger i WordPress.






