WP_User_Query für skalierbare Mitgliederverzeichnisse

WP_User_Query für skalierbare Mitgliederverzeichnisse

Zuletzt überprüft: 12. Oktober 2026
17 Min. Lesezeit
Leitfaden
Full-Stack-Entwickler
Core Web Vitals

Im WordPress-Ökosystem bekommt WP_Query den ganzen Ruhm, aber WP_User_Query trägt Mitgliederseiten, Intranets und Community-Plattformen.

Egal, ob Sie eine einfache “Unser Team”-Seite oder eine komplexe “Arzt finden”-Suche mit Tausenden von Fachleuten bauen: Plugins wie Ultimate Member für die Präsentationsschicht sind oft übertrieben und ein Performance-Flaschenhals. Eine schlanke Abfrage im Template schlägt ein weiteres Mitglieder-Plugin, das bei jedem Request eigene Templates, REST-Routen und CSS lädt.

In diesem Guide umgehen wir die GUI und erstellen effiziente, sichere Benutzerabfragen direkt in PHP: Filter mit role__in, meta_query und Paginierung, ohne die Datenbank zu überlasten oder Anmeldenamen preiszugeben.

Offizielle Referenz: WP_User_Query auf developer.wordpress.org. Jeder Standardwert und jedes Verhalten weiter unten wurde am 12. Oktober 2026 gegen den Quellcode der Klasse in wordpress-develop geprüft.

#get_users() vs. WP_User_Query

Genauso wie get_posts() ein Wrapper für WP_Query ist, ist get_users() ein dünner Wrapper für WP_User_Query. Die Funktion akzeptiert dasselbe Argument-Array und gibt ein Array von WP_User-Objekten zurück (oder Teilmengen der Felder, wenn Sie fields übergeben).

  • Nutzen Sie get_users() für kurze Listen und sonst nichts, zum Beispiel fünf Redakteure für ein Autoren-Widget.
  • Nutzen Sie WP_User_Query, wenn Sie get_results() plus get_total() für paginierte Verzeichnisse, die Inspektion von $query->request beim SQL-Debugging oder einen expliziten Lebenszyklus des Abfrageobjekts brauchen.

Ein Unterschied im Verhalten ist wichtig. get_users() setzt count_total vor dem Aufbau der Abfrage auf false, führt also nie die zusätzliche Zählung aus und hat keine Gesamtzahl für Sie. Für ein Widget ist das richtig, für ein paginiertes Verzeichnis falsch.

Für beide APIs ist das Argument-Array der Vertrag. Alles Folgende funktioniert mit beiden Formen; die Beispiele nutzen WP_User_Query, weil Verzeichnisse Gesamtzahlen brauchen.

#Was WP_User_Query mit Standardargumenten zurückgibt

Die Standardwerte entscheiden über den Großteil der Kosten einer nachlässigen Abfrage:

ArgumentStandardWas das in der Praxis bedeutet
numberkein LimitAlle passenden Benutzer kommen zurück. Laut Docblock ist -1 (alle) auf größeren Websites mit Vorsicht einzusetzen.
paged1Wird nur genutzt, wenn number größer als null ist.
orderbyloginSortierung nach user_login, selten das, was eine öffentliche Liste will.
fieldsallVollständige WP_User-Objekte, alle User-Meta über cache_users() vorgeladen.
count_totaltrueFügt SQL_CALC_FOUND_ROWS hinzu und führt eine zweite Abfrage SELECT FOUND_ROWS() aus.
blog_idaktuelle WebsiteRollen- und Mitgliedschaftsfilter gelten nur für die aktuelle Website.
cache_resultstrueErgebnisse landen im Object Cache (seit WordPress 6.3).

Daraus folgen zwei Dinge. Eine Abfrage ohne number lädt auf einer Website mit vielen Benutzern alle. Und eine Abfrage, die nie paginiert, sollte 'count_total' => false übergeben, denn laut Docblock verbessert das Abschalten die Performance, wenn keine Paginierung nötig ist.

Außerdem verweigert die Klasse einen zu frühen Start: Seit WordPress 6.1.1 löst eine Benutzerabfrage vor dem Hook plugins_loaded einen _doing_it_wrong()-Hinweis aus.

#WP_User_Query mit role__in für eine Team-Seite

Redakteure und Autoren, sortiert nach Anzeigename, zwölf pro Seite:

$paged = max( 1, absint( $_GET['team_page'] ?? 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 ) ) {
	// Eine Abfrage für die Meta aller zwölf Benutzer statt zwölf Abfragen.
	update_meta_cache( 'user', wp_list_pluck( $results, 'ID' ) );

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

Hinweise, die in Produktion zählen:

  • role__in trifft jede der aufgeführten Rollen. Ziehen Sie es mehreren Abfragen mit role vor.
  • fields liefert stdClass-Zeilen statt vollständiger WP_User-Objekte und spart Speicher, wenn Sie nur IDs und Namen brauchen.
  • Mit einem fields-Array lädt WordPress die User-Meta nicht vor. Der Aufruf von update_meta_cache() oben hält die Bio-Abfragen deshalb bei einer einzigen Abfrage. Mit fields => 'all' erledigt die Klasse das selbst über cache_users().
  • Die Seitenzahl kommt aus einem eigenen Parameter team_page. Auf einer statischen Seite nutzt die Hauptabfrage page, nicht paged; get_query_var( 'paged' ) liefert dort stillschweigend den Standardwert.

#Wie die Filter role, role__in und capability funktionieren

Die drei Rollen-Argumente sehen ähnlich aus und verhalten sich unterschiedlich:

  • role akzeptiert einen String oder ein Array, und das Array ist eine UND-Liste: Ein Benutzer muss jede genannte Rolle haben. 'role' => [ 'editor', 'author' ] liefert auf einer Standard-Website fast niemanden.
  • role__in ist die ODER-Liste: mindestens eine der Rollen.
  • role__not_in schließt Benutzer aus, die eine der genannten Rollen haben.

Intern berührt keines davon eine Rollentabelle, denn WordPress hat keine. Rollen liegen als serialisiertes Array in der User-Meta-Zeile {prefix}capabilities, und jede Rollen-Klausel wird zu einem Vergleich LIKE '%"editor"%' auf dieser Zeile. Ein Rollenfilter ist also schon ein Join auf wp_usermeta, bevor Sie eine einzige meta_query hinzufügen.

Seit WordPress 5.9 gibt es außerdem capability, capability__in und capability__not_in. Sie übersetzen Capabilities in die Rollen, die sie gewähren, plus Capabilities, die einem Benutzer direkt zugewiesen sind. Der Docblock warnt, dass sie nicht für Capabilities funktionieren, die nicht in der Datenbank stehen oder über map_meta_cap gefiltert werden. Behandeln Sie sie als Abkürzung für primitive Capabilities wie edit_posts, nicht als Berechtigungsprüfung.

#Wie Sie WP_User_Query mit get_total() paginieren

Die Paginierungsargumente sind number (Seitengröße) und paged (1-basiert). Nach der Abfrage liefert get_total() die Trefferzahl über alle Seiten, nicht nur über die aktuelle.

$per_page = 24;
$paged    = max( 1, absint( $_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();

// Links mit add_query_arg( 'udir_page', $n ) bauen und URLs escapen.

Anti-Patterns, die Sie vermeiden sollten:

  • 'number' => -1 abfragen (oder weglassen) und mit array_slice in PHP zerschneiden
  • Eine zweite vollständige Abfrage nur zum Zählen, wenn get_total() die Arbeit schon gemacht hat
  • Ungeprüfte $_GET-Werte ohne strikte Allow-List in orderby einsetzen

Zwei Details aus dem Quellcode überraschen viele. Wenn Sie offset übergeben, gewinnt es: Die Klasse baut LIMIT offset, number und ignoriert paged. Und mit fields => 'ID' ist das Ergebnis ein flaches Array von ID-Strings aus $wpdb->get_col(), keine Objekte; casten Sie also vor strikten Vergleichen.

Nutzen Sie für öffentliche Verzeichnisse eine eigene Query-Variable (wie oben), damit Sie nicht mit der paged-Schleife des Blogs kollidieren.

#Wie Sie Mitglieder mit meta_query filtern

Mit meta_query werden Verzeichnisse nützlich, und hier zeigen sich die MySQL-Kosten. Beispiel: Abonnenten in Berlin, nur öffentliche Profile, mit PHP im Metafeld für Fähigkeiten.

$args = [
	'role'       => 'subscriber',
	'number'     => 20,
	'paged'      => 1,
	'fields'     => [ 'ID', 'display_name' ],
	'meta_query' => [
		'relation' => 'AND',
		[
			'key'     => 'city',
			'value'   => 'Berlin',
			'compare' => '=',
		],
		[
			'key'     => 'is_public_profile',
			'value'   => '1',
			'compare' => '=',
		],
		[
			'key'     => 'skills',
			'value'   => 'PHP',
			'compare' => 'LIKE', // funktioniert bei serialisierten Arrays; teuer im großen Maßstab
		],
	],
];

$directory = new WP_User_Query( $args );

Praktische Regeln für meta_query auf Benutzern:

  • Bevorzugen Sie exakte =-Vergleiche auf eigenen Schlüsseln (city, is_public_profile) gegenüber LIKE auf serialisierten Blobs.
  • LIKE 'PHP' ist ein Teilstring-Vergleich und trifft deshalb auch eine Fähigkeit namens PHPUnit. Das ist ein Korrektheitsfehler, nicht nur ein Tempoproblem.
  • Speichern Sie mehrwertige Fähigkeiten als getrennte boolesche Metaschlüssel (skill_php = 1), wenn Sie oft danach filtern. So werden UND/ODER zu indexfreundlichen Gleichheitsprüfungen. Alternativ eine skills-Zeile pro Wert, geschrieben mit add_user_meta( $user_id, 'skills', 'php' ), das doppelte Schlüssel erlaubt, und danach gefiltert mit 'compare' => '=' oder 'IN'.
  • Kombinieren Sie relation mit Bedacht. Verschachtelte Gruppen sind gültig, aber jede Klausel ist ein weiterer Join auf wp_usermeta.
  • Koppeln Sie öffentliche Filter immer an ein Sichtbarkeitsflag, damit private Profile nie in nicht angemeldeten Listen erscheinen.

Warum exakte Schlüssel zählen, zeigt das Core-Schema. Die Tabelle wp_usermeta, die wp-admin/includes/schema.php anlegt, hat einen Primärschlüssel, einen Index auf user_id und einen Präfix-Index auf meta_key. Auf meta_value, einer longtext-Spalte, gibt es keinen Index. MySQL kann den Join auf die Zeilen eines Schlüssels eingrenzen, muss aber jede dieser Zeilen lesen, um Werte zu vergleichen. Für Verzeichnisse mit sehr vielen Benutzern lohnt deshalb ein Suchindex in Elasticsearch (über ElasticPress) oder eine eigene SQL-Tabelle.

#Wie search und search_columns in WP_User_Query funktionieren

Das Argument search wird leicht falsch eingesetzt, weil es sich anders verhält als ein Suchfeld.

  • Ohne Platzhalter ist es ein exakter Treffer. 'search' => 'anna' findet den Benutzer, dessen Login genau anna lautet. Setzen Sie * an den Anfang, das Ende oder beide Seiten ('*anna*'), um ein LIKE mit % zu bekommen.
  • search_columns akzeptiert ID, user_login, user_email, user_url, user_nicename und display_name. ID wird immer mit = verglichen.
  • Ist search_columns leer, rät WordPress: Ein Begriff mit @ durchsucht user_email, ein numerischer Begriff user_login und ID, eine URL user_url, alles andere Login, URL, E-Mail, Nicename und Anzeigename zugleich.

Dieser letzte Standard ist für ein öffentliches Verzeichnis gefährlich. Wer einen Teil einer E-Mail-Adresse in Ihre Suche tippt, kann bestätigen, dass die Adresse zu einem Mitglied gehört. Legen Sie für die öffentliche Suche die Spalten immer selbst fest:

$term = sanitize_text_field( wp_unslash( $_GET['q'] ?? '' ) );

$args = [
	'role__in'       => [ 'subscriber' ],
	'search'         => '*' . $term . '*',
	'search_columns' => [ 'display_name' ],
	'number'         => 20,
	'fields'         => [ 'ID', 'display_name' ],
];

Über den Filter user_search_columns kann ein Plugin die Spaltenliste für jede Benutzersuche der Website ändern. Prüfen Sie ihn, wenn Ergebnisse nicht stimmen.

#Wie Sie WP_User_Query für große Verzeichnisse beschleunigen

Benutzerabfragen wirken billig, bis das Verzeichnis öffentlich und gefiltert ist.

#Wie Sie die Rückgabefelder (fields) begrenzen

Standardmäßig lädt WordPress mehr Daten, als ein Kartenraster braucht. Übergeben Sie fields früh:

$args = [
	'role'   => 'subscriber',
	'number' => 100,
	'fields' => [ 'ID', 'display_name', 'user_email' ], // Gibt stdClass-Objekte zurück, nicht WP_User
];

Entfernen Sie user_email aus öffentlichen Templates. Behalten Sie es nur für interne Werkzeuge hinter Capability-Prüfungen.

Halten Sie die Liste noch aus einem zweiten Grund kurz: Nennt ein fields-Array mehr als drei Felder, schaltet WP_User_Query den eigenen Ergebnis-Cache für diese Abfrage ab.

#Wie Sie Benutzer zählen, ohne Profile zu laden

$query = new WP_User_Query(
	[
		'role'   => 'subscriber',
		'number' => 1,
		'fields' => 'ID',
	]
);
$count = $query->get_total(); // nutzt SQL_CALC_FOUND_ROWS

number => 1 hält das Ergebnis bei einer einzigen ID, während get_total() trotzdem die volle Zahl meldet.

Für Rollensummen ohne Filter gibt es count_users(). Kostenlos ist das nicht: Dahinter steht kein gespeicherter Rollenzähler. Mit der Standardstrategie time läuft eine Aggregatabfrage über alle {prefix}capabilities-Zeilen mit einem LIKE pro Rolle, und der Docblock beschreibt sie als CPU-intensiv und geeignet für etwa 10^7 Benutzer. Brauchen Sie nur die Gesamtzahl der Konten, liest get_user_count() die gespeicherte Option user_count. Auf Websites, die wp_is_large_user_count() als groß einstuft (standardmäßig mehr als 10.000 Benutzer), aktualisiert ein Cron-Ereignis diesen Wert zweimal täglich, er kann also hinterherhinken.

#Wie Sie Ergebnisse von WP_User_Query cachen

Seit WordPress 6.3 cacht die Klasse selbst. Mit cache_results auf true speichert sie die Ergebnis-IDs und die Gesamtzahl in der Object-Cache-Gruppe user-queries, mit einem MD5 des erzeugten SQL als Schlüssel. Der Eintrag wird mit dem last-changed-Zeitstempel von users gesalzen, und Core aktualisiert diesen Zeitstempel bei added_user_meta, updated_user_meta, deleted_user_meta, add_user_role, set_user_role und remove_user_role. Jede Profiländerung invalidiert alle gecachten Benutzerabfragen.

Dieser Cache überlebt nur dann zwischen Requests, wenn die Website einen persistenten Object Cache hat (Redis oder Memcached). Ohne ihn hält er einen einzigen Seitenaufruf.

Wenn Sie eine eigene Schicht darüberlegen, etwa einen Transient mit dem gerenderten HTML über die Transients API, nutzen Sie einen Hash der Filter als Schlüssel und leeren Sie ihn über dieselben Hooks. Cachen Sie kein HTML mit Capability-geschützten Feldern für anonyme Besucher. Und halten Sie häufige Meta-Schreibvorgänge aus dem Request-Pfad heraus: Ein Plugin, das bei jedem Seitenaufruf ein “zuletzt gesehen”-Metafeld schreibt, verschiebt den users-Zeitstempel bei jedem Treffer und lässt den eingebauten Cache leer laufen.

#Wann Sie ein Mitgliederverzeichnis zu Elasticsearch verlagern

Joins auf wp_usermeta skalieren nicht wie eine Suchmaschine. Wenn sich die Filter vervielfachen (Stadt + Fachgebiet + Sprache + Verfügbarkeit) und das Publikum groß ist, verlagern Sie die durchsuchbare Projektion nach Elasticsearch (ElasticPress oder ein eigener Index) oder in eine eigene Verzeichnistabelle mit echten Indizes. Behalten Sie WP_User_Query für das abschließende Laden einer kleinen ID-Liste.

Die Klasse hat genau dafür einen Hook. Gibt der Filter users_pre_query ein Array statt null zurück, entfällt das SQL komplett; der Docblock bittet solche Filter, die Eigenschaft total_users zu setzen, damit die Paginierung weiter funktioniert.

#Wie Sie N+1-Abfragen für Avatare und User-Meta vermeiden

In der Schleife vervielfachen sich get_avatar() und wiederholte get_user_meta()-Aufrufe. Laden Sie die Meta der ganzen Seite mit update_meta_cache( 'user', $ids ) vor; die Funktion holt alle Meta-Zeilen der ID-Menge in einer Abfrage, wie im Team-Beispiel. Bei sehr breiten Profilen ist eine denormalisierte Verzeichniszeile, die beim Speichern des Profils geschrieben wird, noch günstiger.

#Wer Benutzer über die WordPress REST API auflisten darf

Ihr Template kann abgesichert sein, während /wp-json/wp/v2/users weiter antwortet. Was zurückkommt, hängt vom Besucher ab. Die Regeln stehen in WP_REST_Users_Controller und zusammengefasst in der Referenz des Users-Endpoints:

  • Ein Besucher ohne die Capability list_users (standardmäßig haben sie nur Administratoren) sieht nur Benutzer, die Beiträge in einem für die REST API freigegebenen Inhaltstyp veröffentlicht haben. Mitglieder ohne Veröffentlichungen fehlen in der anonymen Liste.
  • Im Standardkontext view enthält ein Benutzerobjekt id, name, url, description, link, slug und avatar_urls. username, email, roles und registered_date gibt es nur im Kontext edit, der list_users verlangt.
  • Filtern nach roles oder capabilities, Sortieren nach email oder registered_date und context=edit liefern ohne list_users einen Autorisierungsfehler.
  • Für diese Besucher ist eine Suche auf ID, user_login, user_nicename und display_name festgelegt. Der Parameter search_columns, eingeführt in WordPress 6.8, kann diese Liste nicht erweitern.
  • per_page steht standardmäßig auf 10 und ist auf 100 begrenzt; die Antwort trägt die Header X-WP-Total und X-WP-TotalPages.

Das Feld slug ist die Schwachstelle. wp_insert_user() bildet user_nicename aus dem Login, wenn kein Nicename angegeben ist: Die ersten 50 Zeichen von user_login laufen durch sanitize_title(). Bei den meisten Konten ist der öffentliche Slug also der Login oder fast derselbe. Derselbe Nicename steht in den URLs der Autoren-Archive.

Zwei Filter geben Ihnen Kontrolle. rest_user_query erhält bei jeder Listenanfrage die Argumente für WP_User_Query, dort können Sie eine Sichtbarkeits-meta_query ergänzen. Hat die Website keinen öffentlichen Bedarf für den Endpoint, entfernen Sie ihn nur für anonyme Besucher, denn der Block-Editor liest Benutzer für angemeldete Redakteure über dieselbe Route:

add_filter(
	'rest_endpoints',
	static function ( $endpoints ) {
		if ( ! is_user_logged_in() ) {
			unset( $endpoints['/wp/v2/users'], $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
		}
		return $endpoints;
	}
);

#Wie Sie User Enumeration in einem Mitgliederverzeichnis verhindern

Ein Mitgliederverzeichnis ist eine öffentliche Liste von Konten. Behandeln Sie es so.

  1. Keine Anmeldenamen zeigen: Führen Sie nie echo $user->user_login aus. Der Login ist die halbe Information, die ein Angreifer für einen Anmeldeversuch braucht. Nutzen Sie display_name oder einen eigenen öffentlichen Spitznamen. user_nicename wird standardmäßig aus dem Login abgeleitet und ist deshalb kein sicherer Ersatz.
  2. E-Mails verstecken: Geben Sie user_email nur aus, wenn der Besucher angemeldet ist und sie sehen darf.
  3. Autoren-Archive blockieren: Autoren-Archive (/?author=1, /?author=2) verraten auf vielen Websites weiterhin Logins. Wenn das Produkt keine öffentlichen Autoren-Archive braucht, leiten Sie sie um:
add_action(
	'template_redirect',
	static function () {
		if ( is_author() ) {
			wp_safe_redirect( home_url( '/' ), 301 );
			exit;
		}
	}
);
  1. Ausgaben escapen: alles, was PHP verlässt, mit esc_html(), esc_url() und esc_attr() bei Attributen.
  2. Admin-Verzeichnisse absichern: über die Capability list_users, nicht über eine “geheime” URL.
  3. Öffentliche Suche eingrenzen: search_columns auf display_name beschränken, wie oben gezeigt.

#Mitgliederverzeichnis und personenbezogene Daten nach DSGVO

Für Mitglieder in der EU oder im EWR ja. Namen, Fotos, Städte und Fähigkeiten identifizierbarer Personen sind personenbezogene Daten nach Artikel 4 Nummer 1 der Datenschutz-Grundverordnung. Artikel 25 Absatz 2 ergänzt eine Regel, die sich direkt auf Ihre Abfrageargumente übertragen lässt: Durch Voreinstellung dürfen personenbezogene Daten nicht ohne Eingreifen der betroffenen Person einer unbestimmten Zahl von Personen zugänglich gemacht werden.

Im Code heißt das:

  • Neue Konten starten mit is_public_profile = 0, bis das Mitglied aktiv zustimmt.
  • Eigene Flags für “Telefon zeigen” oder “E-Mail zeigen”: Ein öffentliches Profil ist keine Einwilligung zur Veröffentlichung von Kontaktdaten.
  • Jede öffentliche Abfrage enthält die Sichtbarkeitsklausel in ihrer meta_query, ebenso jeder rest_user_query-Filter.
  • Staging-Datenbanken mit echten Mitgliedern werden anonymisiert, bevor jemand außerhalb des Teams eine Kopie bekommt.

WP_User_Query filtert nur. Die Klasse ersetzt weder Einwilligung noch Zweckbestimmung oder Löschkonzept, aber ohne Sichtbarkeitsflag verhindert keine Datenschutzerklärung, dass das Profil erscheint.

#Mitglieder-Plugin oder eigenes Verzeichnis mit WP_User_Query

Mitglieder-Plugins verdienen ihren Platz bei Registrierung, Bezahlstufen und Nachrichten. Die öffentliche Liste ist das kleinere Problem, und genau sie läuft bei jedem anonymen Request. Behalten Sie das Plugin für den Kontoablauf und bauen Sie die Liste selbst, wenn sie schnell oder stark gefiltert sein muss.

Sind Sie bereits an das Raster eines Plugins gebunden, messen Sie es, bevor Sie es akzeptieren: Öffnen Sie die Liste mit Query Monitor und prüfen Sie, ob die Benutzerabfrage ein LIMIT hat, nur die Felder holt, die die Karten zeigen, und nicht pro Karte eine Meta-Abfrage auslöst.

#Wie ein Template für ein Mitgliederverzeichnis aufgebaut ist

Ein belastbares öffentliches Verzeichnis hat meist vier Schichten:

  1. Eingabe-Bereinigung: Sortierschlüssel per Allow-List, ganzzahlige Seite, normalisierter Stadt-Slug.
  2. Abfrage: role__in / meta_query / fields / number / paged.
  3. Darstellung: Karten nur aus IDs und sicheren Anzeigefeldern.
  4. Cache-Invalidierung: gekoppelt an die Metaschlüssel, die die Sichtbarkeit steuern.

Behalten Sie die Plugin-Oberfläche für Registrierung und Nachrichten, wenn Sie diese Funktionen brauchen. Halten Sie Liste und Filter in Ihrem Theme oder einem kleinen mu-plugin, das Sie selbst kontrollieren. Diese Trennung hält das Verzeichnis schnell, wenn das Marketing im nächsten Quartal einen weiteren Filter wünscht.

#Wie WP_User_Query Rollen auf Multisite behandelt

In einem WordPress-Multisite-Netzwerk ist die Benutzertabelle gemeinsam. WP_User_Query funktioniert weiter, aber Rollenfilter gelten pro Website. Ein Benutzer, der auf Website A Redakteur ist, hat auf Website B vielleicht keine Rolle. Für ein netzwerkweites Personenverzeichnis:

  • Setzen Sie blog_id, wenn Rollen auf eine Website begrenzt sein sollen.
  • Nehmen Sie nicht an, dass role__in => array( 'author' ) auf jedem Blog dieselben Personen bedeutet.
  • Speichern Sie für websiteübergreifende Mitarbeiterlisten ein Netzwerk-Metaflag (zum Beispiel wpp_show_in_directory) und fragen Sie dieses ab statt nur Rollen.

Der Quellcode erklärt den Grund. Auf Multisite fügt selbst eine Abfrage ohne Rollenargumente eine EXISTS-Klausel auf den Schlüssel {prefix}capabilities der aktuellen Website hinzu, sodass nur deren Mitglieder zurückkommen. Mit 'blog_id' => 0 entfallen die Rollen- und Mitgliedschaftsklauseln, und die Abfrage läuft über das ganze Netzwerk.

Staging-Kopien von Multisite kürzen oft die Benutzertabelle. Messen Sie die Abfragekosten auf einem Dump mit der Kardinalität der Produktion, sonst sieht Ihr meta_query-Plan bis zum Launch-Tag gut aus.

#Wie Sie das SQL von WP_User_Query mit Query Monitor testen

Vor dem Merge:

  1. Aktivieren Sie Query Monitor auf Staging.
  2. Öffnen Sie die Verzeichnis-URL mit der schwersten Filterkombination, die das Marketing nutzt.
  3. Bestätigen Sie eine primäre Benutzerabfrage, nicht eine Meta-Abfrage pro Karte.
  4. Speichern Sie $query->request in den PR-Notizen, damit die nächste Person Regressionen erkennt.

Muss das Verzeichnis eine Freitextsuche über Anzeigenamen unterstützen, nutzen Sie einen eigenen Suchindex oder ein eng begrenztes search-Argument in WP_User_Query. Kein LIKE über user_email auf einem öffentlichen Endpoint. Protokollieren Sie langsame Verzeichnis-Requests mit den Filterdaten, damit Sie sie später nachstellen können.

Soll ein eigenes WordPress-Verzeichnis oder eine Mitgliederabfrage vor dem Livegang geprüft werden? Sprechen Sie mit einem WordPress-Entwickler.

#Checkliste für ein Mitgliederverzeichnis mit WP_User_Query

  1. Nutzen Sie fields, damit Kartenraster keine vollständigen Benutzerobjekte laden, und bleiben Sie bei höchstens drei Feldern, um den eingebauten Cache zu behalten.
  2. Paginieren Sie mit number + paged und vertrauen Sie get_total() für die Seitenrechnung; ohne Paginierung count_total => false.
  3. Bevorzugen Sie exakte Metaschlüssel gegenüber LIKE auf serialisierten Fähigkeits-Arrays.
  4. Laden Sie Meta vor mit update_meta_cache() vor der Schleife.
  5. Cachen Sie ID-Listen oder Markup, wenn die Filter stabil sind; invalidieren Sie beim Schreiben von Profilen.
  6. Schützen Sie die Privatsphäre: nur display_name rendern, Logins, Nicenames und E-Mails aus öffentlichem HTML und öffentlicher Suche heraushalten, Ausgaben immer escapen.
  7. Prüfen Sie /wp/v2/users aus Sicht eines anonymen Besuchers.
  8. Messen Sie das SQL ($query->request) auf Staging mit realistischem Benutzer- und Meta-Volumen vor dem Launch.

Benutzer in WordPress sind Entitäten genau wie Beiträge. Fragen Sie sie mit derselben Präzision ab wie WP_Query.

Die Umsetzung dieses Themas betreuen wir im Rahmen unseres WordPress-Sicherheitsaudits.

Nächster Schritt

Machen Sie aus dem Artikel eine echte Umsetzung

Dieser Block stärkt die interne Verlinkung und führt Nutzer gezielt zum nächsten sinnvollen Schritt im Service- und Content-System.

Soll das Thema auf Ihrer Website umgesetzt werden?

Ich kann daraus ein konkretes Audit, Hardening-Maßnahmen und einen priorisierten Fix-Plan ableiten.

Relevanter Cluster

Weitere WordPress-Dienste und Wissensbasis entdecken

Stärken Sie Ihr Unternehmen mit professionellem technischen Support in den Kernbereichen des WordPress-Ökosystems.

Wann sollte ich WP_User_Query statt get_users verwenden?#
Nutzen Sie get_users() für einfache Listen, bei denen Sie nur ein Array von WP_User-Objekten brauchen. Greifen Sie zu WP_User_Query, wenn Sie feines orderby, meta_query, Kontrolle über fields, Paginierungssummen über get_total() oder direkten Zugriff auf das erzeugte SQL brauchen.
Wie paginiere ich ein Mitgliederverzeichnis sicher?#
Übergeben Sie number (Seitengröße) und paged (1-basierter Seitenindex) im Argument-Array und nutzen Sie get_total() für die Gesamtzahl der Treffer. Laden Sie nie alle Benutzer in den Speicher, um sie in PHP mit array_slice zu zerschneiden: Dieses Muster bricht unter echtem Traffic zusammen.
Warum ist meta_query in großen Mitgliederverzeichnissen langsam?#
Jede meta_query-Klausel erzeugt einen Join auf wp_usermeta. LIKE-Vergleiche und serialisierte Fähigkeits-Arrays erzwingen Tabellenscans. Bevorzugen Sie exakte Schlüssel, halten Sie Sichtbarkeitsflags in einer eigenen indizierten Tabelle oder lagern Sie die Suche an Elasticsearch aus, wenn das Verzeichnis über das hinauswächst, was MySQL-Joins bequem schaffen.
Was sollte ich in einer öffentlichen Mitgliederliste nie ausgeben?#
Geben Sie user_login und user_email nie in einem öffentlichen Verzeichnis aus. Rendern Sie display_name (oder ein eigenes öffentliches Spitznamen-Metafeld) und zeigen Sie E-Mails nur hinter Capability-Prüfungen oder in angemeldeten Intranet-Ansichten.

Sie brauchen ein FAQ für Branche und Zielmarkt? Wir erstellen eine Version passend zu Ihren Business-Zielen.

Kontakt aufnehmen

Ähnliche Artikel