Vor den Umstieg auf den Block Editor aka Gutenberg habe ich im Classic Editor das Plugin Better Internal Link Search verwendet, um im Link-Dialog nicht nur nach internen Beiträgen und Seiten zu suchen, sondern auch nach Kategorien, Schlagwörtern und weiteren Terms. Leider unterstützt das Plugin aber nicht den Block Editor und die Autoren haben wohl auch das Interesse an dem Plugin verloren (letztes Update vor 4 Jahren).
Die Suche z. B. nach Kategorien, Schlagwörtern (zusammengefasst Terms genannt) lässt sich aber recht einfach umsetzen, weil man den WP_REST_Search_Handler
recht einfach erweitern kann:
Seit WordPress 5.6 wird jetzt im Block Editor auch standardmäßig nach Terms (Kategorien, Schlagwörter…) gesucht. Ihr müsst jetzt keinen eigenen WP_REST_Taxonomy_Search_Handler
mehr schreiben!
Ich habe die Suche nach Links zu Terms als funktionsfähiges WordPress Plugin als Gist veröffentlicht:
Search Handler
Als Erstes müssen wir unseren eigenen Search Handler bauen. Dafür erweitern wir unsere eigene WP_REST_Taxonomy_Search_Handler
-Klasse, um die abstrakte WP_REST_Search_Handler
-Klasse und nutzen diese als Vorlage. Dabei kann man sich gut an der WP_REST_Post_Search_Handler
Klasse orientieren.
In der _construct-Funktion geben wir unseren Search Type einen eindeutigen Namen taxonomy
und geben als Subtypen alle öffentlichen public => true
Taxonomies an:
class WP_REST_Taxonomy_Search_Handler extends WP_REST_Search_Handler {
public function __construct() {
$this->type = 'taxonomy';
$this->subtypes = get_taxonomies( [
'show_ui' => true,
'public' => true,
] );
}
Code-Sprache: PHP (php)
Search Items
In der Funktion search_items
müssen wir die Suche nach zu der Suchanfrage passenden Terms durchführen und die gefunden IDs zurückgebenen:
public function search_items( WP_REST_Request $request ) {
// Get the taxonomy types to search for the current request.
$taxonomy_types = $request[ WP_REST_Search_Controller::PROP_SUBTYPE ];
if ( in_array( WP_REST_Search_Controller::TYPE_ANY, $taxonomy_types, true ) ) {
$taxonomy_types = $this->subtypes;
}
$offset = ( $request['page'] - 1 ) * $request['per_page'];
$query_args = array(
'taxonomy' => $taxonomy_types,
'number' => (int) $request['per_page'],
'offset' => (int) $offset,
'fields' => 'ids',
'hide_empty' => false,
);
if ( ! empty( $request['search'] ) ) {
$query_args['search'] = $request['search'];
}
$query = new WP_Term_Query();
$found_ids = $query->query( $query_args );
// ToDo: Determine total found taxonomies
$total = $offset + count( $found_ids );
if ( count( $found_ids ) === (int) $request['per_page'] ) {
$total = $total + 1;
}
return [
self::RESULT_IDS => $found_ids,
self::RESULT_TOTAL => $total,
];
}
Code-Sprache: PHP (php)
Im Gegensatz zu WP_Query unterstützt WP_Term_Query allerdings nicht das paged
und posts_per_page
Argument. Dieses habe ich durch number
und offset
ersetzt.
Außerdem ist es nicht möglich, die Anzahl aller gefundenen Terms die mit unserer Suchanfrage zusammenhängen, auszugeben. Deswegen wird zur Not immer ein um eins höherer Wert als $total
zurückgegeben, als bisher gefunden.
Prepare Items
In der Funktion prepare_item
muss jetzt das Item für die Antwort vorbereitet werden. Auch hier kann man sich relativ gut an dem Beispiel für Posts orientieren:
public function prepare_item( $id, array $fields ) {
$term = get_term( $id );
$data = [];
if ( in_array( WP_REST_Search_Controller::PROP_ID, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_ID ] = (int) $term->term_id;
}
if ( in_array( WP_REST_Search_Controller::PROP_TITLE, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_TITLE ] = $term->name;
}
if ( in_array( WP_REST_Search_Controller::PROP_URL, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_URL ] = get_term_link( $term );
}
if ( in_array( WP_REST_Search_Controller::PROP_TYPE, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_TYPE ] = $this->type;
}
if ( in_array( WP_REST_Search_Controller::PROP_SUBTYPE, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_SUBTYPE ] = get_taxonomy( $term->taxonomy )->labels->singular_name;
}
return $data;
}
Code-Sprache: PHP (php)
Prepare Item Links
Zu guter Letzt brauchen wir noch einen Link zu dem REST API Endpunkt für die Taxonomie und die einzelnen Terms. Da dieser Teil nicht von dem Block Editor verwendet wird, gebe ich nur ein leeres Array zurück:
public function prepare_item_links( $id ) {
$links = [];
return $links;
}
Code-Sprache: PHP (php)
Insgesamt sieht WP_REST_Taxonomy_Search_Handler
jetzt wie folgt aus:
class WP_REST_Taxonomy_Search_Handler extends WP_REST_Search_Handler {
public function __construct() {
$this->type = 'taxonomy';
$this->subtypes = get_taxonomies( [
'show_ui' => true,
'public' => true,
] );
}
/**
* Searches the object type content for a given search request.
*
*
* @param WP_REST_Request $request Full REST request.
* @return array Associative array containing an `WP_REST_Search_Handler::RESULT_IDS` containing
* an array of found IDs and `WP_REST_Search_Handler::RESULT_TOTAL` containing the
* total count for the matching search results.
*/
public function search_items( WP_REST_Request $request ) {
// Get the taxonomy types to search for the current request.
$taxonomy_types = $request[ WP_REST_Search_Controller::PROP_SUBTYPE ];
if ( in_array( WP_REST_Search_Controller::TYPE_ANY, $taxonomy_types, true ) ) {
$taxonomy_types = $this->subtypes;
}
$offset = ( $request['page'] - 1 ) * $request['per_page'];
$query_args = array(
'taxonomy' => $taxonomy_types,
// Replace paged with offset
//'paged' => (int) $request['page'],
'number' => (int) $request['per_page'],
'offset' => (int) $offset,
'fields' => 'ids',
'hide_empty' => false,
);
if ( ! empty( $request['search'] ) ) {
$query_args['search'] = $request['search'];
}
$query = new WP_Term_Query();
$found_ids = $query->query( $query_args );
$total = $offset + count( $found_ids );
if ( count( $found_ids ) === (int) $request['per_page'] ) {
$total = $total + 1;
}
return [
self::RESULT_IDS => $found_ids,
self::RESULT_TOTAL => $total,
];
}
/**
* Prepares the search result for a given ID.
*
*
* @param int $id Item ID.
* @param array $fields Fields to include for the item.
* @return array Associative array containing all fields for the item.
*/
public function prepare_item( $id, array $fields ) {
$term = get_term( $id );
$data = [];
if ( in_array( WP_REST_Search_Controller::PROP_ID, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_ID ] = (int) $term->term_id;
}
if ( in_array( WP_REST_Search_Controller::PROP_TITLE, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_TITLE ] = $term->name;
}
if ( in_array( WP_REST_Search_Controller::PROP_URL, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_URL ] = get_term_link( $term );
}
if ( in_array( WP_REST_Search_Controller::PROP_TYPE, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_TYPE ] = $this->type;
}
if ( in_array( WP_REST_Search_Controller::PROP_SUBTYPE, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_SUBTYPE ] = get_taxonomy( $term->taxonomy )->labels->singular_name;
}
return $data;
}
/**
* Prepares links for the search result of a given ID.
*
*
* @param int $id Item ID.
* @return array Links for the given item.
*/
public function prepare_item_links( $id ) {
$links = [];
return $links;
}
}
Code-Sprache: PHP (php)
Search Handler hinzufügen
Unseren eigenen Search Handler für Taxonomies müssen wir jetzt noch über den Filter wp_rest_search_handlers
hinzufügen:
function wp_rest_search_handlers( $handlers ) {
// Add Taxonomy Handler
$handlers[] = new WP_REST_Taxonomy_Search_Handler();
return $handlers;
}
add_filter( 'wp_rest_search_handlers', __NAMESPACE__ . '\wp_rest_search_handlers' );
Code-Sprache: PHP (php)
Search Type
Sucht ihr im Link-Dialog, wird eure Anfrage an den REST API Endpoint /wp-json/wp/v2/search
gesendet. Allerdings mit dem Parameter type=post
. Dadurch weiß der Server, dass ihr nach ein Post sucht. Leider lässt sich nicht der Type, nach dem man sucht (z.B. neben Post auch Taxonomy), im Link Dialog aussuchen. Deswegen müssen wir uns an dieser Stelle etwas überlegen, um diesen Standard zu überschreiben.
/wp-json/wp/v2/search?search=wordpress&per_page=20&type=post&_locale=user
Ich habe da die Notation vom Better Internal Link Search Plugin übernommen und wenn ich meine Suchanfrage mit einem -ter
oder -tax
(ist egal) starte, gefolgt von dem Suchbegriff, z.B. -ter WordPress
, wird dieser Request auf dem Server abgefangen, der type
auf taxonomy
geändert und der Search String angepasst.
Seit WordPress 5.6 wird im Block Editor jeweils für post und term = zwei Requests pro Suche an den Server gesendet d.h. hackt ihr den Request wie ich, musst ihr einen davon abfangen und eine leere Antwort zurückgeben. Sonst wird alles doppelt angezeigt. Ich habe das mit einem „unsupported“ Search Handler gelöst, der für post dann eine leere Antwort zurückgibt
function change_search_types( $result, $server, $request ) {
if ( '/wp/v2/search' !== $request->get_route() ) {
return $result;
}
$supported = [
'tax' => 'taxonomy',
'ter' => 'taxonomy',
'pl' => 'prettylink',
];
$matches = [];
preg_match( '/^-(\w{2,3}) (.*)/', $request->get_param( 'search' ), $matches );
if ( 3 === count( $matches ) && isset( $supported[$matches[1]] ) ) {
$request->set_param( 'type', $supported[$matches[1]] );
$request->set_param( 'search', $matches[2] );
}
return $result;
}
add_filter( 'rest_pre_dispatch', __NAMESPACE__ . '\change_search_types', 10, 3 );
Code-Sprache: PHP (php)
Zusammengesetzt findet ihr das Plugin als Gist auf Github.
Ausblick
Neben Kategorien, Schlagwörter und weiteren Taxonomies kann man auch einen Handler für viele weitere Suchen schreiben. Mit dem Better Internal Link Search Plugin kann man zum Beispiel nach Wikipedia-Einträgen, Github Repositories oder Gists, WordPress Plugins… suchen. Das kann man alles mit seinem eigenen Search Handler umsetzen.
Titelbild: Bild von Free-Photos auf Pixabay
Moin!
Super Plugin hast du da gebaut.
Habe mich schon darüber geärgert, dass ich nicht nach Terms suchen kann.
Hat du geplant, das Plugin ins offizielle Repo zu bringen? Wäre für mich ein Plugin, dass ich in alle Websites einbauen würde.
Es grüßt
derRALF
Hallo Johannes,
ich kann mich Ralf nur anschließen, mir fehlt die Funktion auch ständig.
Mein Wunsch wäre aber die Verankerung im Core.
Vielen Dank für den Code.
Jochen
Ja ich wäre auch eher für eine Lösung im Core. Am besten das man bei der Link Suche den
type
auswählen kann, nach was man suchen möchte. So ist die API ja eigentlich auch angedacht. Dann würde der Workarount mit-ter
wegfallen. Das finde ich aktuell zu unsauber, um es als Plugin zu veröffentlichen.Scheinbar wird daran bereits gearbeitet: https://github.com/WordPress/gutenberg/pull/22600 Allerdings nur nach Kategorien und Post Formats aktuell. Finde ich nicht so ganz rund, ich werde mich da mal einbringen.
Dieser Artikel wurde erwähnt auf gist.github.com
Dieser Article wurde erwähnt auf wpletter.de