… oder um es mit technischen Begrifflichkeiten auszusprechen: Ein einzelner Post eines Post Types soll über einen anderen Slug angezeigt werden. Über die Google-Suche habe ich hier leider keine sinnvollen Treffer gefunden, denn als Ergebnis erhielt ich lediglich Anleitung dazu, wie man die Titelform des gesamten Inhaltstyps verändern kann.

In meinem konkreten Fall ging es darum, dass eine Produktauflistung standardmäßig unter /produkt/produktname erreichbar ist, wobei produkt hier der Name des Inhaltstyps ist. Für ein einzelnes Produkt sollte die URL-Struktur aber anders aussehen: /portfolio/produktname.

Die Eingabe im Backend für eine eigene Titelform für ein einzelnes Produkt erfolgt dabei über ein Postmeta-Feld.

Um das gewünschte Verhalten zu erreichen, musste ich an zwei Stellen ansetzen: Zum einen musste ich den Link zum Produkt ändern, damit er von get_permalink und ähnlichem bereits in abgeänderter Form gezogen wurde und damit auf Archivseiten, in Kategorien etc. bereits korrekt war. Dafür gibt es glücklicherweise den Filter post_type_link, über den ich schlicht die Standard-Titelform durch die individuelle austausche:

/**
 * Replace the permalink of a single post by a custom slug.
 * 
 * @param	string		$post_link The post permalink
 * @param	\WP_Post	$post The post object
 * @return	string The updated post permalink
 */
function my_replace_single_post_slug( string $post_link, WP_Post $post ): string {
	$custom_slug = get_field( 'slug', $post->ID );
	$default_slug = get_option( 'my_product_slug' ) ?? _x( 'product', 'post type slug', 'textdomain' );
	
	if ( $custom_slug ) {
		$post_link = str_replace( '/' . preg_quote( $default_slug ) . '/', '/' . $custom_slug . '/', $post_link );
	}
	
	return $post_link;
}

add_filter( 'post_type_link', 'my_replace_single_post_slug', 10, 2 );
Code-Sprache: PHP (php)

Da ich den Benutzern auch über die allgemeine Titelform die Kontrolle gebe, ist diese in der Option my_product_slug abgespeichert (oder es gibt einen Fallback auf den Standard).

Der spannendere Teil war jedoch, diesen Link auch funktionierend zu bekommen. Denn ein veränderter Link allein führt nicht dazu, dass WordPress automatisch weiß, was es dahinter für Inhalt anzeigen soll. Das wird über die sogenannten „Rewrite Rules“ bzw. Umschreibungsregeln definiert.

Im Grunde wird für jede individuelle Titelform eines jeden Produkts eine Umschreibungsregel angelegt und diese zu Beginn eingefügt, damit sie bevorzugt behandelt wird. Daraufhin wird der Cache für diese Umschreibungsregeln erneuert:

/**
 * Set custom rewrite rules for post with custom slug.
 * 
 * @param	\WP_Post	$post The post object
 */
function my_set_rewrite_rules( WP_Post $post ): void {
	$custom_slug = get_field( 'slug', $post->ID );
	
	if ( ! $custom_slug ) {
		return;
	}
	
	$default_slug = mb_strtolower( _x( 'Products', 'Post Type General Name', 'textdomain' ) );
	$rules = get_option( 'rewrite_rules', [] );
	$rules_added = false;
	
	foreach ( $rules as $link => $rule ) {
		if ( strpos( $link, $default_slug ) === false ) {
			continue;
		}
		
		add_rewrite_rule( str_ireplace( $default_slug, $custom_slug, $link ), $rule, 'top' );
		$rules_added = true;
	}
	
	if ( $rules_added ) {
		flush_rewrite_rules( false );
	}
}

add_action( 'rest_after_insert_product', 'set_rewrite_rules' );
Code-Sprache: PHP (php)

Anmerkungen

Grundsätzlich funktioniert das Verhalten damit so, wie ich es mir wünschte. Dennoch hier ein paar Anmerkungen zu möglichen Problemen:

Die zweite Funktion verwendet die Action rest_after_insert_product, die nur bei einer Beitragsaktualisierung über die API ausgeführt wird, also beispielsweise beim Speichern über den Block-Editor. Die eigentlich von mir gewünschte Action wp_insert_post konnte ich nicht verwenden, da zu diesem Zeitpunkt die Metadaten, also beispielsweise eine individuell hinzugefügte Titelform, noch nicht in der Datenbank gespeichert sind und man daher nur über Umwege an dessen aktuellen Wert käme.

Wenn die individuelle Titelform erneut geändert wird, bleiben die alten Umschreibungsregeln noch bestehen. Das könnte man lösen, indem man bei der Aktualisierung des entsprechenden Meta-Feldes immer noch die zugehörigen alten Umschreibungsregeln entfernt.

Alle anderen Inhalte des Inhaltstyps sind ebenfalls unter den individuellen Titelformen erreichbar. Da sie aber nirgendwo im Quellcode entsprechend hinterlegt sind, noch sich deren kanonische URL verändert, sollte das kein Problem darstellen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert