Das eigene WordPress-Theme erstellen – #17: Die Theme-Widgets

In dieser Artikelreihe geht es darum, ein WordPress-Theme zu erstellen – von Grund auf. In Teil 17 geht es um die zwei Widgets, die das Theme mitbringt.

Im letzten Teil hatte ich geschrieben, dass es ab jetzt um den Customizer gehen würde – dabei habe ich allerdings die zwei Widgets vergessen, die das Theme mitbringt. Die müssen vorher besprochen werden, da auch sie mit Customizer-Einstellungen angepasst werden können.

Das Theme bringt wie gesagt zwei Widgets mit – eins zum Anzeigen der neuesten Galerien und eins zum Hervorheben bestimmter Galerien. Um ein Widget zu erstellen, muss eine Unterklasse von WP_Widget erstellt und darin mindestens die Methoden widget(), update(), form() sowie der Konstruktor genutzt werden. Im Konstruktor werden grundlegende Eigenschaften des Widgets festgelegt, wie dessen Name und Beschreibung. Die widget()-Methode stellt das Widget im Frontend dar, update() kümmert sich um die Aktualisierung der Einstellungen im Backend und form() zeigt das Widget im Widget-Bereich an.

Das Recent-Galleries-Widget

So könnte das Widget für die neuesten Galerien später in der Sidebar aussehen. (Screenshot: eigene Installation; Bilder: Dennis Brinkmann)
So könnte das Widget für die neuesten Galerien später in der Sidebar aussehen. (Screenshot: eigene Installation; Bilder: Dennis Brinkmann)

Für die Klassen erstellen wir jeweils eine eigene Datei innerhabl des inc-Ordners unseres Themes. Das Widget für die neuesten Galerien kommt in die Datei class-recent-galleries.php.

<?php
 
/**
 * Recent_Galleries widget class
 */
class Bornholm_Recent_Galleries extends WP_Widget {
 
    public function __construct() {
        $widget_ops = array(
            'classname'   => 'widget_recent_galleries',
            'description' => _x( 'Your site’s most recent galleries.', 'Description of the recent galleries widget', 'bornholm' )
        );
        parent::__construct( 'recent-galleries', _x( 'Recent Galleries', 'Name of the recent galleries widget', 'bornholm' ), $widget_ops );
    }

Im Code-Block oben ist der Anfang der Klasse Bornholm_Recent_Galleries abgebildet, die die WP_Widget-Klasse erweitert. Über __construct() wird direkt nach Aufrufen der Klasse ein Array mit dem Klassennamen des Widgets sowie der übersetzbaren Beschreibung erstellt. Im Anschluss wird an den Konstruktor der Oberklasse, also WP_Widget, als erster Parameter ein Bezeichner für das Widget übergeben. Es folgt der Name des Widgets sowie die Optionen aus dem Array.

public function widget( $args, $instance ) {
    $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
 
    $number = ( ! empty( $instance['number'] ) ) ? absint( $instance['number'] ) : 2;
    if ( ! $number ) {
        $number = 2;
    }
 
    /**
     * Filter the arguments for the Recent Galleries widget
     *
     * @see WP_Query::get_posts()
     *
     * @param array $args An array of arguments used to retrieve the recent posts.
     */
    $result = new WP_Query( apply_filters( 'widget_galleries_args', array(
        'posts_per_page'      => $number,
        'no_found_rows'       => true,
        'post_status'         => 'publish',
        'ignore_sticky_posts' => true,
        'tax_query'           => array(
            array(
                'taxonomy' => 'post_format',
                'field'    => 'slug',
                'terms'    => 'post-format-gallery'
            )
        )
    ) ) );
 
    if ( $result->have_posts() ) {
        echo $args['before_widget'];
        if ( $title ) {
            echo $args['before_title'] . $title . $args['after_title'];
        }
        while ( $result->have_posts() ) {
            $result->the_post();
            $post   = get_post( get_the_ID() );
            $images = bornholm_get_gallery_images( $post->ID );
            if ( $images ) { ?>
                <div>
                    <?php bornholm_gallery_header( 'h4', $images, 'thumbnail', $post ); ?>
                </div>
            <?php }
        }
        echo $args['after_widget'];
// Reset the global $the_post as this query will have stomped on it
        wp_reset_postdata();
    }
}

Innerhalb der widget()-Methode, der Argumente von der Sidebar sowie die aktuelle Instanz des Widgets übergeben werden, wird zuerst der Titel des Widgets der $title-Variable zugewiesen. Diese Anwendung des Filters ist leicht angepasst aus einem der Core-Widgets übernommen. Falls kein Titel eingetragen wurde, wird ein leerer String gespeichert. Im nächsten Schritt holen wir uns aus $instance['number'] die Anzahl, die angezeigt werden soll. Ist nichts eingetragen, setzen wir den Wert auf 2.

Als nächstes müssen wir uns die neuesten Beiträge mit dem Post-Format „Galerie“ holen, und starten dafür eine neue WP_Query. Innerhalb des Argument-Arrays geben wir für posts_per_page die Anzahl aus $number an. Mit dem Wert true für no_found_rows geben wir an, dass WordPress die gefundenen Datenbankzeilen nicht zählen soll, was die Performance verbessert aber wohl nur funktioniert, wenn keine Pagination eingesetzt wird. Die Beiträge sollen des Weiteren veröffentlicht sein und Sticky-Posts ignoriert werden. Für tax_query geben wir das bereits bekannte Array an, um nur Beiträge des Galerie-Post-Formats zu bekommen.

Wenn wir im Ergebnis Beiträge haben, dann geben wir mit $args['before_widget'] das Markup aus, das in der register_sidebar()-Funktion von der Sidebar, in der das Widget angezeigt wird, für before_widget eingetragen wurde. Das sah beispielsweise für eine Sidebar so aus:

register_sidebar( array(
    'name'          => 'Sidebar',
    'id'            => 'sidebar-1',
    'description'   => '',
    'before_widget' => '<div class="widget clearfix %2$s">',
    'after_widget'  => '</div>',
    'before_title'  => '<h3 class="widget-title">',
    'after_title'   => '</h3>'
) );

Wenn ein Titel vergeben wurde, umschließen wir ihn mit dem Markup aus der entsprechenden register_sidebar()-Funktion und geben ihn aus. Danach starten wir eine Schleife und holen uns die ID des aktuellen Beitrags. Damit können wir uns die Bilder aus der Funktion bornholm_get_gallery_images() holen und das erste mit dem Titel durch die ebenfalls bereits besprochene Funktion bornholm_gallery_header() ausgeben. Nach der Schleife wird das Markup für nach dem Widget ausgegeben und die Postdaten zurückgesetzt.

public function update( $new_instance, $old_instance ) {
    $instance           = $old_instance;
    $instance['title']  = strip_tags( $new_instance['title'] );
    $instance['number'] = (int) $new_instance['number'];
 
    return $instance;
}

Die update()-Methode ist relativ kurz. Hier werden nach dem Speichern des Nutzers im Backend die alte Widget-Werte gegen die Neuen ausgetauscht. Als Parameter bekommt die Methode die neue und alte Instanz des Widgets übergeben. Zuerst wird der Variable $instance die alte Instanz zugewiesen. Anschließend werden die Schlüssel des Arrays mit den neuen Werten überschrieben, für die gleichzeitig sichergestellt wird, dass sie einen sinnvollen Wert enthalten. Danach wird die neue Instanz zurückgegeben.

public function form( $instance ) {
    $title  = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : '';
    $number = isset( $instance['number'] ) ? absint( $instance['number'] ) : 2;
    ?>
    <p><label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:', 'bornholm' ); ?></label>
        <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>"
            name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo $title; ?>"/>
    </p>
 
    <p><label
            for="<?php echo $this->get_field_id( 'number' ); ?>"><?php _e( 'Number of galleries to show:', 'bornholm' ); ?></label>
        <input class="widefat" id="<?php echo $this->get_field_id( 'number' ); ?>"
            name="<?php echo $this->get_field_name( 'number' ); ?>" type="number"
            value="<?php echo $number; ?>"/></p>
    <?php
    }
}

Mit form() wird das Formular ausgegeben, das im Backend angezeigt wird. Als Parameter bekommt es die aktuelle Instanz des Widgets. Zuerst wird geprüft, ob Titel und Anzahl gesetzt sind, sonst werden die Standard-Werte eingesetzt. Anschließend wird für den Titel ein einfaches Textfeld erstellt. Um das id-Attribut und somit auch das for-Attribut des label-Elements zu erstellen, soll die get_field_id()-Methode eingesetzt werden, der der Name des Feldes übergeben wird. Über diesen Namen kann in den anderen Methoden auf den Wert zugegriffen werden, beispielsweise in update() mit $new_instance['title'].

Um das name-Attribut zu erstellen, soll die Methode get_field_name() genutzt werden. Die widefat-Klasse wird von WordPress für Widgets genutzt und damit gleich gestylt. Als value geben wir den eventuell eingetragenen Titel aus. Nach demselben Prinzip wird für das Eingabefeld der Anzahl vorgegangen, nur dass hier als type der Wert number angegeben wird. Anschließend ist das Widget fertig und kann genutzt werden.

Das Featured-Galleries-Widget

Das Featured-Galleries-Widget auf der Demo-Site des WordPress-Themes. (Screenshot: eigene Installation; Bilder: Dennis Brinkmann)
Das Featured-Galleries-Widget auf der Demo-Site des WordPress-Themes. (Screenshot: eigene Installation; Bilder: Dennis Brinkmann)

Um bestimmte Galerien in der Sidebar anzeigen zu können, wird der Nutzer die IDs der jeweiligen Beiträge eintragen müssen. Das ist zwar nicht die allerschönste Lösung, damit kann der Nutzer aber unter anderem auch selbst über die Sortierung der Ausgabe entscheiden. Das Widget gehört in die class-featured-galleries.php im inc-Ordner und fängt so an:

<?php
 
/**
 * Featured_Galleries widget class
 */
class Bornholm_Featured_Galleries extends WP_Widget {
 
    public function __construct() {
        $widget_ops = array(
            'classname'   => 'widget_featured_galleries',
            'description' => _x( 'Enter ids of galleries you want to feature.', 'Description of the featured galleries widget', 'bornholm' )
        );
        parent::__construct( 'featured-galleries', _x( 'Featured Galleries', 'Name of the featured galleries widget', 'bornholm' ), $widget_ops );
    }

Der Konstruktor kümmert sich wieder um den Klassennamen, die Beschreibung sowie den Titel – nichts Neues also.

public function widget( $args, $instance ) {
    $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
 
    $gallery_ids = ! empty( $instance['gallery_ids'] ) ? explode( ',', $instance['gallery_ids'] ) : array();
    $counter     = 0;
    foreach ( $gallery_ids as $gallery_id ) {
        $post   = get_post( $gallery_id );
        $format = get_post_format( $gallery_id );
        if ( $post && $format == 'gallery' ) {
            if ( $counter == 0 ) {
                echo $args['before_widget'];
                if ( $title ) {
                    echo $args['before_title'] . $title . $args['after_title'];
                }
            }
            $images = bornholm_get_gallery_images( $gallery_id ); ?>
            <div>
                <?php bornholm_gallery_header( 'h4', $images, 'thumbnail', $post ); ?>
            </div>
            <?php $counter ++;
        }
    }
    if ( $counter != 0 ) {
        echo $args['after_widget'];
    }
}

Erneut wird in widget() zuerst der Titel ermittelt. Anschließend werden die eingegebenen IDs aus dem Widget geholt. Da wir diese getrennt als Array benötigen und die IDs im Eingabefeld durch Komma getrennt werden sollen, müssen wir sie mit explode() an den Kommata als Trennzeichen trennen. Wenn das Feld leer ist, dann wird ein leeres Array gespeichert. Danach legen wir einen Counter an und starten eine Schleife, die die Beitrags-IDs durchläuft. Darin holen wir uns zuerst das Post-Objekt mit get_post() sowie dessen Post-Format. Wenn ein Post zurückgegeben wurde und er dem richtigen Post-Format entspricht, müssen wir prüfen, ob das der erste Schleifendurchlauf ist. Wenn dem so ist, geben wir das Markup für vor dem Widget sowie den Titel aus.

Danach holen wir uns mit bornholm_get_gallery_images() die Bilder des Beitrags und geben das erste innerhalb eines div-Elements aus, indem wir bornholm_gallery_header() aufrufen – danach erhöhen wir den Counter. Wenn die Schleife komplett abgearbeitet wurde und der Counter nicht mehr den Wert 0 hat, dann geben wir das Markup aus, das nach dem Widget angezeigt werden soll.

public function update( $new_instance, $old_instance ) {
    $instance                = $old_instance;
    $instance['title']       = strip_tags( $new_instance['title'] );
    $instance['gallery_ids'] = strip_tags( $new_instance['gallery_ids'] );
 
    return $instance;
}

Die update()-Methode gleicht der aus dem anderen Widget. Bei den Galerie-IDs könnte man hier noch genauer prüfen, ob die Eingabe dem Muster aus Zahlen und Kommata entspricht, die man erwartet, aber uns reicht das in dem Fall so.

public function form( $instance ) {
    $title       = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : '';
    $gallery_ids = isset( $instance['gallery_ids'] ) ? esc_attr( $instance['gallery_ids'] ) : '';
    ?>
    <p><label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:', 'bornholm' ); ?></label>
        <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>"
            name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo $title; ?>"/>
    </p>
 
    <p><label
            for="<?php echo $this->get_field_id( 'gallery_ids' ); ?>"><?php _e( 'Comma seperated ids of galleries you want to feature:', 'bornholm' ); ?></label>
        <input class="widefat" placeholder="523, 547" id="<?php echo $this->get_field_id( 'gallery_ids' ); ?>"
            name="<?php echo $this->get_field_name( 'gallery_ids' ); ?>" type="text"
            value="<?php echo $gallery_ids; ?>"/></p>
    <?php
    }
}

Auch die form()-Methode bietet nicht wirklich Neues. Zuerst werden die beiden Werte ermittelt und gegebenenfalls auf den leeren Standard gesetzt. Im Anschluss werden wieder die beiden Felder erstellt. Natürlich muss sich ein Widget nicht auf zwei Felder beschränken – ihr könnt so viele einsetzen, wie ihr wollt.

Das wäre es auch schon – jetzt hat unser Theme zwei funktionierende Widgets, die der Nutzer in Sidebars einsetzen kann. Ich bin nicht hundertprozentig sicher, aber ich glaube, dass es im nächsten Teil um den Customizer gehen wird 🙂

Update vom 28. Dezember 2015: Ein kleiner Code-Teil wurde in diesem Teil der Reihe vergessen, weshalb die Widgets so noch nicht funktionieren – der übrige Code ist am Anfang des nächsten Teils Thema.

Der Code auf GitHub

Den Code gibt es im Repo auf GitHub. Den aktuellen Stand nach diesem Artikel zeigt Tag „v0.15“.

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.