Skip to content

Instantly share code, notes, and snippets.

@gnanet
Forked from daggerhart/custom-menu-items.php
Created July 8, 2016 23:58
Show Gist options
  • Save gnanet/98a24bd586e04edc1806dee2e1b8f4dd to your computer and use it in GitHub Desktop.
Save gnanet/98a24bd586e04edc1806dee2e1b8f4dd to your computer and use it in GitHub Desktop.
WordPress class for easily adding custom menu items dynamically to a menu.
<?php
class custom_menu_items {
// only register with wp hooks once
protected $has_registered = false;
// internal list of menus affected
public $menus = array();
// internal list of new menu items
public $menu_items = array();
private function __construct(){}
private function __wakeup() {}
private function __clone() {}
/**
* Singleton
*
* @return custom_menu_items
*/
static public function get_instance(){
static $instance = null;
if ( is_null( $instance ) ){
$instance = new self;
}
$instance->register();
return $instance;
}
/**
* Hook up plugin with WP
*/
private function register(){
if ( ! $this->has_registered ){
$this->has_registered = true;
add_filter( 'wp_get_nav_menu_items', array( $this, 'wp_get_nav_menu_items' ), 20, 2 );
add_filter( 'wp_get_nav_menu_object', array( $this, 'wp_get_nav_menu_object' ), 20,2 );
}
}
/**
* Update the menu items count when building the menu
*
* @param $menu_obj
* @param $menu
*
* @return mixed
*/
function wp_get_nav_menu_object( $menu_obj, $menu ){
if ( is_a( $menu_obj, 'WP_Term' ) && isset( $this->menus[ $menu_obj->slug ] ) ){
$menu_obj->count += $this->count_menu_items( $menu_obj->slug );
}
return $menu_obj;
}
/**
* @param $items
* @param $menu
*
* @return mixed
*/
function wp_get_nav_menu_items( $items, $menu ){
if ( isset( $this->menus[ $menu->slug ] ) ) {
$new_items = $this->get_menu_items( $menu->slug );
if ( ! empty( $new_items ) ) {
foreach ( $new_items as $new_item ) {
$items[] = $this->make_item_obj( $new_item );
}
}
$items = $this->fix_menu_orders( $items );
}
return $items;
}
/**
* Entry point.
* Add a new menu item to the list of custom menu items
*
* @param $menu_slug
* @param $title
* @param $url
* @param $order
* @param $parent
* @param null $ID
*/
static public function add_item( $menu_slug, $title, $url, $order = 0, $parent = 0, $ID = null ){
$instance = custom_menu_items::get_instance();
$instance->menus[ $menu_slug ] = $menu_slug;
$instance->menu_items[] = array(
'menu' => $menu_slug,
'title' => $title,
'url' => $url,
'order' => $order,
'parent' => $parent,
'ID' => $ID,
);
}
/**
* Get an array of new menu items for a specific menu slug
*
* @param $menu_slug
*
* @return array
*/
private function get_menu_items( $menu_slug ){
$items = array();
if ( isset( $this->menus[ $menu_slug ] ) ) {
$items = array_filter( $this->menu_items, function ( $item ) use ( $menu_slug ) {
return $item['menu'] == $menu_slug;
} );
}
return $items;
}
/**
* Count the number of new menu items we are adding to an individual menu
*
* @param $menu_slug
*
* @return int
*/
private function count_menu_items( $menu_slug ){
if ( ! isset( $this->menus[ $menu_slug ] ) ) {
return 0;
}
$items = $this->get_menu_items( $menu_slug );
return count( $items );
}
/**
* Helper to create item IDs
*
* @param $item
*
* @return int
*/
private function make_item_ID( $item ){
return 1000000 + $item['order'] + $item['parent'];
}
/**
* Make a stored item array into a menu item object
*
* @param array $item
*
* @return mixed
*/
private function make_item_obj( $item ) {
// generic object made to look like a post object
$item_obj = new stdClass();
$item_obj->ID = ( $item['ID'] ) ? $item['ID'] : $this->make_item_ID( $item );
$item_obj->title = $item['title'];
$item_obj->url = $item['url'];
$item_obj->menu_order = $item['order'];
$item_obj->menu_item_parent = $item['parent'];
// menu specific properties
$item_obj->db_id = $item_obj->ID;
$item_obj->type = '';
$item_obj->object = '';
$item_obj->object_id = '';
// output attributes
$item_obj->classes = array();
$item_obj->target = '';
$item_obj->attr_title = '';
$item_obj->description = '';
$item_obj->xfn = '';
$item_obj->status = '';
return $item_obj;
}
/**
* Menu items with the same menu_order property cause a conflict. This
* method attempts to provide each menu item with its own unique order value.
*
* @param $items
*
* @return mixed
*/
private function fix_menu_orders( $items ){
usort($items, '_sort_nav_menu_items');
for( $i = 0; $i < count( $items ); $i++ ){
$items[ $i ]->menu_order = $i;
}
return $items;
}
}
@vladcosorg
Copy link

fix_menu_orders is causing ordering issues in WordPress 4.7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment