Websupporter

Vor allem über WordPress.

Den WP_User_Query um einen Taxonomy Query erweitern

Beiträge können in WordPress in Kategorien einsortiert werden. User kann man nicht so einfach einem Term in einer Taxonomy zuordnen. Dabei gibt es dafür natürlich immer wieder Usecases. In diesem Beitrag will ich kurz auf die Probleme eingehen, die damit einhergehen können, auch Benutzer⋅innen bestimmten Kategorien zuzuordnen, um in einem zweiten Schritt zu demonstrieren, wie man ganz simpel den WP_User_Query um ein tax_query-Statement erweitern kann.

Das Taxonomiensystem erweitern

Das Taxonomiensystem von WordPress lässt es durchaus zu, dass nicht nur Posts, sondern auch andere Objekte kategorisiert werden können… Zumindest theoretisch können wir mit register_taxonomy() verschiedene Objekttypen registrieren:

register_taxonomy( $taxonomy, $object_type, $args );

So könnten wir beispielsweise user als Objekttyp angeben. Es sind kleinere Schwachstellen im System, welche dieses Konzept leider nicht bis zum Ende reibungslos erlauben. Zwei Probleme sind mir bekannt:

  1. Existiert ein Posttyp unter der gleichen Bezeichnung wie der Objekttyp, so wird die Taxonomie auch für diesen Posttypen zugänglich.
  2. Registrieren wir eine Taxonomie für sagen wir User und Beiträge (register_taxonomy('tax', ['user', 'post'], [])) und fügen dem Post mit der ID 10 einen bestimmten Term aus der Taxonomie zu, so würde dieser auch automatisch der Benutzer⋅in mit der ID 10 zugefügt.

In unserem Fall können wir beides ausschließen. Einige UI-Probleme und ähnliches werden schon von Plugins wie beispielsweise LH User Taxonomy angegangen und sollen hier nicht weiter erörtert werden.

Den WP_User_Query erweitern

Uns geht es in diesem Beitrag zentral um die Frage, wie wir nun solche Taxonomien im WP_User_Query nutzen können. Sollen User nach bestimmten Kriterien gefiltert werden, so hatten wir bisher nur die Möglichkeit, diese Kriterien im Metadatensystem von WordPress zu hinterlegen und einen entsprechenden WP_User_Query() zu schreiben. Das Metadatensystem eignet sich jedoch nicht wirklich, da sich daraus ziemlich langsame SQL Queries ergeben. Das Taxonomiensystem ist aber genau für solche Filter gebaut worden.

Einzig: Der WP_User_Query() unterstützt das natürlich nicht von sich aus. Warum auch; Taxonomien sind ja für Postobjekte da.

Nun gibt es aber die Action pre_user_query, welche das Queryobjekt übergibt. Diese Aktion wird gefeuert, nachdem die wesentlichen Bestandteile des SQL-Queries schon existieren, nämlich $query_where für das WHERE-statement, $query_from für das JOIN-statement und $query_orderby, welches wir auch benötigen werden.

Wir können also direkt das SQL-Statement für die Usersuche manipulieren, doch sehen wir uns am besten zunächst den Code an. Was wir erreichen möchten ist folgendes:

$args = [
    'tax_query' => [
        [
            'taxonomy' => 'tax',
            'terms' => 999,
        ],
    ],
];

$query = new \WP_User_Query($args);

Wie im WP_Query() wollen wir also einen tax_query-Array direkt als Argument übergeben, welcher auch genauso funktioniert wie im WP_Query().

Die WP_Tax_Query Klasse

Wenn wir im WP_Query() nachsehen, so sehen wir, dass letztlich dieser tax_query-Array an eine neue Instanz von WP_Tax_Query übergeben wird.

Letztlich ist es dieses WP_Tax_Query Objekt, welches die ganze Arbeit für uns übernimmt und das SQL für den WP_Query() hier produziert:

$clauses = $this->tax_query->get_sql( $wpdb->posts, 'ID' );

Instantiieren wir also ein neues WP_Tax_Query Objekt und übergeben diesem den tax_query-Array, erhalten wir mit Hilfe von get_sql() die entsprechenden SQL-Statements zurück. get_sql() erwartet zum einen den Namen der Datenbanktabelle, in welcher die verknüpften Objekte (Posts oder User oder Kommentare…) abgelegt sind, sowie den Namen der Spalte für den Identifikator der Objekte (Post-ID, User-ID, …). Hier zeigt sich, wie dynamisch das Taxonomiensystem von WordPress organisiert ist. Die zentrale Klasse, um die notwendigen SQL-Statements zu produzieren, ist schon so geschrieben, dass man außer der wp_posts-Tabelle auch andere Tabellen heranziehen kann.

Unser Code für den WP_User_Query

Damit haben wir eigentlich alle Bestandteile zusammen, um unseren WP_User_Query zu erweitern:

add_action(
    'pre_user_query',
    function ( $query ) {

        if  ( !isset($query->query_vars['tax_query'] ) {
            return;
        }

        global $wpdb;
        $tax_query = new \WP_Tax_Query( $query->query_vars['tax_query'] );
        $sql = $tax_query->get_sql( $wpdb->users, 'ID' );
        $query->query_where .= $sql['where'];
        $query->query_from .= $sql['join'];
        $query->query_orderby = 'GROUP BY ' . $wpdb->users . '.ID ' . $query->query_orderby;
    }   
);

An get_sql() übergeben wir den Namen der Usertabelle ($wpdb->users, üblicherweise wp_users), sowie die Spalte mit dem Identifikator (ID). Zurück erhalten wir nun einen Array, welcher die jeweiligen Statementerweiterungen für den WHERE-Teil und den JOIN-Teil unseres SQL-Statements enthält. Und schließlich müssen wir noch sicherstellen, dass User nicht doppelt im Ergebnis erscheinen, indem wir die SQL-Abfrage um ein GROUP BY-Statement erweitern.

Mit diesem paar Zeilen haben wir also den WP_User_Query um den aus WP_Query bekannten tax_query-Array erweitert und können Benutzer⋅innen nach bestimmten Kriterien filtern.

Über den Autoren

Seine erste Webseite hat David Remer 1998 in HTML verfasst. Wenig später war er fasziniert von DHTML und JavaScript. Nach jahrelanger Freelancerei arbeitete er zunächst für Inpsyde und ist heute Entwickler bei Automattic. Außerdem hat er das Buch "WordPress für Entwickler" verfasst.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.