toscho.design

WordPress: Automatisch Links ins Menü einbauen

Wollen wir einem Navigationsmenü einen Link hinzufügen, so bietet sich uns der Filter wp_nav_menu_objects an. Er wird von der Funktion wp_nav_menu() gestellt, unmittelbar bevor der Walker auf die Links zugreift und sie formatiert.
Wir bekommen zwei Argumente: $sorted_menu_items und $args. Das erste ist ein Array der Links, das zweite enthält die Argumente, mit denen wp_nav_menu() aufgerufen wurde. Diese Argumente können wir um eigene erweitern.

Zwei Beispiele sollen das verdeutlichen.

An- und Abmeldelink einbauen

Aufgabe: Wir wollen einen Link zum An- oder Abmelden ans Menü anhängen. Allerdings nicht an alle Menüs, sondern nur an ein ausgewähltes. Die ursprüngliche Frage hierzu steht auf WordPress Stack Exchange.

Denken wir uns zunächst ein zusätzliches Argument für das Menü aus, mit dem wir es als erweiterbar kennzeichnen:

// If there is no menu assigned we show nothing.
has_nav_menu( 'top-menu' ) and wp_nav_menu(
	array (
	'theme_location' => 'top-menu',
	'add_loginout'   => TRUE
	)
);

Hier rufen wir ein Menü auf, das wir in der functions.php etwa so registriert haben:

register_nav_menu(
	'top-menu',
	__( 'Menu at the top of the page.', 't5_theme' )
);

Der Parameter add_loginout sei unser Kennzeichen dafür, daß wir einen speziellen Link haben möchten. Dazu registrieren wir eine Filterfunktion, die mit normaler Priorität 10 läuft und zwei Argumente erwartet:

add_filter( 'wp_nav_menu_objects', 't5_menu_log_link', 10, 2 );

In der Funktion t5_menu_log_link() prüfen wir, ob das Argument add_loginout gesetzt wurde und ob es zu TRUE evaluiert (empty erledigt beides auf einmal). Je nach Status des aktuellen Besuchers hängen wir dann einen passenden Link an. Dieser Link ist eine Objektinstanz der Klasse stdClass mit bestimmten notwendigen Eigenschaften.

/**
 * Add a link to the nav menu.
 *
 * @wp-hook wp_nav_menu_objects
 * @param  array  $sorted_menu_items Existing nav menu items
 * @param  object $args Nav menu arguments. 'add_loginout' must be TRUE
 * @return array  Nav menu items
 */
function t5_menu_log_link( $sorted_menu_items, $args )
{
    // Parameter missing or set to FALSE
    if ( empty ( $args->add_loginout ) )
    {
        return $sorted_menu_items;
    }

    $is_in                  = is_user_logged_in();

    $link                   = new stdClass;
    $link->title            = __( $is_in ? 'Log Out' : 'Log In' );
    $link->menu_item_parent = 0;
    $link->ID               = '';
    $link->db_id            = '';
    $link->url              = $is_in ? wp_logout_url() : wp_login_url();

    $sorted_menu_items[] = $link;

    return $sorted_menu_items;
}

Das Ergebnis zeigt dieser Screenshot:

Im hinteren Tab bin ich angemeldet, der vordere Tab ist ein privater, in dem ich abgemeldet bin. Das ist übrigens ein schönes Feature aktueller Browser, das einem das Testen beider Zustände parallel sehr erleichtert.

Wir können auch einen Link anhängen, dessen Titel und Adresse wir jetzt noch gar nicht kennen:

Letzten Blogartikel anhängen

Aufgabe: Der letzte Blogbeitrag soll ein eigener Menüpunkt werden. Wir wollen außerdem einen Auszug ins title-Attribut setzen und den Link mit einer zusätzlichen CSS-Klasse versehen. Ursprung wieder auf WordPress Stack Exchange.

Als kennzeichnendes Argument für wp_nav_menu() wählen wir jetzt add_latest_post; der Einsatz sollte inzwischen klar sein, also lasse ich den aufrufenden Code mal weg und springe gleich zur Filterfunktion.

add_filter( 'wp_nav_menu_objects', 't5_latest_post_in_nav_menu', 10, 2 );

/**
 * Add a link to the latest post to the nav menu.
 *
 * @wp-hook wp_nav_menu_objects
 * @param   array $sorted_menu_items Existing menu items
 * @param   object $args Nav menu arguments as object.
 * @return  array
 */
function t5_latest_post_in_nav_menu( $sorted_menu_items, $args )
{
    if ( empty ( $args->add_latest_post )
        or ! $latest = get_posts( array ( 'numberposts' => 1 ) )
    )
    {
        return $sorted_menu_items;
    }

    $post    = $latest[0];
    $content = empty ( $post->post_excerpt ) ? $post->post_content : $post->post_excerpt;
    $link    = array (
        'title'            => esc_html( $post->post_title ),
        'menu_item_parent' => 0,
        'ID'               => '',
        'db_id'            => '',
        'url'              => get_permalink( $post->ID ),
        'classes'          => array (
                0 => '',
                1 => 'menu-item',
                2 => 'menu-item-type-post_type',
                3 => 'menu-item-object-post',
                4 => 'latest-post', // special class
            ),
        // strips all tags and reduces the length to 20 words
        'attr_title'       => wp_trim_words( $content, 20 ),
    );

    $sorted_menu_items[] = (object) $link;

    return $sorted_menu_items;
}

Was passiert hier?

  1. Wir prüfen, ob der Parameter add_latest_post zu TRUE evaluiert und ob wir einen Beitrag gefunden haben. Klappt eines von beiden nicht, steigen wir sofort aus.
  2. Wir merken uns den Auszug (so vorhanden) oder den Beitragsinhalt.
  3. Wir bauen den Link diesmal als Array, einfach um eine alternative Schreibweise vorzustellen.
  4. Wir maskieren den Post-Titel, denn der darf in WordPress durchaus Markup enthalten. Das können wir im Menü aber nicht gebrauchen.
  5. Dann setzen wir ein paar notwendige Attribute, die hier nicht wichtig sind. Über menu_item_parent könnten wir beispielsweise einen Menüpunkt auswählen, dem unser Link untergeordnet wird. Aber wir wollen ihn ja nicht verstecken …
  6. Als URL nehmen wir den Permalink des Beitrages.
  7. In classes setzen wir die CSS-Klassen, die unser Link erhalten soll. Über die neue Klasse latest-post können wir den Link später im Stylesheet ansprechen.
  8. Von wp_trim_words() lassen wir jetzt alles Markup und alle Zeilenumbrüche aus $content entfernen, außerdem reduzieren wir dessen Länge auf 20 Worte. Das wird schließlich ein title-Attribut, kein Roman.
  9. Und zuletzt konvertieren wir den Array in ein Objekt, das wir an $sorted_menu_items anhängen, ehe wir es WordPress wieder in die Hand drücken.

Ergebnis: