Date post: | 08-May-2015 |
Category: |
Technology |
Upload: | mike-schinkel |
View: | 10,869 times |
Download: | 4 times |
Mastering WordPressCustom Post TypesBy Mike Schinkel – @mikeschinkelFor WordCamp Atlanta Feb 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
416style
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
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
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)
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
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!
What Today’s Post Type?
Example Today:
“PressedComics”
Inspiration for our example:
Small World Comics
Fire up PhpStorm!
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");
Add a functions.php file
/wp-content/themes/mikes_theme/functions.php<?php/* * functions.php for mikes_theme * */
Part 1
Add a Custom Post TypeCalled "Comics"
Register Post Type
<?php/* * functions.php for mikes_theme * */
register_post_type( 'comic' );
Must Use "init" Hook
<?php
add_action( 'init', 'mikes_theme_init' );function mikes_theme_init() { register_post_type( 'comic' );}
Must Make "public"
function mikes_theme_init() { register_post_type( 'comic', array( 'public' => true, ));}
But!
Must Give "label"
function mikes_theme_init() { register_post_type( 'comic', array( 'public' => true, 'label' => 'Comics', ));}
Now!
Y URL NO LOAD COMIC?!?
Refresh Permalinks
Like Candy from a Baby!
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:
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!
THE COMIC POST LISTIN THE ADMIN:
THE COMIC POST EDIT SCREEN:
Part 2
Add aCustom Form (a "Metabox")
and Custom Fields
The "Comic Information"Custom Fields Metabox
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 }
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;}
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)
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>
Part 3
Create a Theme Template File
for a Custom Post Type
Simple Example Theme Template for Custom Post
Type
/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(); ?>
Part 4
Add Admin Columns for your
Custom Post Type
Admin Columns for the "Comics" Custom Post Type
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;}
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; }}
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;}
Part 5
Adding a Custom Taxonomy
to a Post Type
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 ), ));
Part 6
Configure Post Edit Screens
(including Posts and Pages)
'supports' => array('title','excerpt','thumbnail')
gets this:
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' ), ));
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
Part 7
Parent Post Field in a Post Editor Metabox
(useful for non-same post types)
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;}
Part 8
Querying Custom Post
Types
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;}
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;}
Part 9
Hierarchical URLs for Custom Post Types
Hierarchical URLs:
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.
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', ...));
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;}
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;}
To get our "Episodes" Page:
/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', '<<<Previous' ); ?> <?php next_post_link( '%link', 'Next>>>' ); ?> <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(); ?>
Part 10
Custom Post Type Filters in Admin Post List
Filtering our "Episodes:"
Or Not:
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.
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;}
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'] ) ); }}
Takeaway
You can do practically anything you put your mind to with
WordPress' CPTs and a little PHP!
But Wait!
There's More…
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…
Thank You
To Contact Me:Twitter: @mikeschinkelhttp://about.me/mikeschinkel