Für eine bestimmte Bedingung musste ich die Ergebnismenge von WordPress’ get_posts-Funktion anpassen. Da ich ein wenig damit zu kämpfen hatte, möchte ich mit diesem Beitrag sicherstellen, dass das nie wieder passiert.

Was ich erreichen wollte

Da ich alle Beiträge mit einem bestimmten Inhalt (hier Beiträge mit einem bestimmten wiederverwendbaren Block) finden musste, wäre eine individuelle Abfrage viel effizienter und leistungsfähiger als eine Schleife durch alle Beiträge per PHP.

Die individuelle WHERE-Abfrage

Die Anpassung der WHERE-Abfrage war anfangs etwas mühsam, da ich nicht damit vertraut war, sie korrekt als LIKE-Bedingung abzubilden. Nach einer steilen Lernkurve kam ich auf die folgende Methode:

/**
 * Check the post content for a reusable block with a specific ID.
 * 
 * @param	string	$where The current WHERE string
 * @return	string The updated WHERE string
 */
public static function check_post_content_for_reusable_block( string $where ): string {
	global $wpdb;
	
	$extra = $wpdb->esc_like( '<!-- wp:block {"ref":' . (int) self::$reusable_post_id );
	
	return $where . ' AND ' . $wpdb->posts . '.post_content LIKE \'%' . $extra . '%\'';
}
Code-Sprache: PHP (php)

Kurz gesagt: Ich suche nach der Zeichenfolge <!-- wp:block {"ref": nnerhalb des Beitragsinhalts, gefolgt von der tatsächlichen ID des wiederverwendbaren Blocks.

Wo die Probleme begannen

Da ich bereits wusste, dass der posts_where-Filter es mir ermöglichen sollte, die Abfrage meines get_posts genau anzupassen, füge ich einen Filter genau davor ein und entferne ihn direkt danach. Das sah dann so aus:

add_filter( 'posts_where', [ self::class, 'check_post_content_for_reusable_block' ] );

self::$reusable_post_id = $post_id;
// get up to 5 post IDs, which contain this reusable block
$matching_posts_attributes = [
	'post_type' => [
		'page',
		'post',
	],
];
$matching_posts = get_posts( $matching_posts_attributes );

remove_filter( 'posts_where', [ self::class, 'check_post_content_for_reusable_block' ] );
Code-Sprache: PHP (php)

Im Grunde sollte das auch sein, aber es hat nicht funktioniert. Ich erhielt mehr Ergebnisse, als ich wollte, und nicht nur die mit dem gewünschten wiederverwendbaren Block. Bei der weiteren Untersuchung schien er meinen Filter völlig zu ignorieren.

Lektion gelernt

Nach etwa einer Stunde Fehlersuche, in der ich hier und da etwas änderte, fand ich schließlich die Lösung – natürlich in der Dokumentation von get_posts. Standardmäßig verwendet es das Attribut suppress_filters gleich true, was bedeutet, dass es Filter ignoriert wird, einschließlich meines verwendeten posts_where. Als ich dies zu den Attributen hinzufügte, funktionierte es schließlich einwandfrei:

add_filter( 'posts_where', [ self::class, 'check_post_content_for_reusable_block' ] );

self::$reusable_post_id = $post_id;
// get up to 5 post IDs, which contain this reusable block
$matching_posts_attributes = [
	'post_type' => [
		'page',
		'post',
	],
	'suppress_filters' => false,
];
$matching_posts = get_posts( $matching_posts_attributes );

remove_filter( 'posts_where', [ self::class, 'check_post_content_for_reusable_block' ] );
Code-Sprache: PHP (php)

Manchmal kann man durch sorgfältiges Lesen viel Zeit sparen.

Bonus: Mehr Leistung

Je nachdem, was du erreichen möchtest, kannst du die Leistung stark erhöhen. In meinem Fall benötigte ich nur die Beitrags-IDs (um später lediglich die Beitrags-Metainhalte des Ergebnisses zu verändern). Also ergibt ein 'fields' => 'ids' hier durchaus sinn, um nicht für jeden Beitrag ein WP_Post-Objekt zu generieren, sondern nur eine Liste an IDs zurückzugeben.

Insbesondere, wenn du mit einer potenziell großen Datenmenge arbeitest, stelle sicher, dass du so wenig Daten verarbeitest wie möglich.

Schreibe einen Kommentar

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