Die REST-API ist ein Teil von WordPress, mit dem ich relativ selten direkt kommuniziere. Wenn, geht es oft um Filtermöglichkeiten für eine Post-Liste, die im Frontend angezeigt wird, das heißt: Der Website-User ändert einen Filter, wodurch im Hintergrund eine Anfrage an die REST-API geschickt und die Liste mit den Daten der Antwort angepasst wird.

Bis vor Kurzem habe ich dabei immer einiges an HTML innerhalb von JavaScript erzeugt, um das Markup für die einzelnen Posts mit den Daten der REST-API-Antwort zu erstellen. Immer mit Umständlichkeiten oder Dingen, die sich nicht einfach umsetzen lassen, etwa Markup für Responsive Images, das sonst bei bestimmten Bildfunktionen von WordPress direkt mit erstellt wird.

Beim letzten Anwendungsfall kam mir dann der Gedanke, ob das nicht auch schlauer geht.

REST-API-Einträge für Posts mit benötigtem Markup ergänzen

Ich bin auf die Idee gekommen, dass es doch praktisch wäre, ähnlich wie bei dem post_content-Eintrag im REST-API-Objekt eines Posts einen Eintrag zu haben, in dem das Markup liegt, das ich für diesen Beitrag in der Listenansicht brauche. Dann müsste ich mit JavaScript nach der erhaltenen AJAX-Antwort nur noch dieses Markup in die Seite einfügen.

Und über die register_rest_field()-Funktion ist genau das möglich:

add_action( 'rest_api_init', function() {
	register_rest_field( 'post', 'post_teaser', [
		'get_callback' => function( $post_obj ) {
			return sprintf(
				'<article class="post-teaser">
					<h2 class="teaser-title"><a href="%s">%s</a></h2>
					%s
				</article>',
				get_the_permalink( $post_obj['id'] ),
				get_the_title( $post_obj['id'] ),
				has_post_thumbnail( $post_obj['id'] ) ? sprintf(
					'<figure class="teaser-image">%s</figure>',
					get_the_post_thumbnail( $post_obj['id'], 'medium-large' )
				) : ''
			);
		}
	] );
} );Code-Sprache: PHP (php)

Wir führen die Funktion aus, wenn die rest_api_init-Action abgefeuert wird und übergeben als Parameter

  1. den Objekt-Typ post, für den wir das zusätzliche Feld anlegen möchten,
  2. den Namen post_teaser, der als Bezeichner im REST-API-Endpunkt genutzt wird,
  3. ein Array mit dem get_callback-Parameter, in dem definiert wird, was der Wert des Feldes sein soll (hier könnte unter anderem auch noch ein update_callback festgelegt werden, alle Optionen findet ihr auf der register_rest_field()-Devhub-Seite).

Die Funktion, die als Wert für get_callback angelegt ist, erhält als ersten Parameter das Post-Objekt, wie es in der REST-API angezeigt wird. Über $post_obj['id'] haben wir Zugriff auf die ID und können so Dinge wie den Titel und das Beitragsbild ermitteln, um ein Teaser-Markup für den Beitrag zu bauen und als Wert für das post_teaser-Feld zurückzugeben.

Wenn wir jetzt die REST-API für Beiträge abfragen, sollten wir für jeden Beitrag unser neues Feld post_teaser mit dem gewünschten Markup sehen 🎉

Eigenes REST-API-Feld nur in den notwendigen Fällen ausgeben

Beachten sollten wir, dass dieser post_teaser-Eintrag alle REST-API-Anfragen an den posts-Endpunkt verlangsamt, da zusätzliche Daten erst erzeugt und dann übermittelt werden.

Im besten Fall zeigen wir das post_teaser-Markup also nur an, wenn explizit über den _fields-Parameter in der REST-API-Anfrage danach verlangt wird.

Dafür erweitern wir die Funktion, die an get_callback übergeben wird, um ein paar Zeilen (Änderungen in den Zeilen 3–15):

add_action( 'rest_api_init', function() {
	register_rest_field( 'post', 'post_teaser', [
		'get_callback' => function( $post_obj, $attr, $request ) {
			// Get request params.
			$params = $request->get_query_params();

			// Check for _fields param.
			if ( ! isset( $params['_fields'] ) ) {
				return null;
			}

			$fields = wp_parse_list( $params['_fields'] );
			if ( ! in_array( 'post_teaser', $fields ) ) {
				return null;
			}

			return sprintf(
				'<article class="post-teaser">
					<h2 class="teaser-title"><a href="%s">%s</a></h2>
					%s
				</article>',
				get_the_permalink( $post_obj['id'] ),
				get_the_title( $post_obj['id'] ),
				has_post_thumbnail( $post_obj['id'] ) ? sprintf(
					'<figure class="teaser-image">%s</figure>',
					get_the_post_thumbnail( $post_obj['id'], 'medium-large' )
				) : ''
			);
		}
	] );
} );
Code-Sprache: PHP (php)

Zunächst ergänzen wir die Funktionsparameter $attr und $request, um an die Request-Informationen zu gelangen. Innerhalb der Funktion holen wir uns die Anfrageparameter und prüfen auf den _fields-Parameter. Ist er nicht vorhanden oder enthält nicht unser post_teaser-Feld, wird null zurückgegeben.

Dadurch haben wir zwar immer noch bei allen GET-Requests an den posts-Endpunkt unser neues Feld dabei, aber nur bei denen mit post_teaser innerhalb von _fields mit wird das Markup erzeugt und zurückgegeben. So reduzieren wir die unnötigen Daten auf ein Minimum, die in allen anderen Fällen übergeben werden.

Über den Filter rest_post_dispatch wäre es denkbar, das Feld auch komplett für andere Anfragen zu entfernen, aber da in diesem Fall das Markup erst erzeugt würde, glaube ich nicht, dass der Request dadurch schneller wäre als wenn wir den Vorgang des Post-Teaser-Erstellens direkt überspringen und null zurückgeben.