Advertisement
Creative Coding

Custom Post Type Helper Class

by

For a lot of WordPress projects these days we use custom post types. The WordPress development team created some handy methods to integrate them into your projects. But when you use custom post types, taxonomies and meta boxes frequently, it's quite probable that you're going to repeat yourself. That's why we are going to use the power of these WordPress functions to build a more powerful class, which we can use to quickly register post types, taxonomies and meta boxes.


Call Our Class

This is how we call our class when it's done.

	include('custom-post-type.php');
    
	$book = new Custom_Post_Type( 'Book' );
	$book->add_taxonomy( 'category' );
	$book->add_taxonomy( 'author' );
	
	$book->add_meta_box( 
		'Book Info', 
		array(
			'Year' => 'text',
			'Genre' => 'text'
		)
	);
	
	$book->add_meta_box( 
		'Author Info', 
		array(
			'Name' => 'text',
			'Nationality' => 'text',
			'Birthday' => 'text'
		)
	);

Step 1 The Class, Properties and Methods

We start off with creating the class, main properties, constructor and methods. In this tutorial we will fill them with our programming logic.

	class Custom_Post_Type
	{
		public $post_type_name;
		public $post_type_args;
		public $post_type_labels;
		
		/* Class constructor */
		public function __construct()
		{
			
		}
		
		/* Method which registers the post type */
		public function register_post_type()
		{
			
		}
		
		/* Method to attach the taxonomy to the post type */
		public function add_taxonomy()
		{
			
		}
		
		/* Attaches meta boxes to the post type */
		public function add_meta_box()
		{
			
		}
		
		/* Listens for when the post type being saved */
		public function save()
		{
			
		}
	}

Step 2 The Constructor

Within the constructor we create some important variables, which are used within the entire class. We also call add_action to register the post type and we listen for when the post type is being saved, so we can save our post's meta data. If the post type exists, the add_action is not called, but the $post_type_name is set, so we can add taxonomies and meta boxes to it.

	public function __construct( $name, $args = array(), $labels = array() )
	{
		// Set some important variables
		$this->post_type_name		= strtolower( str_replace( ' ', '_', $name ) );
		$this->post_type_args 		= $args;
		$this->post_type_labels 	= $labels;
		
		// Add action to register the post type, if the post type does not already exist
		if( ! post_type_exists( $this->post_type_name ) )
		{
			add_action( 'init', array( &$this, 'register_post_type' ) );
		}
		
		// Listen for the save post hook
		$this->save();
	}

Step 3 Register the Post Type

Within the register_post_type method, which gets called by the add_action in the constructor, we first determine the name (capitalized) and the plural. With this name and plural we build our labels for the post type and overwrite (and merge) them with the given labels from the $this->post_type_labels variable. Then we create our arguments with the same principle.

	public function register_post_type()
	{
		//Capitilize the words and make it plural
		$name 		= ucwords( str_replace( '_', ' ', $this->post_type_name ) );
		$plural 	= $name . 's';
		
		// We set the default labels based on the post type name and plural. We overwrite them with the given labels.
		$labels = array_merge(
		
			// Default
			array(
				'name' 					=> _x( $plural, 'post type general name' ),
				'singular_name' 		=> _x( $name, 'post type singular name' ),
				'add_new' 				=> _x( 'Add New', strtolower( $name ) ),
				'add_new_item' 			=> __( 'Add New ' . $name ),
				'edit_item' 			=> __( 'Edit ' . $name ),
				'new_item' 				=> __( 'New ' . $name ),
				'all_items' 			=> __( 'All ' . $plural ),
				'view_item' 			=> __( 'View ' . $name ),
				'search_items' 			=> __( 'Search ' . $plural ),
				'not_found' 			=> __( 'No ' . strtolower( $plural ) . ' found'),
				'not_found_in_trash' 	=> __( 'No ' . strtolower( $plural ) . ' found in Trash'), 
				'parent_item_colon' 	=> '',
				'menu_name' 			=> $plural
			),
			
			// Given labels
			$this->post_type_labels
			
		);
		
		// Same principle as the labels. We set some defaults and overwrite them with the given arguments.
		$args = array_merge(
		
			// Default
			array(
				'label' 				=> $plural,
				'labels' 				=> $labels,
				'public' 				=> true,
				'show_ui' 				=> true,
				'supports' 				=> array( 'title', 'editor' ),
				'show_in_nav_menus' 	=> true,
				'_builtin' 				=> false,
			),
			
			// Given args
			$this->post_type_args
			
		);
		
		// Register the post type
		register_post_type( $this->post_type_name, $args );
	}

Step 3 Add Some Taxonomies

First we check if the $name parameter is empty. When it is, we do nothing. When it's not, we create three variables in which we store the information for the taxonomy: $taxonomy_name, $taxonomy_labels and $taxonomy_args.

	public function add_taxonomy( $name, $args = array(), $labels = array() )
	{
		if( ! empty( $name ) )
		{
			// We need to know the post type name, so the new taxonomy can be attached to it.
			$post_type_name = $this->post_type_name;

			// Taxonomy properties
			$taxonomy_name		= strtolower( str_replace( ' ', '_', $name ) );
			$taxonomy_labels	= $labels;
			$taxonomy_args		= $args;

			/* More code coming */
		}
	}

After we've done the first checks and then set some variables, we're going to register the post type. But first we check if the taxonomy already exists.

	if( ! taxonomy_exists( $taxonomy_name ) )
	{
		/* Create taxonomy and attach it to the object type (post type) */
	}
	else
	{
		/* The taxonomy already exists. We are going to attach the existing taxonomy to the object type (post type) */
	}

If the taxonomy doesn't exist, we register it. We use an add_action, but not in the normal way. Normally, the second parameter of the add_action is the name of a function, but since we use different parameters each time, we are going to pass a nameless function (Note: this feature requires PHP 5.3+) and use the use() function. With the use() function we can pass variables to the nameless function. This time we need to pass $taxonomy_name, $post_type_name and $taxonomy_args to register the taxonomy.

	//Capitilize the words and make it plural
	$name 		= ucwords( str_replace( '_', ' ', $name ) );
	$plural 	= $name . 's';
	
	// Default labels, overwrite them with the given labels.
	$labels = array_merge(
	
		// Default
		array(
			'name' 					=> _x( $plural, 'taxonomy general name' ),
			'singular_name' 		=> _x( $name, 'taxonomy singular name' ),
		    'search_items' 			=> __( 'Search ' . $plural ),
		    'all_items' 			=> __( 'All ' . $plural ),
		    'parent_item' 			=> __( 'Parent ' . $name ),
		    'parent_item_colon' 	=> __( 'Parent ' . $name . ':' ),
		    'edit_item' 			=> __( 'Edit ' . $name ),
		    'update_item' 			=> __( 'Update ' . $name ),
		    'add_new_item' 			=> __( 'Add New ' . $name ),
		    'new_item_name' 		=> __( 'New ' . $name . ' Name' ),
		    'menu_name' 			=> __( $name ),
		),

		// Given labels
		$taxonomy_labels

	);

	// Default arguments, overwritten with the given arguments
	$args = array_merge(

		// Default
		array(
			'label'					=> $plural,
			'labels'				=> $labels,
			'public' 				=> true,
			'show_ui' 				=> true,
			'show_in_nav_menus' 	=> true,
			'_builtin' 				=> false,
		),

		// Given
		$taxonomy_args

	);
	
	// Add the taxonomy to the post type
	add_action( 'init',
		function() use( $taxonomy_name, $post_type_name, $args )
		{
			register_taxonomy( $taxonomy_name, $post_type_name, $args );
		}
	);

When the taxonomy doesn't exist, we only attach it to our post type. Just like before, we use a nameless function and the use() function. This time we only need to pass $taxonomy_name and $post_type_name.

	add_action( 'init',
		function() use( $taxonomy_name, $post_type_name )
		{
			register_taxonomy_for_object_type( $taxonomy_name, $post_type_name );
		}
	);

Step 4 Meta Boxes

For registering meta boxes, we need the post type name, so first we determine that. After that we need some variables for the meta box itself and we make the custom meta fields global, so we can access them in the save hook. We won't cover too much detail here, because Tammy Hart made a very useful tutorial about reusable meta boxes already.

	public function add_meta_box( $title, $fields = array(), $context = 'normal', $priority = 'default' )
	{
		if( ! empty( $title ) )
		{
			// We need to know the Post Type name again
			$post_type_name = $this->post_type_name;

			// Meta variables
			$box_id 		= strtolower( str_replace( ' ', '_', $title ) );
			$box_title		= ucwords( str_replace( '_', ' ', $title ) );
			$box_context	= $context;
			$box_priority	= $priority;
			
			// Make the fields global
			global $custom_fields;
			$custom_fields[$title] = $fields;
			
			/* More code coming */
		}
		
	}

When we set the variables and globals, we register the meta box with an add_action. Like before, we use a nameless function. This time we need $box_id, $box_title, $post_type_name, $box_context, $box_priority and $fields.

	add_action( 'admin_init',
		function() use( $box_id, $box_title, $post_type_name, $box_context, $box_priority, $fields )
		{
			add_meta_box(
				$box_id,
				$box_title,
				function( $post, $data )
				{
					global $post;
					
					// Nonce field for some validation
					wp_nonce_field( plugin_basename( __FILE__ ), 'custom_post_type' );
					
					// Get all inputs from $data
					$custom_fields = $data['args'][0];
					
					// Get the saved values
					$meta = get_post_custom( $post->ID );
					
					// Check the array and loop through it
					if( ! empty( $custom_fields ) )
					{
						/* Loop through $custom_fields */
						foreach( $custom_fields as $label => $type )
						{
							$field_id_name 	= strtolower( str_replace( ' ', '_', $data['id'] ) ) . '_' . strtolower( str_replace( ' ', '_', $label ) );
							
							echo '<label for="' . $field_id_name . '">' . $label . '</label><input type="text" name="custom_meta[' . $field_id_name . ']" id="' . $field_id_name . '" value="' . $meta[$field_id_name][0] . '" />';
						}
					}
				
				},
				$post_type_name,
				$box_context,
				$box_priority,
				array( $fields )
			);
		}
	);

Step 5 Save the Post's Meta Data

Save all the post's meta data. We loop through them, using the global $custom_fields. This is also a quick coverage, see Tammy Hart's tutorial about reusable meta boxes.

	public function save()
	{
		// Need the post type name again
		$post_type_name = $this->post_type_name;
	
		add_action( 'save_post',
			function() use( $post_type_name )
			{
				// Deny the WordPress autosave function
				if( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) return;

				if ( ! wp_verify_nonce( $_POST['custom_post_type'], plugin_basename(__FILE__) ) ) return;
			
				global $post;
				
				if( isset( $_POST ) && isset( $post->ID ) && get_post_type( $post->ID ) == $post_type_name )
				{
					global $custom_fields;
					
					// Loop through each meta box
					foreach( $custom_fields as $title => $fields )
					{
						// Loop through all fields
						foreach( $fields as $label => $type )
						{
							$field_id_name 	= strtolower( str_replace( ' ', '_', $title ) ) . '_' . strtolower( str_replace( ' ', '_', $label ) );
							
							update_post_meta( $post->ID, $field_id_name, $_POST['custom_meta'][$field_id_name] );
						}
					
					}
				}
			}
		);
	}

Step 6 Optimize

As you can see we use strtolower( str_replace( ' ', '_', $string ) ) and ucwords( str_replace( '_', ' ', $string ) ) a number of times. The reason of creating this class is that we don't repeat ourselves, so we don't want to do that in this part either. That's why we create some helper methods. In this way we can do this: $name = self::beautify( $string ); instead of $name = strtolower( str_replace( ' ', '_', $title ) );

	public static function beautify( $string )
	{
		return ucwords( str_replace( '_', ' ', $string ) );
	}
	
	public static function uglify( $string )
	{
		return strtolower( str_replace( ' ', '_', $string ) );
	}

Another point is the plural forms we create. We just create them by adding an 's' to the word. But what happens when the word ends with a 'y'? For this reason we create a helper method to determine the plural form of a word. Now we can easily do this: $plural = self::pluralize( $string ) and the plural form of our word will be determined.

	public static function pluralize( $string )
	{
		$last = $string[strlen( $string ) - 1];
		
		if( $last == 'y' )
		{
			$cut = substr( $string, 0, -1 );
			//convert y to ies
			$plural = $cut . 'ies';
		}
		else
		{
			// just attach an s
			$plural = $string . 's';
		}
		
		return $plural;
	}

Wrap up

And now we're done. You can now use this class to easily register post types, taxonomies and meta boxes. If you have any suggestions or questions, just leave a comment, so we can talk about it. Hope to see you next time!

Also, I would like to give some credit to Jeffrey Way. I used his class as inspiration for my class and for this tutorial. Also, I would like to give some credit to Tammy Hart, for building the reusable meta boxes tutorial. Take a look at their work.

Related Posts
  • Code
    WordPress
    Quick Tip: Post Types, Taxonomies and PermalinksPost types taxonomies urls
    Custom Post Types and taxonomies are two powerful features of WordPress. Unfortunately, they can have a tendency to cause problems if developers aren't familiar with how permalinks, URLs, and rewriting works within WordPress. In this quick tip, we aim to cover the topic very briefly to make sure you know all you need to know about WordPress URLs, custom post types, taxonomies, and how they all relate.Read More…
  • Code
    Creative Coding
    Using a Custom Post Type to Create a Home Page BannerUsing a custom post type to create a home page banner
    Sometimes it's useful to have a nice obvious banner on your site's home page—for announcements, snippets that aren't long enough to merit a blog post, or links to new content within the site. But you don't want to be editing your homepage content every time you add a new piece of content, nor do you want to be delving into the code to add content.Read More…
  • Code
    Plugins
    Create a Shortcode to List Posts With Multiple ParametersPost listing shortcode main image400
    On many of the client sites I build, I find there are times when I need to include a post listing on a page. I'm not talking about archive pages here, but adding a custom listing to an existing static page. For example, I might want to list some posts on the 'About' page, or the site may require an in-depth page for a topic, with a list of posts and custom post types related to that topic. One way to do this is by creating a custom page template to include the current page content plus the results of a second custom query, but if you want more flexibility over how you list posts, or just want to do it a few times, a shortcode will be a simpler solution. In this tutorial, I'll show you how to create a simple shortcode to list all posts of a custom post type, and then I'll expand on that to create a shortcode with various parameters that users can specify to list posts however they want.Read More…
  • Code
    Plugins
    Using SuperCPT to Create Custom Post Types, Taxonomies and Meta BoxesSupercpt relief
    With the release of version 3.0, WordPress introduced custom post types and updated custom taxonomies which were introduced in v2.8. Since then, WordPress users and developers are able to create their own post types and taxonomies. People are no longer chained to the dull "posts" and "pages" with the boring "categories" and "tags". Yes, using custom post types, custom taxonomies and custom meta boxes are cool. You know what's cooler? Creating each with a single line of code.Read More…
  • Code
    Plugins
    Integrating Multiple Choice Quizzes in WordPress - Creating the BackendIntegrating multiple choice quizzes in wordpress
    Multiple choice questions are something that most of us have faced at least once in our life. We love them because we can provide correct answers by logically thinking about provided possibilities, even if we don't exactly know the correct answer. Also answering takes less time which makes it so popular. Creating a multiple choice quiz in WordPress can be a very exciting and profitable task. You can use it in your personal blog to attract more visitors, or you can create a premium section with advanced quizzes, or you can create quizzes focusing on popular certification exams. There are numerous possibilities for making it profitable.Read More…
  • Code
    Creative Coding
    Using Backbone Within the WordPress Admin: The Back EndThumbnai02l
    The rumours are true! The WordPress Admin Panel is now using Underscore and Backbone! This means that with minimal effort, we can begin to utilise these fantastic JavaScript libraries in our own plugins. This tutorial will show you exactly how you can do that. We'll create the Admin part of a Quiz plugin. We'll use a simple Custom Post Type to save Questions, and then within each question we'll add a meta box that will allow us to enter up to 4 answers and select which is the correct one. We'll be going through how to use templates, how to hook into click and key-up events, how to save data back to the WordPress database and most importantly, how to 'get your truth out of the Dom', as the creator Jeremy Ashkenas likes to put it.Read More…