wake-up-neo.net

Benutzerdefinierte Ausgabe von wp_nav_menu (Anzeige aller untergeordneten Elemente des obersten Menüelements im aktuellen Zweig)

Ich möchte eine Walker-Funktion erstellen oder den aktuellen wp_nav_menus ändern, damit eine benutzerdefinierte Ausgabe erstellt werden kann. Stellen wir uns diese Struktur vor:

menu item 1
menu item 2
 submenu item a
 submenu item b
menu item 3
 submenu item c
 submenu item d
 submenu item e

Am einfachsten ist es, nur die Hauptmenüelemente (Hauptmenü 1,2,3) an einer Stelle anzuzeigen. Dies kann mit wp_nav_menus erfolgen, sodass wir sie nicht codieren müssen.

Der problematische Teil besteht darin, an anderer Stelle Untermenüelemente des aktuellen Hauptelements anzuzeigen. Wenn sich der Benutzer also auf der Seite "Menüelement 1" befindet, wird nichts angezeigt. Wenn sich der Benutzer auf der Seite "Menüelement 2" befindet, zeigt unser "neues" benutzerdefiniertes Menü Folgendes an:

submenu item a
submenu item b

Dieselben Menüelemente werden gerendert, wenn sich der Benutzer auf einer der beiden obigen Seiten befindet (Menüelemente).

Wenn der Benutzer auf "Menüpunkt 3" klickt und seine Zielseite besucht, werden Untermenüpunkte c, d, e angezeigt, genau wie nach dem Klicken auf einen dieser Untermenüpunkte.

Wenn das Menü Elemente der dritten Ebene (und tiefer) enthält, sollte das benutzerdefinierte Menü alle untergeordneten Elemente des aktuell obersten Menüelements neben dem obersten Element selbst anzeigen (das im oberen Teil der an erster Stelle beschriebenen Site aufgeführt wird).

Gibt es eine Möglichkeit, solche Funktionen zu erstellen?

Die Idee liegt in der Nähe von: Anzeigen eines Teils/Zweigs des Menüs Es muss nur dynamisch sein und untergeordnete Elemente des aktuellen Menüzweigs anzeigen/alle untergeordneten Elemente des obersten Menüzweigs, falls vorhanden.

AFAIK Ich kann get_ancestors nicht verwenden, da es nur mit hierarchischen Taxonomien funktioniert, und hier geht es nicht um Menüs, die aus der hierarchischen Struktur von Beiträgen/Seiten erstellt wurden, sondern um die Verwendung des Menü-Editors.

Gelöst: Scheint, dass ich in der Lage war, korrekte Funktionen zu erstellen, indem ich einige Methoden kombinierte:

/**
 * The API function, just a wrap for wp_nav_menu adding filter first and removing after
 */ 
function filtered_nav_menu( $args = array() ) {
    $echo = isset($args['echo']) ? (bool)$args['echo'] : true;
    $args['echo'] = false;
    add_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
    $menu = wp_nav_menu( $args );
    remove_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
    if ( $echo ) echo $menu;
    else return $menu;
}

/**
 * The filter callback, return the filtered elements
 */
function gmfnv_filter( $items ) {
    $found_top_parent_ID = false;
    foreach ($items as $item) {
        if ( ($item->menu_item_parent == 0 && $item->current_item_ancestor == 1) || ($item->menu_item_parent == 0 && $item->current == 1) ) {
            $found_top_parent_ID = $item->ID;
        }
    }
    $children  = submenu_get_children_ids( $found_top_parent_ID, $items );
    foreach ( $items as $key => $item ) {
        if ( ! in_array( $item->ID, $children ) )
            unset($items[$key]);
    }
    return $items;
}

/**
 * Helper function: return children of an element using wp_filter_object_list
 */
function submenu_get_children_ids( $id, $items ) {
    $ids = wp_filter_object_list( $items, array( 'menu_item_parent' => $id ), 'and', 'ID' );
    foreach ( $ids as $id ) {
        $ids = array_merge( $ids, submenu_get_children_ids( $id, $items ) );
    }
    return $ids;
}

Was es tut - es sucht nach dem aktuellsten Vorfahren eines Zweigs, indem es jedes $ items -Element durchläuft und Folgendes überprüft:

($item->menu_item_parent == 0 && $item->current_item_ancestor == 1) || ($item->menu_item_parent == 0 && $item->current == 1)

Diese Bedingung muss zweiteilig sein, da das Menüelement der obersten Ebene möglicherweise auch aktuell ist.

Wenn $ item der obersten Ebene gefunden wird, wird die Funktion submenu_get_children_ids verwendet, um alle untergeordneten Elemente aufzulisten.

3
Marcin Bobowski

Die Idee

Anstatt einen benutzerdefinierten Walker zu erstellen, war es meiner Meinung nach einfacher, die Elemente mithilfe des Filterswp_nav_menu_objectshook zu filtern.

Dieser Hook ist in /wp-includes/nav-menu-templates.php definiert, und wenn er ausgelöst wird, wird er an Funktionen übergeben, die in ihn ein Array $sorted_menu_items einbinden, das alle Elemente des zu druckenden Menüs enthält. Wenn die Funktion dieses Array ändert, werden die resultierenden Elemente hinzugefügt oder entfernt Menü wird geändert.

Die Methode

Wenn der Filter auf wp_nav_menu_objects direkt angewendet wird, werden alle Menüs gefiltert. Ich dachte, es wäre besser, eine Funktion zu erstellen, die wp_nav_menu umschließt den Filter hinzufügt bevorwp_nav_menu aufruft und entferne es nach: auf diese Weise wird nur das gewünschte Menü gefiltert.

Der Filter-Workflow

  1. Durchlaufen Sie alle Elemente, die von wp_nav_menu_objects filter hook übergeben wurden
  2. Erstellen Sie zwei Hilfsarrays: eines nur aus übergeordneten IDs und eines mit Elementen wie $itemid => $parentid.
  3. Überprüfen Sie beim Durchlaufen von Elementen auch, ob die Element-URL mit der aktuellen URL übereinstimmt
  4. wenn die URL nicht übereinstimmt, werden nur übergeordnete Elemente zurückgegeben
  5. wenn die URL mit den Hilfsarrays übereinstimmt, geben Sie die gewünschten Elemente zurück

Der Code

Der Code, den ich dafür geschrieben habe, nutzt 5 Funktionen, also erstelle ich ein Plugin, das all das enthält, hier den Code:

<?php
/**
 * Plugin Name: Filtered Nav Menus
 * Author: Giuseppe  Mazzapica
 * Plugin URI: http://wordpress.stackexchange.com/questions/118720/
 * Author URI: http://wordpress.stackexchange.com/users/35541/
 */

/**
 * The API function, just a wrap for wp_nav_menu adding filter first and removing after
 */ 
function filtered_nav_menu( $args = array() ) {
  $echo = isset($args['echo']) ? (bool)$args['echo'] : true;
  $args['echo'] = false;
  add_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
  $menu = wp_nav_menu( $args );
  remove_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
  if ( $echo ) echo $menu;
  else return $menu;
}

/**
 * The filter callback, return the filtered elements
 */
function gmfnv_filter( $items ) {
  $found = false;
  $parents = $items_tree = $allowed = array();
  $all_items = $items;
  while ( ! empty( $items ) ) {
    $item = array_shift( $items );
    $items_tree[$item->ID] = $item->menu_item_parent;
    if ( (int) $item->menu_item_parent == 0 ) $parents[] = $item->ID;
    if ( isset($item->current) && $item->current ) $found = $item->ID;
  }
  if ( ! $found ) {
    $ids = $parents;
  } else {
    $tree = gmfnv_get_tree( $found, $all_items, $items_tree );
    $ids = array_merge( $parents, $tree );
  }
  foreach ( $all_items as $item ) {
    if ( in_array( $item->ID, $ids ) ) $allowed[] = $item;
  }
  return $allowed;
}


/**
 * Helper function: take the matched element if and the helper array and
 * return the item ancestors by gmfnv_get_parents,
 * and the children of these ancestors returned by gmfnv_get_parents
 * using gmfnv_get_parents
 */
function gmfnv_get_tree( $test, $items, $tree ) {
  $parents = gmfnv_get_parents( $test, $items );
  $parents[] = $test;
  $n = array();
  foreach ( $parents as $parent ) {
    $n = array_merge( $n, gmfnv_get_childrens( $parent, $tree ) );
  }
  return array_unique( $n );
}


/**
 * Helper function: return ancestors of an element using the helper array
 */
function gmfnv_get_parents( $test, $items ) {
  $parents = array();
  foreach( $items as $item ) {
      if (
        (isset($item->current_item_ancestor) && $item->current_item_ancestor)
        || (isset($item->current_item_ancestor) && $item->current_item_ancestor)
      ) $parents[] = $item->ID;
  }
  return $parents;
}


/**
 * Helper function: return children of an element using the helper array
 */
function gmfnv_get_childrens( $test, $tree ) {
  $children = array();
  foreach ( $tree as $child => $parent ) {
    if ( $parent == $test ) $children[] = $child;
  }
  return $children;
}

Wie man

Erstellen Sie eine Datei, die dieses Plugin enthält, und aktivieren Sie es.

Wenn Sie ein Menü nach Bedarf filtern möchten, verwenden Sie stattdessen wp_nav_menu( $args )

filtered_nav_menu( $args );

Haftungsausschluss

Der Code lieferte so wie er ist, keine Garantie, sondern wurde nur schnell auf PHP 5.4, WP 3.7 getestet, wobei einundzwanzigstes Theme aktiv war und keine anderen Plugins: dies funktionierte Fall.

5
gmazzap