Das eigene WordPress-Theme erstellen – #9: Die Einzelansicht der Beiträge und Galerien in der single.php

In dieser Serie geht es darum, ein WordPress-Theme zu erstellen – von Grund auf. Der neunte Teil beschäftigt sich mit der single.php, die für die Anzeige eines einzelnen Beitrags zuständig ist.

Bisher können wir die Anzeige eines einzelnen Beitrags noch nicht von der Übersichtsseite unterscheiden. Dafür gibt es die single.php-Datei. Diese Template-Datei wird von WordPress aufgerufen, wenn ein einzelner Beitrag angezeigt wird. Hier nehmen wir auch gleich die Anpassungen für die Galerie-Ansicht vor, die ein großes Featured Image im Header des Beitrags anzeigen soll. Wie das aussieht, seht ihr im folgenden Screenshot. Oben ist die Einzelansicht einer Galerie abgebildet, unten die Einzelansicht eines ganz normalen Beitrags.

Die zwei verschiedenen Anzeigemöglichkeiten in der single.php des WordPress-Themes: Oben eine Galerie, unten ein normaler Artikel. (Screenshot: eigene WordPress-Installation; Fotos: Dennis Brinkmann)
Die zwei verschiedenen Anzeigemöglichkeiten in der single.php des WordPress-Themes: Oben eine Galerie, unten ein normaler Artikel. (Screenshot: eigene WordPress-Installation; Fotos: Dennis Brinkmann)

Da die single.php durch die Einrückungen hier im Gesamten nur schwer lesbar wäre, poste ich sie gleich stückweise. Den ersten Teil kennen wir schon aus dem Artikel mit der index.php. Wir binden die header.php ein und starten den Loop.

<?php get_header(); ?>
    <main role="main">
        <?php
        while ( have_posts() ) {
            the_post();

Nun müssen wir für die veränderte Anzeige im Header des Beitrags rausfinden, ob es sich um ein Galerie-Post-Format handelt. Dafür gibt es die get_post_format()-Funktion, der die ID des Beitrags übergeben werden muss. Die ID ist im $post-Objekt gespeichert. Wir sichern also zuerst das Ergebnis dieser Abfrage in einer Variable und führen dann im header-Element die Fallunterscheidung durch:

$format = get_post_format( $post->ID ); ?>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
    <header class="entry-header clearfix">
        <?php if ( $format == 'gallery' ) {
            $images = bornholm_get_gallery_images( $post->ID );
            bornholm_gallery_header( 'h1', $images, 'bornholm_large_gallery_image_for_single_view', $post );
        } else {
            bornholm_the_post_header( 'h1', $post );
        } ?>
    </header>

Die Funktion von the_ID() und post_class() haben wir schon im Beitrag zur content.php besprochen. Innerhalb des header-Elements prüfen wir, ob das Ergebnis des get_post_format()-Aufrufs dem Wert gallery entspricht. Wenn das der Fall ist, müssen wir uns die Bilder aus der Galerie besorgen und das erste davon im Header anzeigen. Andernfalls geben wir den normalen Header mit der bornholm_the_post_header()-Funktion aus, die ebenfalls schon in dem Teil mit der content.php erläutert wurde.

Beschäftigen wir uns also näher mit dem Fall, dass eine Galerie angezeigt wird. Wir wollen die Galeriebilder in der $images-Variable speichern und rufen dafür die bornholm_get_gallery_images()-Funktion auf, der wir die ID des Beitrags übergeben. An dieser Stelle vielen Dank an Thomas Scholz, der mir den Großteil dieser Funktion geschrieben und mir bei der Überarbeitung des Theme-Codes einige hilfreiche Tipps gegeben hat. Diese Funktion kommt in die functions.php und sieht wie folgt aus:

/**
 * Fetch image post objects for all gallery images in a post.
 *
 * @param $post_id
 *
 * @return array
 */
function bornholm_get_gallery_images( $post_id ) {
 
    $post = get_post( $post_id );
 
    // Den Beitrag gibt es nicht, oder er ist leer.
    if ( ! $post || empty ( $post->post_content ) ) {
        return array();
    }
 
    $galleries = get_post_galleries( $post, false );
    if ( empty ( $galleries ) ) {
        return array();
    }
    $ids = array();
    foreach ( $galleries as $gallery ) {
        if ( ! empty ( $gallery['ids'] ) ) {
            $ids = array_merge( $ids, explode( ',', $gallery['ids'] ) );
        }
    }
    $ids = array_unique( $ids );
    if ( empty ( $ids ) ) {
        $attachments = get_children( array(
            'post_parent'    => $post_id,
            'post_status'    => 'inherit',
            'post_type'      => 'attachment',
            'post_mime_type' => 'image',
            'order'          => 'ASC',
            'orderby'        => 'menu_order',
        ) );
        if ( empty ( $attachments ) ) {
            return array();
        }
    }
 
    $images = get_posts(
        array(
            'post_type'      => 'attachment',
            'post_mime_type' => 'image',
            'orderby'        => 'post__in',
            'numberposts'    => 999,
            'include'        => $ids
        )
    );
    if ( ! $images && ! $attachments ) {
        return array();
    } elseif ( ! $images ) {
	    $images = $attachments;
    }
 
    return $images;
}

Zuerst holen wir uns mit der get_post()-Funktion das $post-Objekt. Dadurch können wir nun prüfen, ob es den Beitrag überhaupt gibt oder ob er leer ist. In beiden Fällen beenden wir den Durchlauf der Funktion und geben ein leeres Array zurück. Anschließend wollen wir die Galerien des Beitrags ermitteln. Dafür bietet WordPress uns die get_post_galleries()-Funktion, die als ersten Parameter das Post-Objekt erwartet. Der zweite Parameter muss ein boolescher Wert sein und gibt an, ob die Rückgabe als HTML oder als Array erfolgen soll. Da wir für die weitere Verarbeitung ein Array benötigen, übergeben wir hier false.

Im nächsten Schritt prüfen wir, ob es Galerien in dem Beitrag gibt und geben andernfalls wieder ein leeres Array zurück. Jetzt brauchen wir die IDs von den Bildern in einem Array. Das Ergebnis-Array aus dem get_post_galleries()-Aufruf sieht aber bei zwei Galerien in einem Beitrag beispielhaft so aus:

Array ( 
    [0] => Array ( 
        [ids] => 769,768 
        [src] => Array ( 
            [0] => http://example.com/2015/10/20/img.jpg 
            [1] => http://example.com/2015/10/20/img-1.jpg 
        ) 
    )
    [1] => Array ( 
        [ids] => 456,345
        [src] => Array ( 
            [0] => http://example.com/2015/10/20/img-2.jpg 
            [1] => http://example.com/2015/10/20/img-3.jpg 
        ) 
    ) 
)

Wir müssen also die IDs aus dem ids-Eintrag des Arrays bekommen. Da ja mehrere Galerien in einem Beitrag verwendet werden können, müssen wir die einzelnen Ergebnisse zu einem langen ID-Array zusammensetzen. Das ist Aufgabe dieses Code-Teils:

$ids = array();
foreach ( $galleries as $gallery ) {
    if ( ! empty ( $gallery['ids'] ) ) {
        $ids = array_merge( $ids, explode( ',', $gallery['ids'] ) );
    }
}
$ids = array_unique( $ids );

Wir erstellen zuerst ein leeres Array und speichern es in der $ids-Variable. Anschließend durchlaufen wir in einer foreach-Schleife alle Galerien aus dem Beitrag und prüfen darin, ob wir überhaupt IDs in dem Array haben. Ist das der Fall, müssen wir gedanklich die nächste Zeile von innen nach außen durchgehen. Damit haben wir zunächst diesen Teil:

explode( ',', $gallery['ids'] )

Dadurch werden die Werte vom Array-Schlüssel $ids an dem übergebenen Trennzeichen , getrennt und als Array zurückgegeben. Das sieht für die erste Galerie aus unserem Beispiel so aus:

Array ( 
    [0] => 769
    [1] => 768
)

Dieses Array hängen wir mit der array_merge()-Funktion an das $ids-Array an. Hier wird also das lange Array von IDs gebildet. Sind alle Galerien abgearbeitet, werden mit der array_unique()-Funktion doppelte Werte aus dem Array entfernt.

Nun gibt es zwei Varianten, wie der gallery-Shortcode in WordPress eingesetzt werden kann. Einmal mit übergebenen IDs der Bilder und einmal ohne. Wenn keine IDs angegeben werden, zeigt der Shortcode einfach alle Bilder an, die zu dem Beitrag hochgeladen wurden. In diesem Fall haben wir zwar keine IDs aus dem foreach-Durchlauf, wir wollen aber natürlich trotzdem ein Bild als Titelbild anzeigen.

Wenn also die $ids-Variable an dieser Stelle der Funktion leer ist, wurde ein gallery-Shortcode ohne IDs eingesetzt. In diesem Fall kommt folgender Code zum Einsatz:

if ( empty ( $ids ) ) {
    $attachments = get_children( array(
        'post_parent'    => $post_id,
        'post_status'    => 'inherit',
        'post_type'      => 'attachment',
        'post_mime_type' => 'image',
        'order'          => 'ASC',
        'orderby'        => 'menu_order',
    ) );
    if ( empty ( $attachments ) ) {
        return array();
    }
}

Mit der get_children()-Funktion können wir die Anhänge eines Beitrag ermitteln – also alle Medien, die in einem Beitrag über die Schaltfläche „Dateien hinzufügen“ hochgeladen wurden. Um die Dateien für den richtigen Beitrag zu bekommen, übergeben wir in dem Parameter-Array für den Schlüssel post_parent die ID des Beitrags. Als post_status geben wir inherit an, was dann dem Post-Status des Beitrags entspricht. Der Post-Type soll einem Anhang entsprechen und der Typ des Anhangs soll image sein. Das Ganze soll aufsteigend nach der Menü-Reihenfolge sortiert werden – das entspricht in diesem Fall der Reihenfolge des Hochladens.

Wenn das Ergebnis des Aufrufs leer ist, geben wir erneut ein leeres Array zurück. Für den Normalfall, dass ein gallery-Shortcode mit IDs der Bilder eingesetzt wird, nutzen wir den folgenden Code-Schnipsel, um die Post-Objekte der Bilder zu erhalten:

$images = get_posts(
    array(
        'post_type'      => 'attachment',
        'post_mime_type' => 'image',
        'orderby'        => 'post__in',
        'numberposts'    => 999,
        'include'        => $ids
    )
);

Wir rufen die get_posts()-Funktion auf und möchten nur Objekte, die von Typ attachment und gleichzeitig ein Bild sind. Darüber hinaus sollen sie so sortiert werden, wie sie in dem Array $ids vorkommen, das wir dem include-Schlüssel übergeben. Kurz gesagt bekommen wir hier als Ergebnis ein Array mit den Post-Objekten der Bilder, die zu den ermittelten IDs aus den gallery-Shortcodes gehören.

Abschließend prüfen wir, ob beide Aufrufe kein Ergebnis geliefert haben und geben in dem Fall ein leeres Array zurück. Wenn hingegen nur der Aufruf der get_posts()-Funktion leer ist, weisen wir der $images-Variable das Ergebnis des get_children()-Aufrufs zu. Andernfalls behält die $images-Variable den Wert des get_posts()-Aufrufs. Danach geben wir das Ergebnis zurück:

if ( ! $images && ! $attachments ) {
    return array();
} elseif ( ! $images ) {
    $images = $attachments;
}
 
return $images;

Den Galerie-Header ausgeben

Nach dieser komplexeren Funktion müssen wir nun in die single.php zurück. Wir waren bei dieser Stelle im header-Element stehengeblieben:

if ( $format == 'gallery' ) {
    $images = bornholm_get_gallery_images( $post->ID );
    bornholm_gallery_header( 'h1', $images, 'bornholm_large_gallery_image_for_single_view', $post );

In der $images-Variable befinden sich jetzt die Post-Objekte der Galeriebilder. Um den Header der Galerie auszugeben, rufen wir die bornholm_gallery_header()-Funktion auf. Der erste Parameter ist die Ebene der Überschrift, der zweite die Variable mit den Post-Objekten der Bilder, der dritte die Größe des Bildes, das wir ausgeben möchten, und der vierte das Post-Objekt des Beitrags.

Da diese Bildergröße keiner Standardgröße wie etwa large entsprechen soll, müssen wir die erst in der functions.php anlegen, bevor wir uns der bornholm_gallery_header()-Funktion widmen:

add_image_size( 'bornholm_large_gallery_image_for_single_view', 1592, 9999, false );

Das wars schon. Wir übergeben als ersten Parameter an die add_image_size()-Funktion den eindeutigen Bezeichner, anschließend die maximale Breite und Höhe und zum Schluss, ob das Bild auf diese Werte zugeschnitten werden soll. Um diese Größe für bereits hochgeladene Bilder zu erzeugen, müssen die Thumbnails neu erzeugt werden, beispielsweise mit dem Regenerate-Thumbnails-Plugin.

Nun aber zu der Funktion für den Galerie-Header:

/**
 * Displays the header of a gallery.
 * If there are $images, the function displays the title with an image.
 * If not, only the title is displayed.
 *
 * @param $heading, $images, $size
 *
 * @return string Formatted output in HTML
 */
function bornholm_gallery_header( $heading, $images, $size, $post ) {
    if ( $images ) {
        bornholm_gallery_title( $heading, $images, $size, $post );
    } else {
        bornholm_post_title( $heading, $post );
    }
}

Hier müssen wir wieder testen, ob in der $images-Variable überhaupt Bilder enthalten sind oder nicht. Wenn wir Bilder haben, rufen wir die bornholm_gallery_title()-Funktion auf und geben die bekannten Parameter weiter. Gibt es keine Bilder, wird der Beitragstitel mit der bornholm_post_title()-Funktion ausgegeben, die wir im Teil mit der content.php besprochen haben.

Die bornholm_gallery_title()-Funktion sieht folgendermaßen aus:

/**
 * Displays the title of a gallery with an image.
 *
 * @param $heading, $images, $size
 *
 * @return string Formatted output in HTML
 */
function bornholm_gallery_title( $heading, $images, $size, $post ) {
    if ( $size != 'bornholm_large_gallery_image_for_single_view' ) { ?>
        <a href="<?php echo esc_url( get_permalink( $post->ID ) ); ?>">
    <?php }
    if ( ! $heading == '' ) {
        echo '<' . $heading . ' class="entry-title gallery-title">';
        echo esc_html( $post->post_title );
        echo '</' . $heading . '>';
    }
    bornholm_gallery_featured_image( $size, $images, $post );
    if ( $size != 'bornholm_large_gallery_image_for_single_view' ) { ?>
        </a>
    <?php }
}

Wir prüfen zuerst, ob die Größe des Bildes der entspricht, die für die Einzelansicht genutzt wird. Ist das nicht der Fall, geben wir einen Link zu dem Beitrag aus. Anschließend setzen wir das Überschriften-Element zusammen und geben darin den Titel des Beitrags aus. Um das Bild anzuzeigen, rufen wir die bornholm_gallery_featured_image()-Funktion auf, an die wir die Größe des Bildes, die Variable mit den Bildern und das Post-Objekt übergeben.

/**
 * Displays the featured image of a gallery
 *
 * @param $size, $images
 *
 * @return string Formatted output in HTML
 */
function bornholm_gallery_featured_image( $size, $images, $post ) {
    $image         = array_shift( $images );
    $image_img_tag = wp_get_attachment_image( $image->ID, $size ); ?>
    <figure class="gallery-thumb clearfix">
        <?php if ( has_post_thumbnail( $post->ID ) ) {
            echo get_the_post_thumbnail( $post->ID, $size );
        } else {
            echo $image_img_tag;
        } ?>
    </figure>
<?php
}

Da wir nur das erste Bild benötigen, speichern wir den ersten Eintrag des $images-Arrays in der $image-Varible. Im nächsten Schritt holen wir uns das img-Element dieses ersten Bildes durch den Aufruf der wp_get_attachment_image()-Funktion. Neben der ID des Bildes muss noch die Größe übergeben werden, in der das Bild ausgegeben werden soll.

Um dem Beitragsautoren die Möglichkeit zu geben, im Header ein anderes Bild als das erste Galerie-Bild anzuzeigen, prüfen wir innerhalb des figure-Elements zuerst, ob ein Beitragsbild gesetzt ist und geben es aus. Auch hier übergeben wir als zweiten Parameter an die get_the_post_thumbnail()-Funktion wieder unsere eigene Bildgröße. Wenn kein Beitragsbild festgelegt ist, geben wir das Ergebnis des wp_get_attachment_image()-Aufrufs aus.

Damit haben wir den Header in der single.php abgeschlossen und können uns dem nächsten Teil widmen:

    <div class="entry-content">
        <?php the_content();
        bornholm_paginated_posts_navigation(); ?>
    </div>
    <footer class="entry-meta">
        <?php bornholm_footer_meta(); ?>
    </footer>
</article><!-- #post-<?php the_ID(); ?> -->

Wir geben mit der the_content()-Funktion den Inhalt des Beitrags aus und rufen anschließend die Funktion auf, die die Pagination von Beiträgen mit dem <--nextpage-->-Kommentar ermöglicht. Diese Funktion kommt wieder in die functions.php und sieht so aus:

/**
 * Displays navigation for paginated posts
 *
 * @return string Formatted output in HTML.
 */
function bornholm_paginated_posts_navigation() {
    wp_link_pages( array(
        'before'           => '<ul class="page-link">',
        'after'            => '</ul>',
        'link_before'      => '<li>',
        'link_after'       => '</li>',
    ) );
}

Mit der wp_link_pages()-Funktion kann in WordPress eine Pagination ausgegeben werden. Als Parameter übergeben wir die Code-Schnipsel, die vor und hinter der Pagination auftauchen sowie die Teile, die jeden Link umschließen. Die bornholm_footer_meta()-Funktion haben wir bereits im fünften Teil der Reihe besprochen.

Im nächsten Code-Teil geben wir, falls es sich um eine Galerie handelt, eine Sidebar aus:

<?php if ( $format == 'gallery' ) {
    get_sidebar( 'gallery' );
}

Die entsprechende sidebar-gallery.php legen wir erst mal nur an – die werden wir in einem späteren Teil besprechen. Der letzte Teil der single.php sieht folgendermaßen aus:

            comments_template( '', true );
        } ?>
    </main>
<?php if ( $format == 'gallery' ) {
} else {
    get_sidebar();
}
get_footer();

Mit der comments_template()-Funktion lässt sich der Kommentarbereich einbinden. Mit dem ersten Parameter kann ein alternativer Pfad zu der Datei übergeben werden, die die Kommentare handhabt. Der zweite Parameter ermöglicht die Trennung der verschiedenen Kommentar-Typen. Die Standard-Datei für Kommentare in einem Theme ist die comments.php. Wenn nicht vorhanden, wird die von dem Standard-Theme geladen. Wir werden diese Datei erst mal nicht anlegen und uns darum ebenfalls in einem späteren Teil kümmern.

Wenn der Beitrag nicht vom Typ gallery ist, wird vor dem Footer noch die normale Sidebar eingebunden.

Der Theme-Code auf GitHub

Schon fast obligatorisch am Ende der Hinweis, dass ihr den Theme-Code auf GitHub finden könnt. Der Stand nach diesem Teil ist in Tag „v0.7“ festgehalten.

Die weiteren Teile meiner WordPress-Reihe:

Dieser Beitrag ist eine Übernahme meines Beitrags für t3n.de.

Das könnte auch interessant sein

Schreib einen Kommentar

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