WordPress at a glance

Registering Taxonomy without attaching it to any Post Type

As I found, WordPress does not allow creating a taxonomy independently from binding it to any post type. I mean, we can register taxonomy without binding it to post type, but when we go to the page for creating elements of this taxonomy, we will be in the "Posts" item of the Admin menu. But we need to have our own, separate taxonomy page in the Admin menu. I explain it by steps...

Task

I need to keep some data (strings) with a possibility to add some more data there (it is unknown in advance; the code will expand). Further, these data (strings) will be used for WordPress users (users will have setting skills: for example, a user is able to cook, wash, clean).

In order to avoid writing a bunch of code and to be able to create, modify and delete these skills (the number of which is expected up to 2000), I made a decision to use WordPress taxonomy for this purpose.

Advantages: it is very easy to register a taxonomy, so we immediately get a table with pagination and search; the ability to add, change or delete data, as well as the ability to expand the data with term metadata. Moreover, we get a whole package of WP functions to display and manage our taxonomy elements. If, for example, you need to keep the data in a separate table or in WP options, managing all of them requires writing additional code for everything: from the creation of the WordPress admin page to the functions for outputting our data. Still, utilizing the taxonomy registering we get all this at once.

Disadvantages: unused fields in the taxonomy table, but this is a trifle, so actually there are no disadvantages!

The final task is creation of taxonomy that is not tied to a post type and has its own menu item in the admin panel.

Solution

Register a taxonomy. We need it only for data storage, so it is not public (invisible on the front) and without any typical taxonomy parameters:

## create taxonomy skills
add_action( 'init', function (){
	register_taxonomy( 'skills', null, array(
		'labels'                => array(
			'name'          => 'Skills',
			'singular_name' => 'Skill',
			'add_new_item'  => 'Add new Skill',
		),
		'public'                => false,
		'show_ui'               => true,  // equal to the argument public
		'show_in_rest'          => false, // add to REST API
		'hierarchical'          => false,
		'update_count_callback' => '__return_null',
	) );
}, 20 );

## add a taxonomy menu item to the admin menu
add_action( 'admin_menu', 'add_skills_menu_item' );
function add_skills_menu_item(){
	add_menu_page( 'Skills', 'Skills', 'manage_options', "edit-tags.php?taxonomy=skills", null, 'dashicons-awards', 9 );
}

As a result get:

As we can see, everything works fine, but when we enter the skills taxonomy page, we are in the "Posts" menu item.

Now the problem is in disabling "Posts" menu item and make active menu item of taxonomy. There are no any hooks in WordPress to do it simply, so we need some hacks.

Let's do it by replacing our previous code around adding menu item:

## add a taxonomy menu item to the admin menu
add_action( 'admin_menu', 'add_skills_menu_item' );
function add_skills_menu_item(){
	$taxname = 'skills';

	$is_skills = isset($_GET['taxonomy']) && $_GET['taxonomy'] === $taxname;

	// cancel 'current' for posts (taxonomy attaches there by default, even if not set post_type when taxonomy is registered
	$is_skills && add_filter( 'parent_file', function($parent_file){
		return false;
	} );

	// add a taxonomy menu item
	$menu_title = 'Skills';
	add_menu_page( 'Skills', $menu_title, 'manage_options', "edit-tags.php?taxonomy=$taxname", null, 'dashicons-awards', 9 );
	// fix some parameters of the added menu item
	$menu_item = & $GLOBALS['menu'][ key(wp_list_filter( $GLOBALS['menu'], [$menu_title] )) ];
	foreach( $menu_item as & $val ){
		// add 'current' class if need
		if( false !== strpos($val, 'menu-top') )
			$val = 'menu-top'. ( $is_skills ? ' current' : '' );

		$val = preg_replace('~toplevel_page[^ ]+~', "toplevel_page_$taxname", $val );
	}
}

Have:

That's it!

-

My task required me to hide unnecessary fields and add a field to bulk add skills.

Whole previous code, including the code for my further tasks:

<?php

// create taxonomy skills
add_action( 'init', function (){
	register_taxonomy( 'skills', null, array(
		'labels'                => array(
			'name'          => 'Skills',
			'singular_name' => 'Skill',
			'add_new_item'  => 'Add new Skill',
		),
		'public'                => false,
		'show_ui'               => true,  // equal to the argument public
		'show_in_rest'          => false, // add to REST API
		'hierarchical'          => false,
		'update_count_callback' => '__return_null',
	) );

	massadd_skills_handler();
}, 20 );

## add a taxonomy menu item to the admin menu
add_action( 'admin_menu', 'add_skills_menu_item' );
function add_skills_menu_item(){
	$taxname = 'skills';

	$is_skills = isset($_GET['taxonomy']) && $_GET['taxonomy'] === $taxname;

	// cancel 'current' for posts (taxonomy attaches there by default, even if not set post_type when taxonomy is registered
	$is_skills && add_filter( 'parent_file', function($parent_file){
		return false;
	} );

	// add a taxonomy menu item
	$menu_title = 'Skills';
	add_menu_page( 'Skills', $menu_title, 'manage_options', "edit-tags.php?taxonomy=$taxname", null, 'dashicons-awards', 9 );
	// fix some parameters of the added menu item
	$menu_item = & $GLOBALS['menu'][ key(wp_list_filter( $GLOBALS['menu'], [$menu_title] )) ];
	foreach( $menu_item as & $val ){
		// add 'current' class if need
		if( false !== strpos($val, 'menu-top') )
			$val = 'menu-top'. ( $is_skills ? ' current' : '' );

		$val = preg_replace('~toplevel_page[^ ]+~', "toplevel_page_$taxname", $val );
	}
}

## request to bulk add skills
function massadd_skills_handler(){
	if( empty($_POST['massadd_skills']) || ! trim($_POST['massadd_skills']) || ! current_user_can('manage_options') )
		return; // only admin

	$new_skills = wp_unslash( trim($_POST['massadd_skills']) );
	$new_skills = array_filter( array_map( 'trim', explode( "\n", $new_skills ) ) );

	$err_names = [];
	foreach( $new_skills as $skill_name ){
		$data = wp_insert_term( $skill_name, 'skills' );
		if( is_wp_error($data) )
			$err_names[ $skill_name ] = $data->get_error_message();
	}

	// the message about the query result
	add_action( 'admin_notices', function() use ($err_names, $new_skills){
		$added_count = count($new_skills) - count($err_names);
		$message = "<p>Skills added: $added_count</p>";

		if( $err_names ){
			$message .= '<p style="color:red;">';
			$message .= 'Failed to add: <br>';
			foreach( $err_names as $skill_name => $err_msg )
				$message .= '<b>'. esc_html($skill_name) . "</b>: $err_msg <br>";
			$message .= "</p>";
		}

		echo '<div class="notice notice-success is-dismissible"><div>'. $message .'</div></div>';
	} );

}

## form of Bulk skills adding
add_action( 'skills'.'_add_form', 'massadd_skills_form' );
function massadd_skills_form(){
	if( ! current_user_can('manage_options') ) return; // only admin

	// the code is displayed inside the existing form, so close it and open new
	?>
	</form>

	<form method="POST" action="">
		<div class="form-field massadd-skills-wrap">
			<h2>Bulk skills adding </h2>
			<p>The list of skills each on a new line.</p>
			<textarea name="massadd_skills" rows="5" style="width:95%"></textarea>
		</div>
	<?php
	submit_button( 'Add all skills' );
}

## its styles on the taxonomy skills page and on the edit item skills page
## hide no need fields
add_action( 'admin_head', 'hide_unwanted_skill_field' );
function hide_unwanted_skill_field(){
	if( get_current_screen()->id === 'edit-skills' ){
		echo '
		<style>
			.form-field.term-slug-wrap{ display:none; }
			.form-field.term-description-wrap{ display:none; }
		</style>';
	}
}

## Remove no need columns
add_filter( 'manage_'.'edit-skills'.'_columns', function( $columns ){
	unset( $columns['description'], $columns['posts'] );
	return $columns;
});

Have:

No comments
    Hello, !     Log In . Register