69
Mastering WordPress Custom Post Types By Mike Schinkel – @mikeschinkel For WordCamp Atlanta Feb 2012

Mastering Custom Post Types - WordCamp Atlanta 2012

Embed Size (px)

DESCRIPTION

Presentation of WordCamp Atlanta 2012 - Covers a collection of PHP-based techniques for using Custom Post Types to get the most out of WordPress.

Citation preview

Page 1: Mastering Custom Post Types - WordCamp Atlanta 2012

Mastering WordPressCustom Post TypesBy Mike Schinkel – @mikeschinkelFor WordCamp Atlanta Feb 2012

Page 2: Mastering Custom Post Types - WordCamp Atlanta 2012

Slides Available Now

On SpeakerDeck (PDF): http://speakerdeck.com/u/mikeschinkel/

On SlideShare (PPTX): http://www.slideshare.net/mikeschinkel/

The Code (either one): https://gist.github.com/1728805 http://bit.ly/mastering-wordpress-custom-post-ty

pes-wordcamp-atlanta-2012

Page 3: Mastering Custom Post Types - WordCamp Atlanta 2012

416style

Page 4: Mastering Custom Post Types - WordCamp Atlanta 2012

Who Am I?

Conflicted: ½ Entrepreneur, ½ Web Developer

Founder & Prez of NewClarity in Atlanta – We: Build Plugins for Distribution via WordPress.org Extend WordPress for Vertical Market CMS

Active Teacher 350+ Answers on SE’s WordPress Answers Former Meetup Organizer & Presenter Former Developer Trainer

Page 5: Mastering Custom Post Types - WordCamp Atlanta 2012

What do I know?

Tends to develop deep expertise in narrow areas.

2010-2012 – WordPress + PHP + MySQL + jQuery

2007-2009 – Drupal + PHP + MySQL

1995-2006 – ASP + VBScript + MS SQL Server

1993-1994 – Visual Basic + Access Database

1987-1993 – Clipper for DOS

1985-1986 – dBase II & dBase III

1983-1984 – Turbo Pascal

Page 6: Mastering Custom Post Types - WordCamp Atlanta 2012
Page 7: Mastering Custom Post Types - WordCamp Atlanta 2012

My Primary ToolsetMostly commercial because they are worth it.

PhpStorm ($99) + Zend Debugger (free)

Navicat for MySQL ($79)

VirtualHostX ($40)

Transmit for FTP ($34)/FileZilla (free)

HTTPScoop ($15)

Apache+PHP (on Mac OS X), MySQL (free)

Page 8: Mastering Custom Post Types - WordCamp Atlanta 2012

What We’ll Cover TodayEverything will be in PHP

1. Define a Custom Post Type in PHP

2. Custom Form ("MetaBox") + Custom Fields

3. Theme Template for Custom Post Type

4. Custom Columns in Admin Post List

5. Custom Taxonomies for any Post Type

Page 9: Mastering Custom Post Types - WordCamp Atlanta 2012

Today (cont’d)

6. Configure Edit Screens for Posts and Pages

7. Parent Post Field in a Post Editor Metabox

8. Querying Custom Post Types

9. Hierarchical URLs for Custom Post Types

10.Custom Post Type Filters in Admin Post List

Recognize and Bypass the Various Gotchas!

Page 10: Mastering Custom Post Types - WordCamp Atlanta 2012

What Today’s Post Type?

Example Today:

“PressedComics”

Page 11: Mastering Custom Post Types - WordCamp Atlanta 2012

Inspiration for our example:

Small World Comics

Page 12: Mastering Custom Post Types - WordCamp Atlanta 2012

Fire up PhpStorm!

Page 13: Mastering Custom Post Types - WordCamp Atlanta 2012

Create a Child Theme

/wp-content/themes/mikes_theme/styles.css

/*Theme Name: Mike's ThemeDescription: Child Theme of Twenty ElevenTemplate: twentyeleven*/@import url("../twentyeleven/style.css");

Page 14: Mastering Custom Post Types - WordCamp Atlanta 2012

Add a functions.php file

/wp-content/themes/mikes_theme/functions.php<?php/* * functions.php for mikes_theme * */

Page 15: Mastering Custom Post Types - WordCamp Atlanta 2012

Part 1

Add a Custom Post TypeCalled "Comics"

Page 16: Mastering Custom Post Types - WordCamp Atlanta 2012

Register Post Type

<?php/* * functions.php for mikes_theme * */

register_post_type( 'comic' );

Page 17: Mastering Custom Post Types - WordCamp Atlanta 2012

Must Use "init" Hook

<?php

add_action( 'init', 'mikes_theme_init' );function mikes_theme_init() { register_post_type( 'comic' );}

Page 18: Mastering Custom Post Types - WordCamp Atlanta 2012

Must Make "public"

function mikes_theme_init() { register_post_type( 'comic', array( 'public' => true, ));}

But!

Page 19: Mastering Custom Post Types - WordCamp Atlanta 2012

Must Give "label"

function mikes_theme_init() { register_post_type( 'comic', array( 'public' => true, 'label' => 'Comics', ));}

Now!

Page 20: Mastering Custom Post Types - WordCamp Atlanta 2012

Y URL NO LOAD COMIC?!?

Page 21: Mastering Custom Post Types - WordCamp Atlanta 2012

Refresh Permalinks

Page 22: Mastering Custom Post Types - WordCamp Atlanta 2012

Like Candy from a Baby!

Page 23: Mastering Custom Post Types - WordCamp Atlanta 2012

Need "with_front" to omit

/blog/ from URL: function mikes_theme_init() { register_post_type( 'comic', array( 'public' => true, 'label' => 'Comics', 'rewrite' => array( 'with_front' => false, ), ));}

Better, but quite right:

Page 24: Mastering Custom Post Types - WordCamp Atlanta 2012

Need "slug" to make URL start with /comics/

(pural): function mikes_theme_init() { register_post_type( 'comic', array( 'public' => true, 'label' => 'Comics', 'rewrite' => array( 'with_front' => false, 'slug' => 'comics', ), ));} Perfecto!

Page 25: Mastering Custom Post Types - WordCamp Atlanta 2012

THE COMIC POST LISTIN THE ADMIN:

Page 26: Mastering Custom Post Types - WordCamp Atlanta 2012

THE COMIC POST EDIT SCREEN:

Page 27: Mastering Custom Post Types - WordCamp Atlanta 2012

Part 2

Add aCustom Form (a "Metabox")

and Custom Fields

Page 28: Mastering Custom Post Types - WordCamp Atlanta 2012

The "Comic Information"Custom Fields Metabox

Page 29: Mastering Custom Post Types - WordCamp Atlanta 2012

Add a Custom Metabox:

add_action('add_meta_boxes','mikes_theme_add_meta_boxes');function mikes_theme_add_meta_boxes( $post_type ) { if ( 'comic' == $post_type ) add_meta_box( 'comic_info_box', // $id 'Comic Information', // $title 'mikes_theme_comic_meta_box', // $callback 'comic', // $page, 'normal', // $context, 'high' ); // $priority }

Page 30: Mastering Custom Post Types - WordCamp Atlanta 2012

Add a Custom Metabox Callback Function

function mikes_theme_comic_meta_box( $post ) { $cartoonist = ''; $since= ''; $website = ''; $html =<<<HTML<table> <tr> <th><label for="cartoonist">Cartoonist:</label></th> <td><input type="text" name="cartoonist" value="{$cartoonist}" size="25" /></td> </tr> <tr> <th><label for="since">Since:</label></th> <td><input type="text" name="since" value="{$since}" size="15" /></td> </tr> <tr> <th><label for="website">Website:</label></th> <td><input type="text" name="website" value="{$website}" size="25" /></td> </tr></table>HTML; echo $html;}

Page 31: Mastering Custom Post Types - WordCamp Atlanta 2012

Update Custom Fields:

add_action( 'save_post', 'mikes_theme_save_post');function mikes_theme_save_post( $post_id ) { if ( 'comic' == $post_type ) { update_post_meta($post_id,'_cartoonist',$_POST['cartoonist']); update_post_meta($post_id,'_since',$_POST['since']); update_post_meta($post_id,'_website',$_POST['website']); }}

Note the leading underscores on the post meta 'key' names (2nd

parameter)

Page 32: Mastering Custom Post Types - WordCamp Atlanta 2012

Load Custom Fields:

function mikes_theme_comic_meta_box( $post ) { $cartoonist = get_post_meta( $post->ID, '_cartoonist', true ); $since = get_post_meta( $post->ID, '_since', true ); $website = get_post_meta( $post->ID, '_website', true ); $html =<<<HTML<table>

Page 33: Mastering Custom Post Types - WordCamp Atlanta 2012

Part 3

Create a Theme Template File

for a Custom Post Type

Page 34: Mastering Custom Post Types - WordCamp Atlanta 2012

Simple Example Theme Template for Custom Post

Type

Page 35: Mastering Custom Post Types - WordCamp Atlanta 2012

/wp-content/themes/

your_theme/single-comic.php:

<?php //Template for displaying single Comic Post Type.get_header();if ( have_posts() ): the_post(); ?><div id="content"><div id="post-<?php the_ID(); ?>" <?php post_class(); ?>><h1 class="entry-title"><?php the_title(); ?></h1>Cartoonist: <?php echo get_post_meta( $post->ID, '_cartoonist', true); ?><br/>Since: <?php echo get_post_meta( $post->ID, '_since', true ); ?><br/>Website: <?php echo get_post_meta( $post->ID, '_website', true ); ?><br/><br/><?php the_content(); ?></div></div><?php endif;get_footer(); ?>

Page 36: Mastering Custom Post Types - WordCamp Atlanta 2012

Part 4

Add Admin Columns for your

Custom Post Type

Page 37: Mastering Custom Post Types - WordCamp Atlanta 2012

Admin Columns for the "Comics" Custom Post Type

Page 38: Mastering Custom Post Types - WordCamp Atlanta 2012

Use hook"manage_edit-{$post_type}_columns"

add_filter( 'manage_edit-comic_columns', 'mikes_theme_manage_edit_comic_columns'

);function mikes_theme_manage_edit_comic_columns( $columns ) { $columns['cartoonist'] = 'Cartoonist'; $columns['website'] = 'Website'; return $columns;}

Page 39: Mastering Custom Post Types - WordCamp Atlanta 2012

Use hook"manage_{$post_type}_posts_custom_columns"

add_filter( 'manage_comic_posts_custom_column', 'mikes_theme_manage_comic_posts_custom_column', 10, 2 );function mikes_theme_manage_comic_posts_custom_column( $column_name, $post_id) { switch ( $column_name ) { case 'cartoonist': echo get_post_meta( $post_id, "_cartoonist", true ); break; case 'website': $website = get_post_meta( $post_id, "_website", true ); $domain = trim( str_replace( 'http://', '', $website ), '/' ); echo "<a href=\"{$website}\">{$domain}</a>"; break; }}

Page 40: Mastering Custom Post Types - WordCamp Atlanta 2012

Better Admin Columns for the "Comics" Custom Post Type

function mikes_theme_manage_edit_comic_columns( $columns ) { $new_columns = array(); foreach( $columns as $key => $value ) { if ( 'date' == $key ) { $new_columns['cartoonist'] = 'Cartoonist'; $new_columns['website'] = 'Website'; } $new_columns[$key] = $value; } return $new_columns;}

Page 41: Mastering Custom Post Types - WordCamp Atlanta 2012

Part 5

Adding a Custom Taxonomy

to a Post Type

Page 42: Mastering Custom Post Types - WordCamp Atlanta 2012

An "Art Style" Taxonomyfor the Comics Post Type

function mikes_theme_init() { ... ... register_taxonomy( 'art-style', 'comic', array( 'hierarchical' => true, 'label' => 'Art Styles', 'rewrite' => array( 'slug' => 'art-styles', 'with_front' => false ), ));

Page 43: Mastering Custom Post Types - WordCamp Atlanta 2012

Part 6

Configure Post Edit Screens

(including Posts and Pages)

Page 44: Mastering Custom Post Types - WordCamp Atlanta 2012

'supports' => array('title','excerpt','thumbnail')

gets this:

Page 45: Mastering Custom Post Types - WordCamp Atlanta 2012

Configure an "Episodes" Post Type Specify what it

"supports"

function mikes_theme_init() { ... ... register_post_type( 'episode', array( 'label' => 'Episodes', 'public' => true, 'rewrite' => array( 'slug' =>'episodes', 'with_front' => false ), 'supports' => array( 'title', 'thumbnail', 'excerpt' ), ));

Page 46: Mastering Custom Post Types - WordCamp Atlanta 2012

Options for "supports"

'title' – Includes permalink 'editor' – WYSIWYG+HTML content 'author' 'thumbnail' – Featured, theme must support post-thumbnails 'excerpt' 'trackbacks' 'custom-fields' 'comments – Will add comment count balloon on edit screen 'revisions' – Will store revisions 'page-attributes' – Menu order, Parent if hierarchical=true 'post-formats' – Add post formats

Page 47: Mastering Custom Post Types - WordCamp Atlanta 2012

Part 7

Parent Post Field in a Post Editor Metabox

(useful for non-same post types)

Page 48: Mastering Custom Post Types - WordCamp Atlanta 2012

Create a Metabox to Associate an Episode with a Comic: 'parent_id' is

key

function mikes_theme_add_meta_boxes($post_type) { ... if ( 'episode' == $post_type ) add_meta_box( 'episode_info_box', 'Episode Info', 'mikes_theme_episode_meta_box', 'episode','side','low' );}function mikes_theme_episode_meta_box( $post ) { $html =<<<HTML<table><tr><th><label for="parent_id">Comic:</label></th><td><input type="text" name="parent_id" value="{$post->post_parent}" /></td></tr></table>HTML; echo $html;}

Page 49: Mastering Custom Post Types - WordCamp Atlanta 2012

Part 8

Querying Custom Post

Types

Page 50: Mastering Custom Post Types - WordCamp Atlanta 2012

Use the "Comic" Drop Down within the hook 'mikes_theme_episode_meta_box'

function mikes_theme_episode_meta_box( $post ) { $select_html = mikes_theme_comic_dropdown( $post->post_parent ); $html =<<<HTML<table><tr><th><label for="parent_id">Comic:</label></th><td>{$select_html}</td></tr></table>HTML; echo $html;}

Page 51: Mastering Custom Post Types - WordCamp Atlanta 2012

Use WP_Query() to create a "Comic" Drop Down for "Episode" Parent

Selection

function mikes_theme_comic_dropdown( $selected_id ) { $query = new WP_Query( 'post_type=comic&posts_per_page=-1' ); $comics = array(); foreach( $query->posts as $comic ) { $title = get_the_title( $comic->ID ); $selected = $comic->ID == intval( $selected_id ) ? ' selected' : ''; $comics[ $comic->ID ] = <<<HTML<option value="{$comic->ID}"{$selected}>{$title}</option>HTML; } $comics = implode( '', $comics ); $html =<<<HTML<select name="parent_id"><option value="0">None selected</option>{$comics}</select>HTML; return $html;}

Page 52: Mastering Custom Post Types - WordCamp Atlanta 2012

Part 9

Hierarchical URLs for Custom Post Types

Page 53: Mastering Custom Post Types - WordCamp Atlanta 2012

Hierarchical URLs:

Page 54: Mastering Custom Post Types - WordCamp Atlanta 2012

Enable /comics/{$comic}/{$episode}/such as /comics/small-world/xmas-2012/

1. Call add_rewrite_rule() in 'init' hook.

2. Add 'query_var' arguments to 'comic' and 'episode' post types named 'comic_qv' and 'episode_qv', respectively.

3. Add 'post_type_link' hook.

4. Add 'request' hook.

Page 55: Mastering Custom Post Types - WordCamp Atlanta 2012

Call add_rewrite_rule() in 'init' hook.And add 'query_var' arguments

add_rewrite_rule( '^comics/([^/]*)/([^/]*)/?', 'index.php?post_type=episode&comic_qv=$matches[1]&episode_qv=$matches[2]', 'top');register_post_type( 'comic', array( ... 'query_var' => 'comic_qv', ...));register_post_type( 'episode', array( ... 'query_var' => 'episode_qv', ...));

Page 56: Mastering Custom Post Types - WordCamp Atlanta 2012

Add 'post_type_link' hook.

add_action( 'post_type_link', 'mikes_theme_post_type_link', 10, 4 );function mikes_theme_post_type_link( $link, $post, $leavename, $sample ) { if ( 'episode' == $post->post_type ) { $parent = get_post( $post->post_parent ); $comic_slug = isset( $parent->post_name ) ? $parent->post_name ' : '%comic%'; $episode_slug = $sample ? '%postname%' : $post->post_name; $link = preg_replace( '#^(https?://[^/]+/).*$#', "$1comics/{$comic_slug}/{$episode_slug}/", $link ); } return $link;}

Page 57: Mastering Custom Post Types - WordCamp Atlanta 2012

Add 'request' hook.

add_action( 'request', 'mikes_theme_request' );function mikes_theme_request( $query_vars ) { global $wp; if ( ! is_admin() && isset( $query_vars['post_type'] ) && 'episode' == $query_vars['post_type'] ) { $comic = get_page_by_path( $query_vars['comic_qv'], OBJECT, 'comic' ); $episode_query = new WP_Query( array( 'name' => $query_vars['episode_qv'], 'post_type' => 'episode', 'fields' => 'ids', 'post_parent' => $comic->ID, 'posts_per_page' => 1, 'suppress_filters' => true, )); if ( 0 == count( $episode_query->posts ) ) { $query_vars = array( 'error' => '404' ); } } return $query_vars;}

Page 58: Mastering Custom Post Types - WordCamp Atlanta 2012

To get our "Episodes" Page:

Page 59: Mastering Custom Post Types - WordCamp Atlanta 2012

/wp-content/themes/

your_theme/single-episode.php:

<?phpget_header();if ( have_posts() ): the_post(); ?><div id="content"> <div id="post-<?php the_ID(); ?>" <?php post_class(); ?>> <?php edit_post_link( 'Edit', '<div class="edit-link">', '</div>' ); ?> <?php previous_post_link( '%link', '&lt;&lt;&lt;Previous' ); ?> &nbsp;&nbsp;&nbsp; <?php next_post_link( '%link', 'Next&gt&gt&gt;' ); ?> <h2 class="entry-parent">Comic: <?php echo get_the_title( $post->post_parent ); ?></h2> <h1 class="entry-title"><?php the_title(); ?></h1> <?php the_content(); ?> <?php if ( has_post_thumbnail( $post->ID ) ) $image_html = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'single-post-thumbnail' ); ?> <img src="<?php echo $image_html[0]; ?>"> </div></div><?php endif;get_footer(); ?>

Page 60: Mastering Custom Post Types - WordCamp Atlanta 2012

Part 10

Custom Post Type Filters in Admin Post List

Page 61: Mastering Custom Post Types - WordCamp Atlanta 2012

Filtering our "Episodes:"

Page 62: Mastering Custom Post Types - WordCamp Atlanta 2012

Or Not:

Page 63: Mastering Custom Post Types - WordCamp Atlanta 2012

Enable /comics/{$comic}/{$episode}/such as /comics/small-world/xmas-2012/

1. Make mikes_theme_comic_dropdown() reusable.

2. Add 'restrict_manage_posts' hook.

3. Add 'pre_get_posts' hook.

Page 64: Mastering Custom Post Types - WordCamp Atlanta 2012

Make mikes_theme_comic_dropdown() reusable

function mikes_theme_comic_dropdown( $selected_id, $name = 'parent_id' ) { $query = new WP_Query( 'post_type=comic&posts_per_page=-1' ); $comics = array(); foreach( $query->posts as $comic ) { $title = get_the_title( $comic->ID ); $selected = $comic->ID == intval( $selected_id ) ? ' selected' : ''; $comics[ $comic->ID ] = <<<HTML<option value="{$comic->ID}"{$selected}>{$title}</option>HTML; } $comics = implode( '', $comics ); $html =<<<HTML<select name="{$name}"><option value="0">None selected</option>{$comics}</select>HTML; return $html;}

Page 65: Mastering Custom Post Types - WordCamp Atlanta 2012

Add 'restrict_manage_posts' hook.And add 'pre_get_posts' hook.

add_action( 'restrict_manage_posts', 'mikes_theme_restrict_manage_posts' );function mikes_theme_restrict_manage_posts() { $comic_id = empty( $_GET['comic_id'] ) ? 0 : $_GET['comic_id']; echo 'Comic: ' . mikes_theme_comic_dropdown( $comic_id, 'comic_id' );}

add_filter('pre_get_posts','mikes_theme_pre_get_posts');function mikes_theme_pre_get_posts( $query ) { global $pagenow, $typenow, $wp_the_query; if ( 'edit.php' == $pagenow && 'episode' == $typenow && $query === $wp_the_query && ! empty( $_GET['comic_id'] ) ) { $query->set( 'post_parent', intval( $_GET['comic_id'] ) ); }}

Page 66: Mastering Custom Post Types - WordCamp Atlanta 2012

Takeaway

You can do practically anything you put your mind to with

WordPress' CPTs and a little PHP!

Page 67: Mastering Custom Post Types - WordCamp Atlanta 2012

But Wait!

There's More…

Page 68: Mastering Custom Post Types - WordCamp Atlanta 2012

Look for “Sunrise”

A Platform Extension for WordPress

Targeting needs of Professional Site Builders

To be GPL and freely distributed

Designed to be Modular

Goal: To Have Community of Module Contributors

Timeline: Pre-Alpha now Closed Beta – Q1 2012 Open Beta – Q2 2012 Release – Hmm…

Page 69: Mastering Custom Post Types - WordCamp Atlanta 2012

Thank You

To Contact Me:Twitter: @mikeschinkelhttp://about.me/mikeschinkel