20.4.4. Benutzerdefinierte Inhaltstypen in der REST API

Sie haben bereits in einem der früheren Kapitel gelernt, wie sich benutzerdefinierte Inhalte generieren lassen. Zum Beispiel eigene Artikeltypen, Taxonomien oder Terme.

Die jeweils in den Kapiteln genannte Funktionen zum Anlegen neuer Inhaltstypen können genutzt werden um sie in der REST-API aufzunehmen. Dabei können wir auf die entsprechenden Controller-Klassen von WordPress zurückgreifen um uns die Arbeit zu erleichtern. Genug der Worte. Schauen wir uns ein erstes Beispiel an.

Benutzerdefinierte Artikeltypen

Durch die Funktion register_post_type() lassen sich neue Artikeltypen anlegen. Wir erstellten einen Artikteltyp mit dem Namen book wie folgt:

<?php
add_action( 'init', 'mm_register_post_type' );

function mm_register_post_type() {

	$labels = array(
			'name'               => 'Books',
			'singular_name'      => 'Book',
			'menu_name'          => 'Books',
			'name_admin_bar'     => 'Book',
			'add_new'            => 'Add New',
			'add_new_item'       => 'Add New Book',
			'new_item'           => 'New Book',
			'edit_item'          => 'Edit Book',
			'view_item'          => 'View Book',
			'all_items'          => 'All Books',
			'search_items'       => 'Search Books',
			'parent_item_colon'  => 'Parent Books:',
			'not_found'          => 'No books found.',
			'not_found_in_trash' => 'No books found in Trash.'
	);

	$args = array(
			'labels'             => $labels,
			'public'             => true,
			'publicly_queryable' => true,
			'show_ui'            => true,
			'show_in_menu'       => true,
			'query_var'          => 'book',
			'rewrite'            => array( 'slug' => 'book' ),
			'capability_type'    => 'post',
			'has_archive'        => true,
			'hierarchical'       => false,
			'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' )
	);

	register_post_type( 'book', $args );

}
?>

show_in_rest-Parameter

Da wir den Artikeltyp nun auch in der REST-API nutzen möchten, ergänzen wir in der Argumentenliste folgendes:

<?php
$args = array(
	'labels'             => $labels,
	'public'             => true,
	...
	'show_in_rest'       => true,
);
?>

Kaum zu glauben, aber das war’s! Nun bekommen wir zwei neue Routen mit allen Endpunkten, die wir brauchen:

GET|POST /wp/v2/book
GET|POST|PUT|PATCH|DELETE /wp/v2/book/(?P<id>[\\d]+)

rest_base-Parameter

Natürlich gibt es aber noch ein paar weitere Einstellungen. So lässt sich z.B. die so genannte REST Basis einstellen. Das gelingt wie folgt:

<?php
$args = array(
	'labels'             => $labels,
	'public'             => true,
	...
	'show_in_rest'       => true,
	'rest_base'          => 'books',
);
?>

Damit lässt sich die URL zu den Routen anpassen. Statt book haben wir nun – weil es etwas logischer ist – books anstatt book in den URLs stehen:

/wp/v2/books
/wp/v2/books/(?P<id>[\\d]+)

rest_controller_class-Parameter

Dazu gestellt sich das letzte Argument namens rest_controller_class. Damit lässt sich die Controller-Klasse für den Artikeltyp einstellen. Der Standardwert ist WP_REST_Posts_Controller. Man kann aber auch seinen eigenen Klassennamen eingeben. Die PHP-Klasse muss aber eine Subklasse von WP_REST_Posts_Controller sein.

In der Argumentenliste können wir ergänzen:

<?php
$args = array(
	'labels'			=> $labels,
	'public'			=> true,
	...
	'show_in_rest'		=> true,
	'rest_base'		=> 'books',
	'rest_controller_class' => 'MM_REST_Books_Controller',
);
?>

Doch im Code müssen wir die Klasse definieren:

<?php
class MM_REST_Books_Controller extends WP_REST_Posts_Controller {
	...
}
?>

Controller-Klassen übernehmen – wie der Name bereits aussagt – die Kontrolle über die einzelnen Endpunkte der Routen. Genauer gesagt empfängt ein Controller Daten, verarbeitet diese in einer eigenen Funktion und gibt dann wieder etwas zurück. Wenn Sie einen benutzerdefinierte Artikeltyp anlegen der dem Typ „Beiträge“ sehr ähnlich ist, ist es von Vorteil, die Controller-Klasse nicht zu verändern weil die Funktionen in der Regel gleich sind.

Wenn Sie aber ganz andere Funktionen benötigten, ist der Einsatz eines eigenen Controllers sinnvoll. Im Falle von benutzerdefinierten Inhaltstypen (wie die oben genannten Artikeltypen bzw. die unten genannten Taxonomien) müssen Sie Ihre Controller-Klasse von WP_REST_Posts_Controller oder WP_REST_Terms_Controller ableiten.

Wie Sie eigene Controller bauen bzw. die bestehenden erweitern, beschreibe ich im nächsten Kapitel.

Benutzerdefinierte Taxonomien

Im Kapitel zu den Taxonomien haben wir zum Artikeltyp book noch die Taxonomie genere angelegt:

<?php

add_action( 'init', 'mm_register_taxonomy_and_post' );

function mm_register_taxonomy_and_post() {

	$tax_labels = array(
		'name'              => 'Genres',
		'singular_name'     => 'Genre',
		'search_items'      => 'Search Genres',
		'all_items'         => 'All Genres',
		'parent_item'       => 'Parent Genre',
		'parent_item_colon' => 'Parent Genre:',
		'edit_item'         => 'Edit Genre',
		'update_item'       => 'Update Genre',
		'add_new_item'      => 'Add New Genre',
		'new_item_name'     => 'New Genre Name',
		'menu_name'         => 'Genre',
	);

	register_taxonomy( 'genre', 'book', array(
		'labels' => $tax_labels
	) );

	$labels = array(
		'name'               => 'Books',
		'singular_name'      => 'Book',
		'menu_name'          => 'Books',
		'name_admin_bar'     => 'Book',
		'add_new'            => 'Add New',
		'add_new_item'       => 'Add New Book',
		'new_item'           => 'New Book',
		'edit_item'          => 'Edit Book',
		'view_item'          => 'View Book',
		'all_items'          => 'All Books',
		'search_items'       => 'Search Books',
		'parent_item_colon'  => 'Parent Books:',
		'not_found'          => 'No books found.',
		'not_found_in_trash' => 'No books found in Trash.'
	);

	$args = array(
		'labels'             => $labels,
		'public'             => true,
		'publicly_queryable' => true,
		'show_ui'            => true,
		'show_in_menu'       => true,
		'query_var'          => 'book',
		'rewrite'            => array( 'slug' => 'book' ),
		'capability_type'    => 'post',
		'has_archive'        => true,
		'hierarchical'       => false,
		'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ),

		// Hinzufügen des Taxonomies
		'taxonomies'         => array( 'genre' ),
	);

	register_post_type( 'book', $args );

}
?>

Auch hier können wir in der Argumentenliste die drei Parameter von oben ergänzen:

<?php
register_taxonomy( 'genre', 'book', array(
	'labels'				=> $tax_labels,
	'show_in_rest'			=> true,
	'rest_base'			=> ''genres,
	'rest_controller_class'	=> 'WP_REST_Terms_Controller',
) );
?>

Und erhalten dann die zwei zusätzlichen Routen mit dessen Endpunkten:

GET|POST /wp/v2/genres
GET|POST|PUT|PATCH|DELETE /wp/v2/genres/(?P<id>[\\d]+)

Die Klasse, die per rest_controller_class übergeben wird, muss hier eine Subklasse von WP_REST_Terms_Controller sein.

Existierende Artikeltypen erweitern

Nicht immer sind Sie es, die die volle Kontrolle über einen benutzerdefinierten Artikel haben. Es ist ja immerhin auch möglich, dass Sie auf ein anderes Plugin, welches einen benutzerdefinierten Artikeltyp erstellt, aufbauen.

Nehmen wir an, ein Plugin hat den benutzerdefinierten Artikeltyp book hinzugefügt und definiert, dass dieser explizit nicht in der REST-API auftauchen soll:

$args = array(
	'labels'             => $labels,
	'public'             => true,
	...
	'show_in_rest'       => false,
);

register_post_type( 'book', $args );

Wir nutzen den register_post_type_args-Filter-Hook und ändern dies:

<?php
add_filter( 'register_post_type_args', 'mm_manipulate_post_type_args', 10, 2 );

function mm_manipulate_post_type_args( $args, $post_type ) {

	if ( 'book' === $post_type ) {
		$args['show_in_rest'] = true;
		$args['rest_base']    = 'books';
	}

	return $args;
}
?>

Existierende Taxonomien erweitern

Ähnlich wie bei den benutzerdefinierten Artikeltypen gibt es auch bei Taxonomien einen entsprechenden Filter, der hier benutzt werden kann:

<?php

add_filter( 'register_taxonomy_args', 'mm_taxonomy_args', 10, 2 );

function mm_taxonomy_args( $args, $taxonomy_name ) {

	if ( 'genre' === $taxonomy_name ) {
		$args['show_in_rest']          = true;
		$args['rest_base']             = 'genres';
		$args['rest_controller_class'] = 'WP_REST_Terms_Controller';
	}

	return $args;
}

?>