WordPress: Trình đơn tuỳ biến theo chuyên mục và trang

Gần đây có một khách quen đề nghị làm một oép sai về thông tin đào tạo. Nhìn cái khung nội dung cũng thấy đủ hoa mắt vì nó có quá nhiều chuyên mục, trong mỗi chuyên mục lại có một mớ các chuyên mục con ở nhiều cấp khác nhau. Một cái trình đơn (menu) chung sẽ rất khó có thể đem lại sự tiện dụng cho người dùng. Vì thế, với mỗi một nhóm nội dung (như Đào tạo đại học, Đào tạo sau đại học, Đảm bảo chất lượng,…) thì lại cần một trình đơn riêng.

Website chạy bằng WordPress nên trong trường hợp này thì từng chuyên mục (category), các bài viết (post) thuộc chuyên mục và từng trang (page) sẽ hiển thị thêm một trình đơn riêng. Mục đích là để người đọc thấy rõ cấu trúc nội dung cũng như có lối ngắn nhất đến nội dung cần đọc.

Trình đơn tuỳ biến
Minh hoạ cho dễ hình dung

Mô tả giải pháp

Đối với chuyên mục, trong phần tạo mới và chỉnh sửa chuyên mục, anh sẽ phải thêm một trường để chọn trình đơn tương ứng.

Đối với bài viết, anh sẽ xác định chuyên mục của bài viết đấy rồi lấy thông tin tuỳ chọn.

Nếu chuyên mục cấp thấp mà không xác định trình đơn thì sẽ dùng trình đơn được xác định ở thư mục cấp cao hơn (nếu có).

Còn đối với trang thì anh dùng meta field với một meta box trực quan.

Sau cùng, anh tạo một widget để hiển thị trình đơn này.

Hiện thực hoá

Tất cả có thể nhét vào một trình bổ sung (plugin) mà không phải can thiệp gì vào giao diện (theme). Anh đặt tạm một cái tên là “Sidebar Custom Menu”.

<?php 
/**
 * Plugin Name: Sidebar Custom Menu
 * Plugin URI: https://luom.tv
 * Description: Show a custom menu for specific categories or pages.
 * Version: 0.0.1
 * Author: Pham Thanh Long
 * Author URI: https://luom.tv
 * License: GPL2
 */


// ***********************************************
// BEGIN Category Settings for Sidebar Custom Menu
function luomtv_category_custom_menu($taxonomies) {

    $menus = get_terms('nav_menu');
    $menus = array_combine(wp_list_pluck($menus, 'term_id'), wp_list_pluck($menus, 'name'));

    // Editing an exist category
	if (current_filter() == 'category_edit_form_fields') {

		$cat_menu = get_term_meta($taxonomies->term_id, 'cat_menu', true);        
		?>
		 
        <tr class="form-field">
            <th valign="top" scope="row"><label for="cat_menu"><?php _e('Sidebar Menu'); ?></label></th>
            <td>
                <select name="cat_menu" id="cat_menu">
                    <option <?php selected(esc_attr($cat_menu), '0'); ?> value="0"><?php _e('-- Unset --'); ?></option>
                    
                    <?php foreach ($menus as $menu_id => $menu_name): ?>

                        <option <?php selected(esc_attr($cat_menu), $menu_id); ?> value="<?php echo esc_attr($menu_id); ?>"><?php echo esc_html($menu_name); ?></option>

                    <?php endforeach; ?>

                </select>
				<p class="description"><?php _e('Select a menu to display on sidebar. Leave unset to use parent\'s setting.'); ?></p>
		   </td> 
	   </tr>
	
	   <?php
    // Adding a new category
    } elseif (current_filter() == 'category_add_form_fields') { ?>

		<div class="form-field">
			<label for="cat_menu"><?php _e('Sidebar Menu'); ?></label>
			<select name="cat_menu" id="cat_menu">
                <option value="0"><?php _e('-- Unset --'); ?></option>

                <?php foreach ($menus as $menu_id => $menu_name): ?>

                    <option value="<?php echo esc_attr($menu_id); ?>"><?php echo esc_html($menu_name); ?></option>

                <?php endforeach; ?>

            </select>
			<p class="description"><?php _e('Select a menu to display on sidebar. Leave unset to use parent\'s setting.'); ?></p>
		</div><?php
	}
}
add_action('category_edit_form_fields', 'luomtv_category_custom_menu', 10, 2);
add_action('category_add_form_fields', 'luomtv_category_custom_menu', 10, 2); 


function luomtv_save_category_fields($term_id) {

	if (isset($_REQUEST['cat_menu'])) { 
		$cat_menu = $_REQUEST['cat_menu']; 
			 
		update_term_meta($term_id, 'cat_menu', $cat_menu);   
	}
}
add_action('edited_category', 'luomtv_save_category_fields', 10, 2);
add_action('create_category', 'luomtv_save_category_fields', 10, 2); 
// END Category Settings for Sidebar Custom Menu


// ************************************************
// BEGIN Meta Box: Sidebar Custom Menu (pages only)
function luomtv_metabox_custom_menu() {

    add_meta_box(
        'luomtv_metabox_custom_menu',       // Unique ID
        'Sidebar Custom Menu',              // Box title
        'luomtv_metabox_custom_menu_html',  // Content callback
        'page'                              // Post type
   );
}
add_action('add_meta_boxes', 'luomtv_metabox_custom_menu');


function luomtv_metabox_custom_menu_html($post) {

    $sidebar_menu = get_post_meta($post->ID, '_luomtv_sidebar_menu', true);

    if ('' == $sidebar_menu) {
        $sidebar_menu = '0';
    }

    $menus = get_terms('nav_menu');
    $menus = array_combine(wp_list_pluck($menus, 'term_id'), wp_list_pluck($menus, 'name')); ?>

    <label for="luomtv_sidebar_custom_menu">Select sidebar menu: </label>
    <select name="luomtv_sidebar_custom_menu" id="luomtv_sidebar_custom_menu" style="1px solid silver;">
        <option <?php selected(esc_attr($sidebar_menu), '0'); ?> value="0"><?php _e('-- No menu --'); ?></option>
        
        <?php foreach ($menus as $menu_id => $menu_name): ?>

            <option <?php selected(esc_attr($sidebar_menu), $menu_id); ?> value="<?php echo esc_attr($menu_id); ?>"><?php echo esc_html($menu_name); ?></option>

        <?php endforeach; ?>
    </select><?php
}

function luomtv_metabox_custom_menu_save($post_id) {

    if (array_key_exists('luomtv_sidebar_custom_menu', $_POST)) {
        update_post_meta(
            $post_id,
            '_luomtv_sidebar_menu',
            $_POST['luomtv_sidebar_custom_menu']
       );
    }
}
add_action('save_post', 'luomtv_metabox_custom_menu_save');
// END Meta Box: Sidebar Custom Menu


// ****************************************
// BEGIN The widget for Sidebar Custom Menu
class luomtv_widget_custom_menu extends WP_Widget {
 
    function __construct() {
        parent::__construct(
         
            // Base ID
            'luomtv_widget_custom_menu', 
             
            // Widget name
            __('Sidebar Custom Menu', 'luomtv'),
             
            // Widget description
            array('description' => __('Show custom menu for current page or category', 'luomtv'),)
       );
    }
     
    // Creating widget front-end
     
    public function widget($args, $instance) {
        $title = apply_filters('widget_title', $instance['title']);
         
        // before and after widget arguments are defined by themes
        echo $args['before_widget'];     
        luomtv_show_sidebar_custom_menu();
        echo $args['after_widget'];
    }
     
    // Widget Backend
    public function form($instance) {

        if (isset($instance[ 'title' ])) {
            $title = $instance[ 'title' ];
        } else {
            $title = __('Sidebar Custom Menu', 'luomtv');
        }
        // Widget admin form ?>
        <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><?php
    }
     
    // Updating widget replacing old instances with new
    public function update($new_instance, $old_instance) {

        $instance = array();
        $instance['title'] = (! empty($new_instance['title'])) ? strip_tags($new_instance['title']) : '';
        return $instance;
    }
 
// Class luomtv_widget_custom_menu ends here
} 
 

// Register and load the widget
function luomtv_load_widget() {
    register_widget('luomtv_widget_custom_menu');
}
add_action('widgets_init', 'luomtv_load_widget');
// END The widget for Sidebar Custom Menu


// *************************************
// BEGIN Showing the Sidebar Custom Menu
function luomtv_get_category_has_custom_menu() {

    // Get the ID of current category
    if (is_category()) {
        $cat_id = get_query_var('cat');

    // or get the category ID of current post
    } else if (is_single()) {
        $categories = get_the_category();
        $cat_id = $categories[0]->cat_ID;
    
    } else {
        return false;
    }

    while ($cat_id) {

        $cat_menu = get_term_meta($cat_id, 'cat_menu', true);
        
        // If a custom menu is set to this category, then return it
        if ($cat_menu && $cat_menu != '0') {
            return $cat_id;
        }

        // If not...

        $this_cat = get_category($cat_id);

        // By assigning $cat_id to itself parent category,
        $cat_id = $this_cat->category_parent; // if this is a top level category then
                                              // $cat_id = NULL
                                              // and current while loop will be terminated.

        // This category is the top level category so far
        $top_level_cat = $this_cat->cat_ID;
    }

    return $top_level_cat;
}

function luomtv_show_sidebar_custom_menu() {

    // Get the menu ID
    if (!is_page()) {
        $cat_id = luomtv_get_category_has_custom_menu();
        $menu_id = get_term_meta($cat_id, 'cat_menu', true);

    } else {
        $menu_id = get_post_meta(get_the_ID(), '_luomtv_sidebar_menu', true);
    }

    // Show the menu
    if ($menu_id && '0' != $menu_id) {
        wp_nav_menu(array('menu' => $menu_id));
    }
}
// END Showing the Sidebar Custom Menu
?>

Vì lâu không đụng đến PHP nói chung và WordPress nói riêng nên cũng phải Google mãi mới được đôi trăm dòng.

Sử dụng

Đầu tiên là vào mục Appearance / Widgets rồi thêm một widget có tên là “Sidebar Custom Menu” vào vị trí cần hiển thị.

Tìm widget có tên Sidebar Custom Menu

Tiếp theo là tạo các trình đơn trong mục Appearance / Menus. Rồi sửa chuyên mục (mục Posts / Categories) hoặc khi tạo mới thì chọn trình đơn cần hiển thị.

Đối với trang thì khi sửa hoặc viết bài mới cũng sẽ có tuỳ chọn tương tự.

Còn bài viết thì không phải làm gì, vì nó được xác định theo chuyên mục của bài viết đấy rồi.

Đại khái vậy.