Corey edited this page Jan 28, 2014 · 10 revisions


SuperCPT: WordPress Custom Post Types, Meta Boxes, Meta Fields, and Taxonomies

Introducing SuperCPT: Developer-Friendly Custom Post Types, Meta Boxes, Meta Fields, and Custom Taxonomies. When I say developer-friendly, I mean it. The proof is in the pudding, so let's get right into examples.

Adding a Custom Post Type

Let's craft a very basic custom post type for keeping track of movies:

$movies = new Super_Custom_Post_Type( 'movie' );

Now we need to define its properties, hook it into a WordPress action, register it, set its labels, and all that... right? Nope. Labels are automatically generated using "Movie" and "Movies", The post type is automatically registered and put in the admin menu. Your basic custom post type is done. Of course, if you want to customize it, there are ways of doing so.

Overriding plurals, since gooses is silly.

Let's say you were storing information about Geese. If you used 'goose', you'd end up seeing Gooses everywhere, and we all know gooses don't exist. Let's fix that.

$geese = new Super_Custom_Post_Type( 'goose', 'Goose', 'Geese' );

Much better. But nobody cares about gooses or geese so let's go back to our far more interesting movie database.

Custom Taxonomies

Thinking about the properties of movies, they have directors and producers. Both would be good taxonomies, so we can easily reuse them across movies, and you'd be able to sort movies by each (and have RSS feeds, etc). So, let's register both taxonomies then connect them to our movies:

$producers = new Super_Custom_Taxonomy( 'producer' );
$directors = new Super_Custom_Taxonomy( 'director' );
connect_types_and_taxes( $movies, array( $producers, $directors ) );

The connect_types_and_taxes helper function isn't entirely necessary -- you can connect the two within their respective classes, but it consolidates it to one place and ensures you don't forget to add the connection to one or the other. A taxonomy that isn't attached to a post type can exist, but has no Admin UI, so it's used for "behind the scenes" purposes.

Meta Boxes and Post Meta Fields

We've barely done any work and yet our custom post type is coming along marvelously! We can use the content area for a synopsis, and we can attach directors and producers like a breeze. Let's add a meta box to the editing page's sidebar, and in it some custom fields to record some basic information about the film.

$movies->add_meta_box( array(
    'id' => 'basic-information',
    'context' => 'side',
    'fields' => array(
        'tagline' => array(),
        'length' => array( 'label' => 'Running Time', 'type' => 'text', 'placeholder' => 'hh:mm' ),
        'languages' => array( 'type' => 'checkbox', 'options' => array( 'English', 'Spanish', 'Latin' ) ),
        'release_date' => array( 'type' => 'date' ),
) );

There are a number of things happening here. First, we're adding a meta box (which is draggable, collapsible) and it will automatically inherit the title "Basic Information" from the id (the id is mandatory). We're adding it to the sidebar via the context property, and we're adding a few fields. These fields will automatically get nonced, the data will get stored on save, and all the HTML will be written for you. If you've ever used add_meta_box and setup fields manually, you know just how much of a pain it is, and therefore how amazing this is. Furthermore, if you're familiar with add_meta_box, you'll feel right at home. We've tried our hardest to maintain core's existing keys, structure, etc.

Going into the fields, we see more awesomeness happening. This is an associative array where the keys are meta_keys and the values are properties for that field. Fields are text inputs by default, and the labels are derived from the meta keys (and are easily overridden). The properties are associative arrays as well, with a few special keys. Any keys not in the known list are assumed to be HTML attributes and become such for the rendered elements.

Looking at the example above, tagline is a basic text field, release_date will have a jQuery datepicker, length has an HTML5 placeholder and the label will be "Running Time" instead of "Length," and languages become a list of checkboxes. The type for languages could have just as easily been 'radio' or 'select', and if it were 'select', we could have made it a multi-select by adding 'multiple' => 'multiple'; the system is smart enough to know how to code the "options". Lastly, let's say that the options should have underlying values like a language code -- we can make this an associative array, e.g. array( '001' => 'English', '005' => 'Spanish', '017' => 'Latin' ) and the keys in the array will become the "values" in each option.

Visual Editors (WYSIWYG)

Let's add another meta box with just a WYSIWYG editor for our review of the movie.

$movies->add_meta_box( array(
    'id' => 'review-wrapper',
    'title' => 'Review',
    'fields' => array(
        'review' => array( 'type' => 'wysiwyg', 'label' => false )
) );

This just got real cool. Some things of note here are that the box's title and id are different (so we don't have conflicting HTML ids between the field and the box), and the editor has no label. We left the context out, so it defaults to the main column. In the main column, the WYSIWYG editor is quite large with a big toolbar -- what would happen if the context were set to 'side'? A different editor would show that would be more appropriate for the space. In fact, most fields would change a little cosmetically to better fit the space. They're self-aware like a T-1000 and know what they have to do to look good like the T-X.

We've done some really amazing stuff so far, and we've barely scratched the surface.

Post Relationships

Maybe we decided to setup actors as a custom post type so we can have images, a bio, and other information we wouldn't be able to provide with a taxonomy. SuperCPT allows you to create one-to-one, one-to-many, and many-to-many relationships with other post types. At it's most basic, to set that up we'd first add the post type,

$actors = new Super_Custom_Post_Type( 'actor' );

Next, we'll add a multi-select box to our movie editing page where we can select actors from the "actor" custom post type:

$movies->add_meta_box( array(
    'id' => 'actor-wrapper',
    'title' => 'Cast',
    'fields' => array(
        'review' => array( 'type' => 'select', 'label' => false, 'data' => 'actor', 'multiple' => 'multiple' )
) );

If you're following along at home, add a few actors and load up a new/edit movie page. Amazing! If not, see the image below for a demo of what this might look like.

A demo image will go here soon.

Truthfully, this list in particular would get out of hand after a while (given all the actors in the world) so we'd probably want to run a custom interface. Actually -- what if we did want to do that? Can we easily customize the output? In short, yes; there are many, many ways. In fact, this collection of greatness is riddled with actions and filters for you to customize it to your heart's desire! The goal here is to make your work orders of magnitude easier.

Working With Existing Post Types like Posts and Pages

Let's say in our blog we often found ourselves writing about movies, and wanted a way to feature them in the sidebar of the blog post. What if we added a meta box to the basic 'post' type and were able to select the movie just like we select actors in the movies? We can instantiate Super_Custom_Post_Meta (which is used by Super_Custom_Post_Type) to leverage everything we've learned so far to do that:

$post_meta = new Super_Custom_Post_Meta( 'post' );
$post_meta->add_meta_box( array(
    'id' => 'featured-movie',
    'context' => 'side',
    'fields' => array(
        '_movies' => array( 'label' => false, 'type' => 'select', 'data' => 'movie' )
) );

So we instantiated Super_Custom_Post_Meta for the 'post' post type, and we added a meta box to it just like we were adding them to our custom post type through Super_Custom_Post_Type before. This will create a single-select dropdown element in the sidebar just as you'd expect. The only different thing here, and it's not a typo, is that the meta_key starts with an underscore. Since posts have the "Custom Fields" box on the editing page, our custom meta info will show up there as well as in the sidebar. Since we don't want all that confusion, we can employ a little WordPress trick and prefix our field name with an underscore. To recap that, 'movies' would show in the Custom Field box, but '_movies' would not.

Custom Icons

SuperCPT adds custom icons for your custom post types courtesy of Font Awesome. After you define your Custom Post Type, simply call the method set_icon on the object, along with the icon slug. The icon slugs can be found in the SuperCPT Settings page. Here is a demo:

$movies->set_icon( 'film' );

This will result in:

A demo image will go here soon.

There are 350 icons to choose from courtesy of Font Awesome, so have fun :-).

Further Reading