Bestimmte Gutenberg-Blöcke eines Beitrags im Theme an anderer Stelle ausgeben

Manchmal kann es gewollt sein, bestimmte Inhalte eines Beitrags oder einer Seite an anderer Stelle auszugeben als es WordPress standardmäßig vorsieht, zum Beispiel einen Slider über dem Titel eines Beitrags. Hier zeige ich euch, wie ihr die Gutenberg-Blöcke eines Beitrags im Theme Block für Block durchlaufen könnt, um das zu bewerkstelligen.

Update vom 9. Januar 2020: Jeslen hat unter der englischen Version des Beitrags darauf hingewiesen, dass der wpautop-Filter nicht über unser Content-Markup laufen sollte. Andernfalls könnten wir unerwartete Absatz-Elemente in unserer Ausgabe erhalten. Ich habe den Code entsprechend angepasst.

Normalerweise wird der Inhalt in einem Theme über the_content() ausgegeben. Hilfreicher ist für uns hier aber get_the_content(), um den Beitrags- oder Seiteninhalt inklusive der Gutenberg-Blockkommentare zu bekommen, wie er in der Datenbank steht.

Seit WordPress 5.0 wird auf diesen Inhalt der Filter do_blocks angewandt, der in 5.0.3 so aussieht:

/** * Parses dynamic blocks out of `post_content` and re-renders them. * * @since 5.0.0 * @global WP_Post $post The post to edit. * * @param string $content Post content. * @return string Updated post content. */ function do_blocks( $content ) { // If there are blocks in this content, we shouldn't run wpautop() on it later. $priority = has_filter( 'the_content', 'wpautop' ); if ( false !== $priority && doing_filter( 'the_content' ) && has_blocks( $content ) ) { remove_filter( 'the_content', 'wpautop', $priority ); add_filter( 'the_content', '_restore_wpautop_hook', $priority + 1 ); } $blocks = parse_blocks( $content ); $output = ''; foreach ( $blocks as $block ) { $output .= render_block( $block ); } return $output; }
Code-Sprache: PHP (php)

Man kann sehen, wie eine parse_blocks()-Funktion aufgerufen wird, die in $blocks die einzelnen Blöcke speichert, sodass man sie im Anschluss in einer Schleife durchlaufen kann. Und genau diesen Teil stellen wir nach.

Die Inhaltsblöcke als Array erhalten

In einem Theme können wir über folgende Zeile die Blöcke eines Posts als Array bekommen:

$blocks = parse_blocks( get_the_content() );
Code-Sprache: PHP (php)

Für einen Absatz sieht der Array-Eintrag dann beispielsweise so aus:

2 => array (size=5) 'blockName' => string 'core/paragraph' (length=14) 'attrs' => array (size=0) empty 'innerBlocks' => array (size=0) empty 'innerHTML' => string '<p>Ein Absatz</p>' (length=19) 'innerContent' => array (size=1) 0 => string '<p>Ein Absatz</p>' (length=19)
Code-Sprache: PHP (php)

Der Eintrag enthält verschiedene Informationen zum Block, wie etwa den Namen und den Inhalt.

Den Slider-Block getrennt vom restlichen Inhalt ausgeben

Um jetzt einen Slider aus dem Inhalt über dem Beitragstitel auszugeben, können wir wie folgt vorgehen:

// Get post content to extract slider shortcode. $blocks = parse_blocks( get_the_content() ); foreach ( $blocks as $block ) { if ( 'soliloquy/soliloquywp' === $block['blockName'] ) { echo do_shortcode( $block['innerHTML'] ); break; } } // Display the title.
Code-Sprache: PHP (php)

In dem Projekt, bei dem ich dieses Verhalten umsetzen musste, kam der Soliloquy-Slider zum Einsatz, der inzwischen auch einen Block für den neuen Editor anbietet (in der Datenbank wird dann aber weiterhin ein Shortcode gespeichert). Dieser Block hat den Bezeichner soliloquy/soliloquywp, weshalb wir innerhalb der foreach-Schleife auf diesen Namen prüfen.

Wenn wir ihn gefunden haben, geben wir den Shortcode aus $block['innerHTML'] aus und beenden die Schleife. Danach können wir den Titel des Beitrags anzeigen.

Den übrigen Inhalt ausgeben

Jetzt müssen wir nur noch den restlichen Inhalt ausgeben:

$content_markup = ''; foreach ( $blocks as $block ) { if ( 'soliloquy/soliloquywp' === $block['blockName'] ) { continue; } else { $content_markup .= render_block( $block ); } } // Remove wpautop filter so we do not get paragraphs for two line breaks in the content. $priority = has_filter( 'the_content', 'wpautop' ); if ( false !== $priority ) { remove_filter( 'the_content', 'wpautop', $priority ); } echo apply_filters( 'the_content', $content_markup ); if ( false !== $priority ) { add_filter( 'the_content', 'wpautop', $priority ); }
Code-Sprache: PHP (php)

Wir durchlaufen wieder das $blocks-Array und testen erneut auf den Slider-Block, um ihn dieses mal zu überspringen. Bei allen anderen Blöcken fügen wir das Ergebnis von render_block( $block ) an eine Variable an. Diese Funktion gibt für einen Block den entsprechenden HTML-String zurück.

Um Dinge wie Shortcodes und Embeds aufzulösen, führen wir am Ende vor der Ausgabe des $content_markup noch die Filter aus, die WordPress standardmäßig über the_content laufen lässt.

Diese Lösung hat sicher noch ein bisschen Optimierungspotenzial, so könnte man die Core-Filter entfernen, damit im Hintergrund nicht zweimal die Blöcke geparst werden. Wenn ihr – wie in meinem Fall – diese spezielle Block-Behandlung aber nur für bestimmte Ansichten haben möchtet, und ansonsten auf the_content() zurückgreift, müsstet ihr auch diese Vorkommen dann entsprechend anpassen.

Wenn ihr nicht gerade eine Unmenge an Blöcken in euren Beiträgen habt, dürfte der Performance-Verlust durch die doppelte Ausführung der Block-Parser-Logik vermutlich nicht sonderlich ins Gewicht fallen.

4 Reaktionen zu »Bestimmte Gutenberg-Blöcke eines Beitrags im Theme an anderer Stelle ausgeben«

  1. Vielen Dank für den Beitrag, hat mir gerade sehr geholfen.

    Ich habe bei mir die do_shortcode( $block['innerHTML'] ); Funktion ersetzt durch render_block($block );

    Dann funktioniert es auch mit verschachtelten Blöcken wie dem neuen Gruppen-Block, der selbst kein "innerHTML" besitzt.

  2. Guter Beitrag, vielen Dank, hat auch mir sehr geholfen!

    Funktionierte leider nicht ganz - wahrscheinlich wurde an Gutenberg mittlerweile einiges geändert. So ging es -- zumindest bei mir:

    $blocks = parse_blocks( get_the_content() );
    foreach ( $blocks as $block ) {
    if ( $block['blockName'] === 'acf/testimonial' ) {
    echo render_block( $block );
    }
    }

    Um den Custom ACF Block an anderer Stelle auszuschließen, einfach !== anstelle von ===.

Reposts

Schreibe einen Kommentar

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