Hoàng Web

Thiết Kế Website WordPress

  • Kho giao diện
  • Dịch Vụ
    • Thiết kế web giá rẻ
    • Thiết kế website WordPress
    • Hosting Miễn Phí 100GB
    • Tích hợp thanh toán MoMo, ViettelPay, Vietcombank, MB..
    • Tối ưu Google PageSpeed
    • Sửa lỗi nâng cấp website
    • Viết plugin WordPress
    • Code Tool theo yêu cầu
  • Bảng giá
  • Quy trình làm việc
  • Giới thiệu
  • Liên Lạc
Trang chủ » Wordpress » Tùy biến hiển thị menus trong wordpress – Nav Menu Walker

Tùy biến hiển thị menus trong wordpress – Nav Menu Walker

Thứ Năm, 03/07/2014 by Hoàng Quách

Nội dung

  • 1 Kế thừa Walker_Nav_Menu class
  • 2 Sử dụng Custom WordPress Walker
  • 3 Phương thức kế thừa trong class Walker
  • 4 Tùy biến hooks
        • 4.0.0.1 walker_nav_menu_start_el
        • 4.0.0.2 nav_menu_link_attributes
  • 5 Sửa tham số wp_nav_menu
  • 6 Tạo Custom Menu Widget
  • shares
  • Facebook
  • Facebook Messenger
  • Gmail
  • Viber
  • Skype

Nếu website của bạn có một hệ thống menu lớn, bạn muốn hiển thị một phần dữ liệu menu ở một khu vực nào đó trên trang/ một trang khác. Bài viết này sẽ giải thích chi tiết cách làm này. Ý tưởng hiện ra trong đầu bạn là tạo từng menu cho mỗi trang, nhưng tại sao lại làm cách này chia ra làm nhiều menus sẽ khó quản lý trong khi wordpress có API cho phép thiết lập trạng thái cho từng menu item.

Xóa ul wrap
Xóa ul bao menu chứa thẻ li, bạn điều chỉnh tham số ‘items_wrap’

<?php wp_nav_menu( array( 'items_wrap' => '%3$s' ) ); ?>

Hoặc
Thêm ký tự trước menu

<?php wp_nav_menu( array( 'theme_location' => 'primary', 'items_wrap' => '<ul><li id="item-id">Menu: </li>%3$s</ul>' ) ); ?>

Kế thừa Walker_Nav_Menu class

Tạo một class mới trong /functions.php , tên lớp nên có tiền tố để tránh trùng với class khác.

class JC_Walker_Nav_Menu extends Walker_Nav_Menu {
}

Trong Class Walker_Nav_Menu có các hàm bạn có thể kế thừa và override, lớp được viết ở /wp-admin/includes/nav-menu.php
Ví dụ sau đây mình sẽ lấy tất cả menu items là con của 1 menu chỉ định, để làm điều này cần kế thừa 2 hàm của lớp là: start_llvl và end_lvl.

class JC_Walker_Nav_Menu extends Walker_Nav_Menu {

	function start_lvl( &$output, $depth = 0, $args = array() ) {}

	function end_lvl( &$output, $depth = 0, $args = array() ) {}
}

Truyền parent menu id vào construct để lấy xác định các menu item con, tiếp tục lấy các menu items ids con làm parent id để xác định các menu item con của nó, cứ thế cho đến hết, dùng mảng lưu tất cả các menu items cho việc quản lý.

class JC_Walker_Nav_Menu extends Walker_Nav_Menu {

	var $menu_id = 0;
	var $menu_items = array();

	function __construct($id = 0){
		$this->menu_id = $id;
		$this->menu_items[] = $id;
	}

	function start_el( &$output, $item, $depth, $args ) {
		if( !empty($this->menu_items) && !in_array($item->ID, $this->menu_items) && !in_array($item->menu_item_parent, $this->menu_items)){
			return false;
		}

		if(!in_array($item->ID, $this->menu_items)){
			$this->menu_items[] = $item->ID;
		}

		parent::start_el($output, $item, $depth, $args);
	}

	function end_el( &$output, $item, $depth = 0, $args = array() ) {
		if( !empty($this->menu_items) && !in_array($item->ID, $this->menu_items) && !in_array($item->menu_item_parent, $this->menu_items)){
    			return false;
    		}
    		parent::end_el($output, $item, $depth, $args);
	}

	function start_lvl( &$output, $depth = 0, $args = array() ) {}

	function end_lvl( &$output, $depth = 0, $args = array() ) {}
}

Sử dụng Custom WordPress Walker

Hiển thị nav menu với wp_nav_menu, khởi tạo class JC_Walker_Nav_Menu và truyền parent menu id=8 để chỉ hiển thị toàn bộ các menu dưới menu có id=8.

<?php  wp_nav_menu( array('menu' => 'header-menu', 'walker' => new JC_Walker_Nav_Menu(8)) ); ?>

Phương thức kế thừa trong class Walker

Bạn có thể extends walker class để thay đổi nội dung hiển thị HTML cho item và thậm trí sub items tạo bởi các hàm như: wp_nav_menu, wp_list_pages, wp_list_categories.

Bằng cách sửa đổi nội dung của các hàm thiết kế của lớp kế thừa, chúng ta sẽ làm thay đổi cách hiển thị cho các dữ liệu Menu, Category trong wordpress.
Phương thức start_el mở thẻ li bắt đầu cho một item và tương ứng đóng item với phương thức end_el. Sử dụng tham số xác định thông tin và dữ liệu của item bạn có thể loại bỏ nó trong kết quả hiển thị. Ví dụ sau có loại bỏ top-level elements.

// Don't print top-level elements
function start_el(&$output, $item, $depth=0, $args=array()) {
    if( 0 == $depth )
        return;
    parent::start_el(&$output, $item, $depth, $args);
}

function end_el(&$output, $item, $depth=0, $args=array()) {
    if( 0 == $depth )
        return;
    parent::end_el(&$output, $item, $depth, $args);
}

Walker class còn có phương thức display_element, phương thức này giúp sử lý những item có chứa sub items bên dưới. Hàm display_element kế thừa trong class chính được chạy trong hàm start_el nếu có kế thừa tính năng của hàm mẹ start_el. Trong cách sử dụng php class, chúng ta gọi hàm mẹ của phương thức kế thừa với từ khóa parent, giống như thế này:

// Don't print top-level elements
function start_el(&$output, $item, $depth=0, $args=array()) {
    if( 0 == $depth )
        return;
    parent::start_el(&$output, $item, $depth, $args);
}

Tham khảo mẫu nội dung đầy đủ của hàm start_el và end_el.

var $number = 1;

    function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
        $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

        $class_names = $value = '';

        $classes = empty( $item->classes ) ? array() : (array) $item->classes;
        $classes[] = 'menu-item-' . $item->ID;

        $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
        $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';

        $id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
        $id = $id ? ' id="' . esc_attr( $id ) . '"' : '';

        $output .= $indent . '<li' . $id . $value . $class_names .'>';

        // add span with number here
        if ( $depth == 0 ) { // remove if statement if depth check is not required
            $output .= sprintf( '<span>%02s.</span>', $this->number++ );
        }

        $atts = array();
        $atts['title']  = ! empty( $item->attr_title ) ? $item->attr_title : '';
        $atts['target'] = ! empty( $item->target )     ? $item->target     : '';
        $atts['rel']    = ! empty( $item->xfn )        ? $item->xfn        : '';
        $atts['href']   = ! empty( $item->url )        ? $item->url        : '';

        $atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args );

        $attributes = '';
        foreach ( $atts as $attr => $value ) {
            if ( ! empty( $value ) ) {
                $value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
                $attributes .= ' ' . $attr . '="' . $value . '"';
            }
        }

        $item_output = $args->before;
        $item_output .= '<a'. $attributes .'>';
        $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
        $item_output .= '</a>';
        $item_output .= $args->after;

        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }
function end_el(&$output, $item, $depth=0, $args=array()) {
		$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
        $output .= $indent."</li>\n";
    }

Mọi item đều sử lý qua hàm display_element này. Ví dụ, chúng ta không cho hiển thị các menu ở top-level và URL hiện tại không thuộc về item đó. Để làm điều này, chúng ta dựa vào thuộc tính class : current-menu-item, current-menu-parent, current-menu-ancestor

// Only follow down one branch
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {

    // Check if element as a 'current element' class
    $current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' );
    $current_class = array_intersect( $current_element_markers, $element->classes );

    // If element has a 'current' class, it is an ancestor of the current element
    $ancestor_of_current = !empty($current_class);

    // If this is a top-level link and not the current, or ancestor of the current menu item - stop here.
    if ( 0 == $depth && !$ancestor_of_current)
        return;

    parent::display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output );
}

Hai phương thức cuối cùng nhóm các sub items ở mọi levels, là start_lvl & end_lvl. Thường sử dụng thẻ ul. Nếu bạn cũng muốn loại bỏ cho nhóm items ở top-level. Lưu ý: toàn bộ những items trong level đó sẽ bỏ qua, không được hiển thị.

// Don't wrap the top level
function start_lvl(&$output, $depth=0, $args=array()) {
    if( 0 == $depth )
        return;
    parent::start_lvl(&$output, $depth, $args);
}

function end_lvl(&$output, $depth=0, $args=array()) {
    if( 0 == $depth )
        return;
    parent::end_lvl(&$output, $depth, $args);
}

Kế thừa nội dung của phương thức tạo thẻ bao items ở trên, đầy đủ giống như sau.

function start_lvl(&$output, $depth=0, $args=array()) {
		$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
        $output .= $indent."\n<ul>\n";
    }
	function end_lvl(&$output, $depth=0, $args=array()) {
		$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
        $output .= $indent."</ul>\n";
    }

Tùy biến hooks

Bạn để ý ngoài cách sử dụng class walker vào menu, bạn có thể chia nhỏ can thiệp vào từng dữ liệu có trong walker mặc định của menu với các hooks filter.

walker_nav_menu_start_el

Chúng ta để ý có filter ‘walker_nav_menu_start_el‘ cho phép bạn tùy biến hiển thị menu bằng cách sửa hook này. Bạn có thể thêm vào functions.php như sau, ví dụ:

if (function_exists('qtrans_convertURL')) {

function qtrans_in_nav_el($output, $item, $depth, $args) {

	$attributes = !empty($item->attr_title) ? ' title="' . esc_attr($item->attr_title) . '"' : '';

	$attributes .=!empty($item->target) ? ' target="' . esc_attr($item->target) . '"' : '';

	$attributes .=!empty($item->xfn) ? ' rel="' . esc_attr($item->xfn) . '"' : '';

	// Integration with qTranslate Plugin

	$attributes .=!empty($item->url) ? ' href="' . esc_attr( qtrans_convertURL($item->url) ) . '"' : '';

	$output = $args->before;

	$output .= '<a' . $attributes . '>';

	$output .= $args->link_before . apply_filters('the_title', $item->title, $item->ID) . $args->link_after;

	$output .= '</a>';

	$output .= $args->after;

	return $output;

}

add_filter('walker_nav_menu_start_el', 'qtrans_in_nav_el', 10, 4);

}

nav_menu_link_attributes

Bạn có thể sửa mảng $atts là kết quả của filter nav_menu_link_attributes. Ví dụ sau mình fix lỗi URL trang chủ khi chuyển ngôn ngữ trong wordpress cho plugin qtranslate, bạn thêm đoạn code sau vào functions.php

function qtrans_convertHomeURL($url, $what) {
    if($what=='/') return qtrans_convertURL($url);
    return $url;
}

add_filter('home_url', 'qtrans_convertHomeURL', 10, 2);
add_filter('nav_menu_link_attributes','custom_menu',10,3);
function custom_menu($atts, $item, $args){
	if($item->url=='/'){
		$atts['href']=qtrans_convertURL($item->url);
	}
	return $atts;
}

Sửa tham số wp_nav_menu

Bạn có thể sửa lại tham số cài đặt cho các menu tạo bởi wp_nav_menu, tùy chỉnh cấu hình của mọi menu sử dụng filter wp_nav_menu_args.

function modify_nav_menu_args( $args )
{
	if( 'primary' == $args['theme_location'] )
	{
		$args['depth'] = -1;
		$args['container_id'] = 'my_primary_menu';
	}

	return $args;
}

add_filter( 'wp_nav_menu_args', 'modify_nav_menu_args' );

Khi wp_nav_menu lấy giá trị mảng truyền vào, nó sẽ sử lý qua filter wp_nav_menu_args, do đó chúng ta có thể sửa lại mọi menu trước khi nó được hiển thị như đúng thiết lập tại vị trí gọi hàm hiển thị menu wp_nav_menu.

Tạo Custom Menu Widget

Tạo custom menu widget, cho lựa chọn hiển thị một phần của menu. Xem code widget sau đây:

<?php
class JC_Adv_Menu_Widget extends WP_Widget {

	public function __construct() {
		parent::__construct(
	 		'jc_menu_widget', // Base ID
			'Advanced Menu Widget', // Name
			array( 'description' => __( 'Custom options to output menus in your theme')) // Args
		);
	}

	public function widget( $args, $instance ) {
		extract( $args );

		$title = apply_filters( 'widget_title', $instance['title'] );

		echo $before_widget;

		if ( ! empty( $title ) )
			echo $before_title . $title . $after_title;

		wp_nav_menu( array('menu' => $instance['menu'], 'walker' => new JC_Walker_Nav_Menu($instance['menu_item'])) );

		echo $after_widget;
	}

 	public function form( $instance ) {

		$title = isset($instance['title']) ? $instance['title'] : '';
		$menu = isset($instance['menu']) ? $instance['menu'] : '';
		$menu_item = isset($instance['menu_item']) ? $instance['menu_item'] : '';
		$menus = get_terms('nav_menu');

		?>
		<p>
		<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></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 esc_attr( $title ); ?>" />
		</p>

		<p>
		<input class="widefat" id="<?php echo $this->get_field_id( 'menu' ); ?>" name="<?php echo $this->get_field_name( 'menu' ); ?>" type="hidden" value="<?php echo $menu; ?>" />
		</p>

		<p>
			<label for="<?php echo $this->get_field_id( 'menu_item' ); ?>"><?php _e( 'Select Menu Part:' ); ?></label>
			<select class="widefat" id="<?php echo $this->get_field_id( 'menu_item' ); ?>" name="<?php echo $this->get_field_name( 'menu_item' ); ?>" >
				<?php foreach($menus as $m){
					wp_nav_menu(array(
					  'menu' => $m->slug, // your theme location here
					  'container' => false,
					  'walker'         => new Walker_Nav_Menu_Dropdown($menu_item),
					  'items_wrap'     => '<optgroup id="'.$m->slug.'" label="'.$m->name.'">%3$s</optgroup>',
					));
				} ?>
			</select>
		</p>

		<script type="text/javascript">
		jQuery(document).ready(function($) {
			$('select#<?php echo $this->get_field_id( 'menu_item' ); ?>').change(function(){
				var label=$('select#<?php echo $this->get_field_id( 'menu_item' ); ?> :selected').parent().attr('id');
			    $('#<?php echo $this->get_field_id( 'menu' ); ?>').val(label);
			});
		});
		</script>

		<?php
	}

	public function update( $new_instance, $old_instance ) {
		$instance = array();

		$instance['title'] = strip_tags( $new_instance['title'] );
		$instance['menu'] = strip_tags( $new_instance['menu'] );
		$instance['menu_item'] = strip_tags( $new_instance['menu_item'] );

		return $instance;
	}

}

add_action( 'widgets_init', create_function( '', 'register_widget( "JC_Adv_Menu_Widget" );' ) );

class Walker_Nav_Menu_Dropdown extends Walker_Nav_Menu{

	var $item_id = 0;

	function __construct($id = 0){
		$this->item_id = $id;
	}

	public function start_lvl(&$output, $depth){}

	public function end_lvl(&$output, $depth){}

	public function start_el(&$output, $item, $depth, $args){

		$item->title = str_repeat("&nbsp;", $depth * 4) . $item->title;

		parent::start_el($output, $item, $depth, $args);
		if($item->ID == $this->item_id)
			$output = str_replace('<li', '<option value="'.$item->ID.'" selected="selected"', $output);
		else
			$output = str_replace('<li', '<option value="'.$item->ID.'"', $output);
	}

	public function end_el(&$output, $item, $depth){
		$output .= "</option>\n";
	}
}
?>

Ngoài tính năng walker mà menu cung cấp, có thể sử dụng các hàm menu : wp_get_nav_menu_items, wp_get_nav_menu_object, get_nav_menu_locations..
để lấy dữ liệu menu.
Tham khảo:

function vcn_menus_items($id){
    static $data_menu=array();
    $locations = get_nav_menu_locations();
    $menu=wp_get_nav_menu_object($locations['menu1']);
    $args=array(
        "post_parent"=>$id
        //'post_type'=>'nav_menu_item',
        //'post_parent'=>0
    );
    $menu_items = wp_get_nav_menu_items($menu->term_id,$args);

    if(count($data_menu)==0)
    foreach( (array)$menu_items as $key=>$menu_item){print_r($menu_item);
   $t = wp_get_nav_menu_items($menu_item->object_id,array('post_parent'=>$menu_item->post_parent));print_r($t);
    echo '<hr/>';continue;
        echo $menu_item->post_type.'<br/>';
        echo $menu_item->object.'<br/>';
        echo $menu_item->url.'<br/>';
        echo $menu_item->menu_item_parent .'<br/>';
        echo $menu_item->title.'<hr/>';

		//các item theo thứ tự or trong cùng 1 menu item gốc:ie:
		/*
			-item 1	//theo thứ tự: ie (1)
				-item-1-0	(2)
				-item-1-1	(3)
				-item-1-2	(4)
				...
			-item 2	//khác menu_order	ie:(6)
		*/
		$element->menu_order

        //save
        if(!$menu_item->menu_item_parent) $data_menu[]=array('main'=>$menu_item,'sub'=>array());
        else $data_menu[count($data_menu)-1]['sub'][]=$menu_item;

        //echo '<li><a href="' . $url . '">' . $title . '</a></li>';
    }
    return $data_menu;
}

Hết !

Hãy cho mình biết suy nghĩ của bạn trong phần bình luận bên dưới bài viết này. Hãy theo dõi kênh chia sẻ kiến thức WordPress của Hoangweb trên Twitter và Facebook

  • shares
  • Facebook
  • Facebook Messenger
  • Gmail
  • Viber
  • Skype

Chuyên mục: Wordpress Tìm kiếm: nav menu, walker, wp_nav_menu

Tôi giúp gì cho bạn?

HOÀNG WEB

Địa chỉ: Tây Sơn, Phường Quang Trung, Quận Đống Đa, Hà Nội

Hotline: 0987 342 124 – 0868 292 303 (8h:00 – 21h:00)

Email: [email protected]

Website: www.hoangweb.com

KẾT NỐI VỚI TÔI

  • Facebook
  • GitHub
  • YouTube

SẢN PHẨM

  • Plugin Thanh Toán Quét Mã QR Code Tự Động
  • WP2Speed – Tối ưu Google Speed
  • 23WebHost – Hosting Miễn Phí 100GB

LIÊN KẾT

  • Có nên thuê thiết kế website giá rẻ?
  • Hướng dẫn thanh toán
  • Chính sách hoàn tiền
  • Trung tâm hỗ trợ

Copyright © 2023 | All rights reserved | HOANG WEB
Mọi hình thức sao chép nội dung trên website này mà chưa được sự đồng ý đều là trái phép.