Tutorial – Custom Post Types for WordPress

With WordPress 3, came a slew of new features, the best one by far being Custom Post Types. I had fun using this to generate a “Dogs” page for the charity “Stiggy’s Dogs” that I manage. Stiggy’s Dogs rehabilitates dogs to pair them with returning veterans. I realize there is an abundance of tutorials out there, but they are all slightly different. Hopefully this will help someone else or give them some ideas: Here is an image of the final product as it is today:

WordPress Custom Post Type

There is a plan to develop it further with links that direct you to a chronological archive of posts related to the individual dog (almost like a diary), however this custom post type has already been very beneficial so far, as this static page gets quite a bit of traffic. For the charity, it’s a great way of showing their “portfolio”. Depending on the choices made when posting, the following various images will be displayed:

WordPress Custom Port Type Images

Now that you’ve seen where we’re going, let me show you how we get there. Custom Post Types only really consist of two files, the back-end which I’ve contained in the functions.php, and the front-end which we’ll use a loop for. As for functions.php, this is what it looks like (please take note that I’ve had to add additional php openers in order for the code to display below, if you want the final code, please download below).

Backend: Functions.php

In first line, we’ll fire up the custom post type fundamentals, defining the basics of our new tool. There are quite a number of parameters, so definitely reference the WordPress Codex if working with this for the first time:

// ************ DOGS START **************

// 1. Initialize

add_action( 'init', 'noeltockdotcom_dogs_register' );

// 2. Register Custom Post Type

function noeltockdotcom_dogs_register() {
	
		$labels = array(
			'name' => __( 'Dogs' ),
			'singular_name' => __( 'Dog' ),
			'add_new' => __( 'Add New' ),
			'add_new_item' => __( 'Add New Dog' ),
			'edit' => __( 'Edit' ),
			'edit_item' => __( 'Edit Dog' ),
			'new_item' => __( 'New Dog' ),
			'view' => __( 'View Dog' ),
			'view_item' => __( 'View Dog' ),
			'search_items' => __( 'Search Dogs' ),
			'not_found' => __( 'No dogs found' ),
			'not_found_in_trash' => __( 'No dogs found in deleted items' ),
			'parent' => __( 'Parent Dog' ),
		);
		
		$args = array( 
		'labels' => $labels,
		'description' => __( 'Your Dogs' ), 
		'public' => true,
		'supports' => array('title','editor','author','thumbnail','excerpt','comments'),
                'rewrite' => true,
                'capability_type' => 'post',
		);
		
	register_post_type( 'noeltockdotcom_dogs', $args);

}

Next up we’ll need to register our custom fields. This is what will make our custom post type so special. In the first instance we’ll tell WordPress which fields we want:

<!--?php

// **** 3. Register Custom Fields **** 

// Initialize

	add_action( 'admin_init', 'noeltockdotcom_dogs_admin' );
	
	// Add Meta Boxes
	
	function noeltockdotcom_dogs_admin(){
		add_meta_box("noeltockdotcom_dog_meta", "Dog Details", "noeltockdotcom_dog_meta", "noeltockdotcom_dogs", "normal", "low");
	}
	
	// Display Dog
	
	function noeltockdotcom_dog_meta (){
		global $post;
		$custom = get_post_custom($post->ID);
		$name = $custom["dog_name"][0];
		$status = $custom["dog_status"][0];	
		$gender = $custom["dog_gender"][0];
		$owner = $custom["dog_owner"][0];
		$branch = $custom["dog_branch"][0];
		$placed = $custom["dog_placed"][0];
	?>
	
	<label>Name of Dog:</label>
	<input name="dog_name" value="<?php echo $name; ?>" />
	<label>Status:</label>
	<select name="dog_status">
		<option value="deployed" <?php if (($status == '') || ($status == 'deployed')) { ?>selected="selected"<?php } ?>>Deployed</option>
		<option value="intraining" <?php if ($status == 'intraining') { ?>selected="selected"<?php } ?>>In Training</option>
		<option value="prospective" <?php if ($status == 'prospective') { ?>selected="selected"<?php } ?>>Prospective</option>	
	</select>	
	<label>Gender:</label>
	<select name="dog_gender">
		<option value="male" <?php if (($gender == '') || ($gender == 'male')) { ?>selected="selected"<?php } ?>>Male</option>
		<option value="female" <?php if ($gender == 'female') { ?>selected="selected"<?php } ?>>Female</option>
	</select>	
	<label>New Owner:</label>
	<input name="dog_owner" value="<?php echo $dog_owner; ?>" />
	<label>From Military Branch:</label>
	<select name="dog_branch">
		<option value="army" <?php if (($branch == '') || ($branch == 'army')) { ?>selected="selected"<?php } ?>>Army</option>
		<option value="marines" <?php if ($branch == 'marines') { ?>selected="selected"<?php } ?>>Marine Corps</option>
		<option value="navy" <?php if ($branch == 'navy') { ?>selected="selected"<?php } ?>>Navy</option>
		<option value="airforce" <?php if ($branch == 'airforce') { ?>selected="selected"<?php } ?>>Air Force</option>
		<option value="coastguard" <?php if ($branch == 'coastguard') { ?>selected="selected"<?php } ?>>Coast Guard</option>
		<option value="none" <?php if ($branch == 'none') { ?>selected="selected"<?php } ?>>None</option>
	</select>
	<label>Date Placed:</label>
	<input name="dog_placed" value="<?php echo $dog_placed; ?>" />

	<?php
	}
	
	// Save Fields
	
	add_action ('save_post', 'save_dogs');
	
	function save_dogs(){
		global $post;
		update_post_meta($post->ID, "dog_name", $_POST["dog_name"]);
		update_post_meta($post->ID, "dog_status", $_POST["dog_status"]);
		update_post_meta($post->ID, "dog_gender", $_POST["dog_gender"]);
		update_post_meta($post->ID, "dog_owner", $_POST["dog_owner"]);
		update_post_meta($post->ID, "dog_branch", $_POST["dog_branch"]);
		update_post_meta($post->ID, "dog_placed", $_POST["dog_placed"]);
	}

Last but not least; The great thing about Custom Post Types is that they also have their own dashboard for viewing all posts at once (which you can customize to show any or all of your custom fields). To display these various columns showing your custom content, whip up the following code:

<!--?php

// **** 4. Show Columns in Admin ****

// Initialize

add_action ("manage_posts_custom_column", "dogs_custom_columns");
add_filter ("manage_edit-noeltockdotcom_dogs_columns", "dogs_edit_columns");

// Display Columns

function dogs_edit_columns($columns){
	$columns = array (
		"cb" => "<input type=\"checkbox\" />",
		"title" => "Dog Name",
		"status" => "Status",
		"gender" => "Gender",
		"owner" => "Owner",
		"branch" => "Military Branch",
		"placed" => "Date Placed",
		"description" => "Description",
	);
	
	return $columns;
}
	
// Define Column Content
	
function dogs_custom_columns($column){
	global $post;
	switch ($column){
		case "status":
			$custom = get_post_custom();
			echo $custom["dog_status"][0];
			break;					
		case "gender":
			$custom = get_post_custom();
			echo $custom["dog_gender"][0];
			break;
		case "owner":
			$custom = get_post_custom();
			echo $custom["dog_owner"][0];
			break;	
		case "branch":
			$custom = get_post_custom();
			echo $custom["dog_branch"][0];
			break;	
		case "placed":
			$custom = get_post_custom();
			echo $custom["dog_placed"][0];
			break;	
		case "description":
			the_excerpt();
			break;		
	}
}

At this point, you’ll have successfully created everything needed in the back-end to make this functional. Good job!

Frontend: The Loop

This is actually the easy part, and I’ve tried to throw in some inline CSS (although not best practice) to show you how it all works.

<?php

//The Query

query_posts('post_type=noeltockdotcom_dogs&meta_key=dog_status&orderby=meta_value&order=ASC');

//The Loop

if (have_posts()) : ?>

<?php while (have_posts()) : the_post(); ?>

<?php
	$custom = get_post_custom($post->ID);
	$name = $custom["dog_name"][0];
	$status = $custom["dog_status"][0];
	$gender = $custom["dog_gender"][0];
	$owner = $custom["dog_owner"][0];
	$branch = $custom["dog_branch"][0];
	$placed = $custom["dog_placed"][0];
	$post_image_id = get_post_thumbnail_id($post_to_use->ID);
		if ($post_image_id) {
			$thumbnail = wp_get_attachment_image_src( $post_image_id, 'post-thumbnail', false);
			if ($thumbnail) (string)$thumbnail = $thumbnail[0];
		}
?>
<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<div class="dogblock">
			<div style="float:left;background: url('<?php echo $thumbnail; ?>') no-repeat; width:300px; height:250px;">
				<?php if ($status == "deployed") {
					echo "<img src='http://www.stiggysdogs.org/images/deployed.png' />";
					} elseif ($status == "intraining") {
					echo "<img src='http://www.stiggysdogs.org/images/intraining.png' />";
					} else {
					echo "";
				} ?>
			</div>

			<div class="customdogcontent">
				<h3><?=$name?></h3>
				<?php if ($status == "deployed") {
					echo "<div style=\"background: url('http://www.stiggysdogs.org/images/$branch.png') no-repeat; min-height:60px;;width:320px;padding:20px 0 0 70px;font-size:20px;\">$owner</div>"; } ?>
				<p><?php the_content(); ?></p>
			</div>
</div>
</div>
<div style="padding:20px 0 0 0;width:660px;border-bottom:1px dotted #b49579;clear:both;"></div>
<?php endwhile; ?>

<!-- Stop the post loop and process any code included here only once. This text is generated only if there are posts. Any code included in this part of the document is processed only once. -->

<?php else : ?>

<p>No Dogs :( </p>
<!-- If you are seeing this message, there are no posts to process/display. Any code included in this part of the document is processed only once. -->

<?php endif; ?>

Summary

Custom Post Types for WordPress are incredibly useful as they allow you to create and display content exactly how you want it, making your life as a web designer that much easier. More importantly, they are an excellent way to manage your client’s needs within WordPress (as opposed to custom PHP/MySQL development which may not always be future-proof when considering future versions of WordPress). If you have any questions, please let me know, I’d be glad to help!

Download

Download the two PHP files here (functions.php & loop.php)

Related Links

  • Nice, thanks!

  • cacosquad

    really good tutorial,
    but there’s a problem with wp autosave,
    which wipes all the custom fields.

    i’m trying this fix:

    function save_dogs(){

    if ( defined(‘DOING_AUTOSAVE’) && DOING_AUTOSAVE )
    return $post_id;

    global $post;

    ……
    }

    source
    http://stackoverflow.com/questions/4822175/autosave-and-custom-fields-in-wordpress

  • Great tut, thank you!
    I was wondering where to place or how to use the loop.
    Should I make a custom single.php and paste that in then set it as a page’s template?

  • Dave A

    Thanks for sharing your knowledge. Always great to see. I’ll try to do the same.

  • Robypriz

    Good tutorial,
    I’m new to wp, I’m not able to view loop.php….if I try ?post_type=noeltockdotcom_dogs&meta_key=dog_status&orderby=meta_value&order=ASC doesn’t show custom attributes.
    Thanks
    Roberto

  • smolahloe

    Thank you for a really great tutorial. This is the first ‘Custom Post Type’ that is so easy to understand.

  • was955

    do you know why it is not working with 3.5.1?

  • Mark

    Hi, great article first of all !!!

    I followed this article to implement custom post types and everything is working fine. However I notice that when I search for a client from the admin page using a custom field value.

    Do you need to register these fields in some way to be taken into consideration during the search ?

    Regards,
    Mark

  • rey

    sdry