Successfully reported this slideshow.

Mastering Custom Post Types - WordCamp Atlanta 2012

14,642 views

Published on

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

Mastering Custom Post Types - WordCamp Atlanta 2012

  1. 1. Mastering WordPressCustom Post TypesBy Mike Schinkel – @mikeschinkelFor WordCamp Atlanta Feb 2012
  2. 2. Slides Available Now On SpeakerDeck (PDF): On SlideShare(PPTX): The Code (either one):
  3. 3. 416style
  4. 4. 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 CMSActive Teacher  350+ Answers on SE’s WordPress Answers  Former Meetup Organizer & Presenter  Former Developer Trainer
  5. 5. 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
  6. 6. My Primary Toolset Mostly 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)
  7. 7. What We’ll Cover Today Everything will be in PHP1. Define a Custom Post Type in PHP2. Custom Form ("MetaBox") + Custom Fields3. Theme Template for Custom Post Type4. Custom Columns in Admin Post List5. Custom Taxonomies for any Post Type
  8. 8. Today (cont’d)6. Configure Edit Screens for Posts and Pages7. Parent Post Field in a Post Editor Metabox8. Querying Custom Post Types9. Hierarchical URLs for Custom Post Types10. Custom Post Type Filters in Admin Post List Recognize and Bypass the Various Gotchas!
  9. 9. What Today’s Post Type?Example Today:“PressedComics”
  10. 10. Inspiration for our example: Small World Comics
  11. 11. Fire up PhpStorm!
  12. 12. Create a Child Theme /wp-content/themes/mikes_theme/styles.css /* Theme Name: Mikes Theme Description: Child Theme of Twenty Eleven Template: twentyeleven */ @import url("../twentyeleven/style.css");
  13. 13. Add a functions.php file /wp-content/themes/mikes_theme/functions.php <?php /* * functions.php for mikes_theme * */
  14. 14. Part 1 Add aCustom Post TypeCalled "Comics"
  15. 15. Register Post Type<?php/* * functions.php for mikes_theme * */register_post_type( comic );
  16. 16. Must Use "init" Hook<?phpadd_action( init, mikes_theme_init );function mikes_theme_init() {register_post_type( comic );}
  17. 17. Must Make "public" But!function mikes_theme_init() {register_post_type( comic, array( public => true, ));}
  18. 18. Must Give "label" Now!function mikes_theme_init() {register_post_type( comic, array( public => true, label => Comics, ));}
  19. 19. Y URL NO LOAD COMIC?!?
  20. 20. Refresh Permalinks
  21. 21. Like Candy from a Baby!
  22. 22. 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:
  23. 23. 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!
  24. 24. THE COMIC POST LIST IN THE ADMIN:
  25. 25. THE COMICPOST EDIT SCREEN:
  26. 26. Part 2 Add aCustom Form(a "Metabox") andCustom Fields
  27. 27. The "ComicInformation" Custom Fields Metabox
  28. 28. 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}
  29. 29. Add a Custom Metabox Callback Functionfunction 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;}
  30. 30. 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)
  31. 31. 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>
  32. 32. Part 3 Create aTheme Template File for a Custom Post Type
  33. 33. Simple Example ThemeTemplate for Custom Post Type
  34. 34. /wp- content/themes/your_theme/singl e-comic.php:<?php //Template for displaying single Comic Post Type.get_header();if ( have_posts() ): the_post(); ?><div id="content"><div id="post-<?phpthe_ID(); ?>" <?phppost_class(); ?>><h1 class="entry-title"><?phpthe_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/><?phpthe_content(); ?></div></div><?phpendif;get_footer(); ?>
  35. 35. Part 4Add Admin Columns for your Custom Post Type
  36. 36. Admin Columns forthe "Comics" Custom Post Type
  37. 37. 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;}
  38. 38. 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; }}
  39. 39. Better Admin Columns for the "Comics" Custom Post Typefunction 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;}
  40. 40. Part 5 Adding aCustom Taxonomy to a Post Type
  41. 41. An "Art Style" Taxonomy for the Comics Post Typefunction mikes_theme_init() { ... ...register_taxonomy( art-style, comic, array( hierarchical => true, label => Art Styles, rewrite => array( slug => art-styles, with_front => false ), ));
  42. 42. Part 6 Configure Post Edit Screens(including Posts and Pages)
  43. 43. supports=>array(title,excerpt,thumbnail)g ets this:
  44. 44. 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 ), ));
  45. 45. 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
  46. 46. Part 7Parent Post Field in aPost Editor Metabox (useful for non-same post types)
  47. 47. Create a Metabox to Associate an Episode with a Comic: parent_id is keyfunction 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;}
  48. 48. Part 8 QueryingCustom Post Types
  49. 49. Use the "Comic" Drop Down within the hook mikes_theme_episode_meta_boxfunction 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;}
  50. 50. Use WP_Query() to create a "Comic" Drop Down for "Episode" Parent Selectionfunction 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;}
  51. 51. Part 9Hierarchical URLs for Custom Post Types
  52. 52. Hierarchical URLs:
  53. 53. Enable /comics/{$comic}/{$episode}/ such as/comics/small-world/xmas-2012/1. Call add_rewrite_rule()in inithook.2. Add query_vararguments to comic and episode post types named comic_qv and episode_qv, respectively.3. Add post_type_link hook.4. Add requesthook.
  54. 54. Call add_rewrite_rule() in init hook. And add query_var argumentsadd_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, ...));
  55. 55. Add post_type_linkhook.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;}
  56. 56. Add requesthook.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;}
  57. 57. To get our "Episodes" Page:
  58. 58. /wp- content/themes/your_theme/singl e-episode.php:<?phpget_header();if ( have_posts() ): the_post(); ?><div id="content"><div id="post-<?phpthe_ID(); ?>" <?phppost_class(); ?>><?phpedit_post_link( Edit, <div class="edit-link">, </div> ); ?><?phpprevious_post_link( %link, &lt;&lt;&lt;Previous ); ?>&nbsp;&nbsp;&nbsp;<?phpnext_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"><?phpthe_title(); ?></h1><?phpthe_content(); ?><?php if ( has_post_thumbnail( $post->ID ) ) $image_html = wp_get_attachment_image_src(get_post_thumbnail_id( $post->ID ), single-post-thumbnail ); ?><imgsrc="<?php echo $image_html[0]; ?>"></div></div><?phpendif;get_footer(); ?>
  59. 59. Part 10Custom Post Type Filters in Admin Post List
  60. 60. Filtering our "Episodes:"
  61. 61. Or Not:
  62. 62. 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_postshook.
  63. 63. Make mikes_theme_comic_dropdown() reusablefunction 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;}
  64. 64. Add restrict_manage_posts hook. And add pre_get_postshook.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] ) ); }}
  65. 65. TakeawayYou can do practically anything you put your mind to withWordPress CPTs and a little PHP!
  66. 66. But Wait!Theres More…
  67. 67. 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…
  68. 68. Thank You To Contact Me:  Twitter: @mikeschinkel  http://about.me/mikeschinkel

×