getherbert / herbert

The WordPress Plugin Framework:
http://getherbert.com/
634 stars 95 forks source link

Tutorial: How to combine CPTs/Panels into one Top Admin menu #175

Open webstractions opened 7 years ago

webstractions commented 7 years ago

Overview

This is a real-life sample of a plugin I am working on. It is called TVTrax, the inner workings is unimportant, this is just a clip of how to handle multiple custom post types and Herbert panels, all within one top-level Admin Menu.

What I am using for Custom Post Types

I am using an alternative custom post type library, other than the one in the Herbert docs. As of 0.9 the docs listed JJGraingers custom post class requirement as "jjgrainger/wp-custom-post-type-class": "dev-master" for a composer install.

There is a newer version from jjgrainger called PostTypes. You can include this in your plugin composer.json with $ composer require jjgrainger/posttypes.

Whether you use this package or not, there are only two CPT settings that you need concern yourself with ... they are the show_ui and show_in_menu arguments for registering your custom post type. More on this later.

First, Create Your Plugin Panel and subPanels

In this case, I have one main panel and a subpanel for settings. Eventually, I want the main panel on the top, and settings on the bottom of the top-level menu. Additionally, I want the main panel renamed to 'Dashboard'.

Add the following code to your `app/panels.php' file:

<?php namespace TVTrax;

/** @var \Herbert\Framework\Panel $panel */

$panel->add([
    'type'   => 'panel',
    'as'     => 'mainPanel',
    'title'  => 'TV Trax',
    'rename' => 'Dashboard',
    'slug'   => 'tvtrax-index',
    'icon'   => 'dashicons-desktop',
    'order'  => '35.555',
    'uses'   => __NAMESPACE__ . '\Controllers\AdminController@index'
]);

$panel->add([
    'type'   => 'sub-panel',
    'parent' => 'mainPanel',
    'as'     => 'settingsPanel',
    'title'  => 'Settings',
    'slug'   => 'tvtrax-settings',
    'order'  => '0',
    'uses'   => __NAMESPACE__ . '\Controllers\AdminController@settings'
]);

This code assumes that you have an AdminController with index and settings methods. Also, the two methods are to be seperated on the main plugin menu.

You will get an Admin Menu with TV Trax on the top and two submenu items labled 'Dashboard' and 'Settings'.

Register Your Custom Post Types

No matter how you register your custom post types, there are only two arguments that you need to worry about.

In your options, set the following:

$options = [
    'show_ui'      => true,
    'show_in_menu' => 'tvtrax-index'
];

You will notice that the show_in_menu label refers to the tvtrax-index in the previous main panel addition. This is !important.

At this point, you will get a top-level menu for your plugin and an unordered list of submenus in the order of panels then CPTs (or vice-versa). Now we need to list them in the correct order.

Get your hooks in order

I like to have all of my actions and filters in one file. I call it hooks.php and it sits in my \app folder.

Create an apps\hooks.php file. Include it in your herbert.congig.php file with:

/**
 * Auto-load all required files.
 */
'requires' => [
    __DIR__ . '/app/hooks.php'
]

Before we can hook something, we need something to hook it to. In this case, we need an AdminController.

Create a controller in /controllers called AdminController.php. Add the following:

<?php
namespace TVTrax\Controllers;

class AdminController {

    public function index() {

        echo '<h1>TV Trax Dashboard</h1>';

    }

    public function settings() {

        echo '<h1>TV Trax Settings</h1>';
    }

    /**
     * Reorders submenu items for TVTrax Admin Toplevel menu
     * 
     * While the filter passes a menu order array, we don't use it.
     * Instead, we alter the global variable $submenu to accomplish
     * our reording.
     * 
     * @uses filter 'custom_menu_order'
     * 
     * @param array $menu_order
     * @return array
     */
    public static function custom_menu_order( $menu_order ) {

        global $submenu;

        $tv_menu = 'tvtrax-index';

        // Reorder the submenu array
        $arr = array();
        $arr[] = $submenu[$tv_menu][3];
        $arr[] = $submenu[$tv_menu][0];
        $arr[] = $submenu[$tv_menu][1];
        $arr[] = $submenu[$tv_menu][2];
        $arr[] = $submenu[$tv_menu][4];

        // Assign re-ordered array to our top level menu
        $submenu[$tv_menu] = $arr;

        return $menu_order;
    }

}

Inside the hooks.php file, add the following:

// Adjust the order of TVTrax submenu items
add_filter( 'custom_menu_order', [ '\TVTrax\Controllers\AdminController', 'custom_menu_order' ] );

Remember to change your namespaces and mind your R's. Help me improve on this too!

webstractions commented 7 years ago

Perhaps this can be added to the Wiki?