特定のadd_filter
クラスが実行されるたびに特定のWalker_Nav_Menu
関数を実行する方法はありますか?この関数は、ウォーカーが移動したときには必ず実行する必要があります。それらの変数、特に$column_limit
を共有したいのですが。それらが生成される前にliにクラスを追加するために、関数はwalkerの他のすべてのものの直前に実行されるべきです。
この一連のPHPの目的は、WordPressのメガメニュー有効化機能です。基本的に、このようなマークアップがあるとします。
<ul>
<li><a href="#">Example</a></li>
<li class="widget">
<a href="#">Example</a>
<ul>
<li><a href="#">Example</a></li>
<li><a href="#">Example</a></li>
<li><a href="#">Example</a></li>
<li class="break"><a href="#">Example</a></li>
<li><a href="#">Example</a></li>
<li><a href="#">Example</a></li>
</ul>
</li>
<li><a href="#">Example</a></li>
<li><a href="#">Example</a></li>
<li><a href="#">Example</a></li>
</ul>
break
とwidget
はWordPressのメニューエディタでユーザーによって追加されたクラスで、ウォーカーと関数はこれを出力します。
<ul>
<li><a href="#">Example</a></li>
<li class="widget mega-menu-columns-3">
<a href="#">Example</a>
<section>
<ul>
<li><a href="#">Example</a></li>
<li><a href="#">Example</a></li>
<li><a href="#">Example</a></li>
</ul>
<ul class="sub-menu">
<li class="break"><a href="#">Example</a></li>
<li><a href="#">Example</a></li>
<li><a href="#">Example</a></li>
</ul>
<aside>
<h1>Widget Title</h1>
<p>Widget text</p>
</aside>
</section>
</li>
<li><a href="#">Example</a></li>
<li><a href="#">Example</a></li>
<li><a href="#">Example</a></li>
</ul>
この関数は、上記のコードのmega-menu-columns-#
に<li>
を追加しています。他のすべての変更は、Walkerによって行われています。これはすべてうまくいきますが、もっと少ない設定でうまくいくことを望みます。基本的に私は一度カラム制限を設定できるようにしたいと思います、そして関数の中でtheme_location
を設定する必要はありません。ウォーカーを使用するようにメニューを設定した場合は、自分のPHPすべてを自動的に実行します。これで十分明確でない場合は、さらに説明を求めてください。
これが私の現在のコードです:
// mega menu walker
class megaMenuWalker extends Walker_Nav_Menu {
private $column_limit = 3; // used to how many columns can be generated (</ul><ul class="sub-menu"> and the widgets)
private $show_widget = false; // used to determine weather or not to show a widget in the drop down
private $column_count = 0; // for counting how many columns are in a drop down
static $li_count = 0; // for counting how many lis are in a drop down, used only to check if the li is first in its list
function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
$classes = empty($item->classes) ? array() : (array) $item->classes; // retrive array of classes for each li
$item_id = $item->ID; // retrieve item id for each li, not really used, keeping it to maintain standard WP menu structure
if ($depth == 0) {
self::$li_count = 0; // reset the li counter when we return to the top level in the menu
}
if ($depth == 0 && in_array("widget", $classes)) {
$this->show_widget = true; // show the widget if the top level li has a class of "widget"
$this->column_count++; // if the widget is shown, increase the column counter by one
}
if ($depth == 1 && self::$li_count == 1) {
$this->column_count++; // if there's anything in a drop down, increase the column counter by one
}
if ($depth == 1 && in_array("break", $classes) && self::$li_count != 1 && $this->column_count < $this->column_limit) {
// if we're in ul ul, and an li has a class of break, and it's not the first in its list, and we haven't met the column limit...
$output .= "</ul><ul class=\"sub-menu\">"; // add a break in the list
$this->column_count++; // increase the column count by one
}
$class_names = join(" ", apply_filters("nav_menu_css_class", array_filter($classes), $item)); // set up the classes array to be added as classes to each li
$class_names = " class=\"" . esc_attr($class_names) . "\""; // set up class HTML
$output .= sprintf(
"<li id=\"menu-item-%s\"%s><a href=\"%s\">%s</a>", // output li structure
$item_id, // not really used, keeping it for standard WP menu structure
$class_names, // add the class names
$item->url, // add the URL
$item->title // add the title
);
self::$li_count++; // increase the li counter. Not used except if == 1
}
function start_lvl(&$output, $depth = 0, $args = array()) {
if ($depth == 0) {
$output .= "<section>"; // if a drop down exists, wrap it in <section>. I'm not sure why $depth == 0 and not 1, but it works, so I'm not complaining.
}
$output .= "<ul class=\"sub-menu\">"; // output standard WP menu UL for drop downs
}
function end_lvl(&$output, $depth = 0, $args = array()) {
$output .= "</ul>"; // close UL for drop downs
if ($depth == 0) {
if ($this->show_widget) {
// if the parent li has a class of widget...
ob_start();
dynamic_sidebar("Navigation Callout"); // show the widget
$widget = ob_get_contents(); // retrieve the widget's HTML as a variable
ob_end_clean(); // clear the shown widget
$output .= $widget; // show the widget
$this->show_widget = false; // reset the widget variable to false so the next li doesn't get one unless indicated.
// I know this section is complicated, but it was the only way to correctly display the widget
}
$output .= "</section>"; // end the <section> that contains the drop downs.
}
}
function end_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
if ($depth == 0 && $this->column_count > 0) {
$this->column_count = 0; // reset the column counter when a drop down ends.
}
$output .= "</li>"; // close the li of each menu item
}
}
// add mega-menu-columns-# classes
// I didn't write this, but I'll do my best to explain it
add_filter("wp_nav_menu_objects", function($items, $args) {
if ("first" !== $args->theme_location) {
return $items; // don't run the function if it's not a specific menu. This should really be a check if the walker is set to a menu, not the menu's location.
}
static $post_id = 0; // set up the post id variable
static $x_key = 0; // not sure what this is
static $column_count = 1; // set the initial column count to 1
static $column_limit = 3; // set the column limit to 3. Ideally would pull from the walker.
static $li_count = 0; // set up the li counter
$tmp = array(); // not sure waht this is
foreach($items as $key => $item) {
if (0 == $item->menu_item_parent) {
$x_key = $key; // not sure...
$post_id = $item->ID; // set the post ID to the item's ID
$column_count = 1; // reset the column count
$li_count = 0; // reset the li count
if (in_array("widget", $item->classes, 1)) {
$column_count++; // if widget is in the li class, add 1 to the column count
}
}
if ($post_id == $item->menu_item_parent) {
$li_count++; // if an item has children, increase the li count by one
if (in_array("break", $item->classes, 1) && $li_count > 1 && $column_count < $column_limit) {
$column_count++; // if break is in the item's classes, and the li count isn't one, and the column count is'nt equal to the column limit, increase the counter by one
}
$tmp[$x_key] = $column_count; // not sure what this is.
}
}
foreach($tmp as $key => $value) {
$items[$key]->classes[] = sprintf("mega-menu-columns-%d", $value); // adding the new class based on column count to the lis
}
unset($tmp); // not sure
return $items; // ending the filter
}, PHP_INT_MAX, 2); // not sure
そのすべてがあなた自身のコードであれば、それをwalkerの一部として実装するのが妥当でしょう。
サードパーティのwalkerの実行中にフィルタを追加する必要がある場合は、おそらく次のようにします。
walker
が次のように設定されているか確認しながら、wp_nav_menu_args
フックに追加します。
add_filter( 'wp_nav_menu_args', function( $args ) {
if ( $args['walker'] instanceof megaMenuWalker ) {
add_filter( 'wp_nav_menu_objects', 'callback_name_here' );
}
return $args;
} );
メニューマークアップが完全に生成されたら、wp_nav_menu
フックでそれを削除します。
add_filter( 'wp_nav_menu', function( $nav_menu ) {
remove_filter( 'wp_nav_menu_objects', 'callback_name_here' );
return $nav_menu;
} );