16

OOPost Types: Methods Part 2 – Object Oriented WordPress 3.0 App

This is the second part of building a WordPress 3.0 custom post type class. In this tutorial we'll build the methods that the class can use, and that any objects you build later on can access and use as well....
OOPost Types: Methods Part 2 – Object Oriented WordPress 3.0 App

Second Day Of Classes And OOPost Types

In the first tutorial - Classes part 1 of OOPost Types, I showed you how to start building the class for a custom post type to submit sites, essentially making a type of social bookmarking sort of post type. If you didn't read through that post and just skipped to this one I would recommend that you go back and review it first. Otherwise here is the code that I left off with in posttypes.php.

<?php
// Create a post type class for site posts
// To use as a bookmarking post type for sites you want to save/share.
class TypeSites {
    public $meta_fields = array( 'title', 'description', 'siteurl', 'category', 'post_tags' );

	public function TypeSites() {

        $siteArgs = array(
			'labels' => array(
                'name' => __( 'Sites', 'post type general name' ),
                'singular_name' => __( 'Site', 'post type singular name' ),
                'add_new' => __( 'Add New', 'site' ),
                'add_new_item' => __( 'Add New Site' ),
                'edit_item' => __( 'Edit Site' ),
                'new_item' => __( 'New Site' ),
                'view_item' => __( 'View Site' ),
                'search_items' => __( 'Search Sites' ),
                'not_found' =>  __( 'No sites found in search' ),
                'not_found_in_trash' => __( 'No sites found in Trash' ),
			),
			'public' => true, 'show_ui' => true,
			'_builtin' => false,
			'capability_type' => 'post',
			'hierarchical' => false,
			'rewrite' => array('slug' => 'site'), // Permalinks. Fixes a 404 bug
			'query_var' => 'site',
			'taxonomies' =>  array('category', 'post_tag'), // Add tags and categories taxonomies
			'supports' => array('title','editor','author','comments')
        );

        register_post_type( 'site', $siteArgs );	

	}

} // end of TypeSites{} class
?>

Excellent Hosting- You Will Never Need Another Web Host Again

Adding Methods To The Class Madness

Starting where I left off, let's continue building our class by adding some methods, or functions as many people call them. First let's set up some custom columns for the Sites manage page. You've probably seen this code before but here it is again.

UPDATED!

	// Create the columns and heading title text
	public function site_edit_columns($columns) {
        $columns = array(
			'cb' 		=> '<input type="checkbox" />',
			'title' 		=> 'Site Title',
			'url'	 	=> 'URL',
			'category'	=> 'Category',
			'post_tags' => 'Tags',
			'siteurl' 	=> 'Screenshot',
        );
        return $columns;
    }
	// switching cases based on which $column we show the content of it
    public function site_custom_columns($column) {
		global $post;
        switch ($column) {
            case "title" : the_title();
                break;
            case "url" : $m = $this->mshot(150); echo '<a href="'.$m[0].'" target="_blank">'.$m[0].'</a>';
				break;
            case "category" : the_category();
				break;
            case "post_tags" : the_tags('',', ');
                break;
            case "siteurl" : $m = $this->mshot(150); echo $m[1];
				break;
        }
    }

The difference with this custom column code compared to other custom columns codes is that it is invoking a method to generate the content displayed in two of the columns. This is a method that I have added below as an update to this post.

It sets the variable $m to the method mshot() with a size of 150. The number is used to make the image size. It is required, but will not affect the output when just getting the Url. The mshot() function returns an array containing the values of the url and image output. In order to retrieve them, we echo $m[0] for Url, and $m[1] for the auto generated site screenshot. This might be difficult to follow, as it is hard to explain. It took me a while to figure out how to get it to work, hence the lateness in my update.

Now let's add two more functions. First is a function for hooking into the template redirect action in WordPress, which will be used for setting up use of custom templates for Sites, which I'll explain more when we create new objects of this class.

UPDATE: $wp variable should be $wp_query or you get Notice: Undefined index 'post_type' in... I just realized this sorry.

	// Template redirect for custom templates
    public function template_redirect() {
        global $wp_query;
        if ($wp_query->query_vars['post_type'] == 'site') {
            include(TEMPLATEPATH . '/single-site.php'); // a custom single-slug.php template
            die();
        } else {
			$wp_query->is_404 = true;
		}
    }

questions
This function is used for inserting new Site posts using the wp_insert_post() function now available in WordPress 3.0. It will be extremely useful later on in the series, too.

	// For inserting posts
    public function wp_insert_post($post_id, $post = null) {
        if ($post->post_type == "site") {
            foreach ($this->meta_fields as $key) {
                $value = @$_POST[$key];
                if (empty($value)) {
                    delete_post_meta($post_id, $key);
                    continue;
                }
                if (!is_array($value)) {
                    if (!update_post_meta($post_id, $key, $value)) {
                        add_post_meta($post_id, $key, $value);
                    }
                } else {
                    delete_post_meta($post_id, $key);
                    foreach ($value as $entry) add_post_meta($post_id, $key, $entry);
                }
            }
        }
    }

Notice that the wp_insert_post() function uses the $meta_fields variable we've set up in the first tutorial. It is referred to using $this->meta_fields since it is being used inside the class method.

Adding Custom Meta Boxes

Using the add_meta_box() function you are able to hook into the posting page and add form fields. I plan to do a separate post for meta boxes as part of this post series so I am not going to go into much detail here.

Basically what is going on here is the admin_init() function initializes the meta_options() function. The meta_options() function gets the Url if one exists in the input field, and runs it through a wacky little script I created for allowing submitted Urls to work whether they include the http:// part of the Url or not. So http://new2wp.com AND new2wp.com will both be accepted, and work with the following.

The Url is then used to create a new Url that is encoded and appended to http://s.wordpress.com/mshots/v1/ to create a live screenshot of the page which the Url points to.
The input fields are then added. The first one is where you enter the Url, and the second one is just for making the new Url. The screenshot or properly known as, mshot, image it generates is then echoed out below the two input fields with the '?w=250' parameter appended to the end of it. This is to tell the mshot image it should have a width of 250 pixels. This code took me quite some time to compile and get working so enjoy it.

	// Add meta box
	function admin_init() {
        add_meta_box("siteS-meta", "Site", array(&$this, "meta_options"), "site", "side", "high");
    }

	// Admin post meta contents
	public function meta_options() {
		global $post, $url;
		$custom = get_post_custom($post->ID);
		$url = $custom["siteurl"][0];
		$myurl = trailingslashit( get_post_meta( $post->ID, 'siteurl', true ) );
		if ( $myurl != '' ) {
			if ( preg_match( "/http(s?):\/\//", $myurl )) {
				$siteurl = get_post_meta( $post->ID, 'siteurl', true );
				$mshoturl = 'http://s.wordpress.com/mshots/v1/' . urlencode( $myurl );
			} else {
				$siteurl = 'http://' . get_post_meta( $post->ID, 'siteurl', true );
				$mshoturl = 'http://s.wordpress.com/mshots/v1/' . urlencode( 'http://' . $myurl );
			}
			$imgsrc  = '<img src="' . $mshoturl . '?w=250" alt="' . $title . '" title="' . $title . '" />';
		} ?>
		<p><label>Clean Url: <input id="siteurl" size="26" name="siteurl" value="<?php echo $url; ?>" /></label></p>
		<p><label>Mshot Url: <input id="mshoturl" size="26" name="mshoturl" value="<?php echo $mshoturl; ?>" /></label></p>
		<p><?php echo '<a href="'.$siteurl.'">'.$imgsrc.'</a>'; ?></p>
	<?php
	} // end meta options

UPDATE: This Is A Newly Added Method

I apologize for not including this originally, my bad. Here is one more method to include after the meta_options function. This will output either the Url of a site post, or the image source including the size of the image by passing the object a number for the size. An example of for how to use this, refer to the custom columns code above. You will notice that the Url and image are output to create the separate columns for each site post. $m = $this->mshot(150); echo $m[1];

    public function mshot($mshotsize) {
        global $post, $url;
        $imgWidth = $mshotsize;
        $myurl = get_post_meta($post->ID, 'siteurl', true);
		if ( $myurl != '' ) {
			if ( preg_match( "/http(s?):\/\//", $myurl )) {
				$siteurl = get_post_meta( $post->ID, 'siteurl', true );
				$mshoturl = 'http://s.wordpress.com/mshots/v1/' . urlencode( $myurl );
			} else {
				$siteurl = 'http://' . get_post_meta( $post->ID, 'siteurl', true );
				$mshoturl = 'http://s.wordpress.com/mshots/v1/' . urlencode('http://'.$myurl );
			}
		}
        $mshotimg = '<img src="'.$mshoturl.'?w='.$imgWidth.'" alt="'.get_the_title().'" title="'.get_the_title().'" />';

        return array( $siteurl, $mshotimg );
	}

Page.ly WordPress hosting for people like you and me

Initializing it for Takeoff

We need to initialize the class functions, and add them to the dashboard admin, and then instantiate the class with a new object of it for hooking into the 'init' action. To do that you can add the following to your 'register_post_type() function just before the closing bracket.

UPDATED! The correct hook for the 'site_edit_columns' should be manage_edit-site_columns().

        add_action( 'admin_init', array(&$this, 'admin_init') ); // this must be first
        add_action( 'template_redirect', array(&$this, 'template_redirect') );
        add_action( 'wp_insert_post', array(&$this, 'wp_insert_post'), 10, 2 );

		// add custom columns
		add_filter( 'manage_posts_custom_column', array( &$this, 'site_custom_columns' ));
		add_action( 'manage_edit-site_columns', array ( &$this, 'site_edit_columns' )); // manage_edit-{post_type}_columns used for custom post types

To make custom columns for custom post types, WordPress manage_post_custom_columns() also uses manage_edit-{post_type}_columns to edit the custom post type manage post page columns. Just using manage_post_custom_columns for custom post types will cause your 'post' manage page to be overwritten with your custom columns.

After that you need to create a new object of the class to instantiate the class. This function will be used to make a new instance of the class. The function is then added to the init hook for WordPress.

/* Initialize Post Types */
add_action('init', 'pTypesInit');
function pTypesInit() {
    global $sites;
    $sites = new TypeSites();
}

The pTypesInit() function will be used to instantiate more post type classes later on in this series. By the time the series is over, the posttypes.php will be a work of art if ever one existed for custom post types.

The Full Sites Custom Post Type Class

This class is now complete. Of course you can always add or alter it however you like. Here is the full complete code.

<?php
// Initialize the Class and add the action
add_action('init', 'pTypesInit');
function pTypesInit() {
    global $sites;
    $sites = new TypeSites();
}

// Create a post type class for 'Site' posts
// To use as a bookmarking post type for sites you want to save/share.
class TypeSites {

	// Store the data
	public $meta_fields = array( 'title', 'description', 'siteurl', 'category', 'post_tags' );

	// The post type constructor
	public function TypeSites() {

        $siteArgs = array(
			'labels' => array(
                'name' => __( 'Sites', 'post type general name' ),
                'singular_name' => __( 'Site', 'post type singular name' ),
                'add_new' => __( 'Add New', 'site' ),
                'add_new_item' => __( 'Add New Site' ),
                'edit_item' => __( 'Edit Site' ),

                'new_item' => __( 'New Site' ),
                'view_item' => __( 'View Site' ),
                'search_items' => __( 'Search Sites' ),
                'not_found' =>  __( 'No sites found in search' ),
                'not_found_in_trash' => __( 'No sites found in Trash' ),
			),
			'public' => true, 'show_ui' => true,
			'_builtin' => false,
			'capability_type' => 'post',
			'hierarchical' => false,
			'rewrite' => array('slug' => 'site'), // Permalinks. Fixes a 404 bug
			'query_var' => 'site',
			'taxonomies' =>  array('category', 'post_tag'), // Add tags and categories taxonomies
			'supports' => array('title','editor','author','comments')
        );
        register_post_type( 'site', $siteArgs );	

	// Initialize the methods
        add_action( 'admin_init', array(&$this, 'admin_init') );
        add_action( 'template_redirect', array(&$this, 'template_redirect') );
        add_action( 'wp_insert_post', array(&$this, 'wp_insert_post'), 10, 2 );

		// add custom columns
		add_filter( 'manage_posts_custom_column', array( &$this, 'site_custom_columns' ));
		add_action( 'manage_edit-site_columns', array ( &$this, 'site_edit_columns' )); // manage_edit-{post_type}_columns used for custom post types

	}

	// Create the columns and heading title text
	public function site_edit_columns($columns) {
        $columns = array(
			'cb' 		=> '<input type="checkbox" />',
			'title' 		=> 'Site Title',
			'url'	 	=> 'URL',
			'category'	=> 'Category',
			'post_tags' => 'Tags',
			'siteurl' 	=> 'Screenshot',
        );
        return $columns;
    }
	// switching cases based on which $column we show the content of it
    public function site_custom_columns($column) {
		global $post;
        switch ($column) {
            case "title" : the_title();
                break;
            case "url" : $m = $this->mshot(100); echo '<a href="'.$m[0].'" target="_blank">'.$m[0].'</a>';
				break;
            case "category" : the_category();
				break;
            case "post_tags" : the_tags('',', ');
                break;
            case "siteurl" : $m = $this->mshot(150); echo $m[1];
				break;
        }
    }

	// Template redirect for custom templates
    public function template_redirect() {
        global $wp_query;
        if ( $wp_query->query_vars["post_type"] == "site" ) {
            include( TEMPLATEPATH . "/single-site.php" ); // a custom single-slug.php template
            die();
        } else {
			$wp_query->is_404 = true;
		}
    }

	// For inserting new 'site' post type posts
    public function wp_insert_post($post_id, $post = null) {
        if ($post->post_type == "site") {
            foreach ($this->meta_fields as $key) {
                $value = @$_POST[$key];
                if (empty($value)) {
                    delete_post_meta($post_id, $key);
                    continue;
                }
                if (!is_array($value)) {
                    if (!update_post_meta($post_id, $key, $value)) {
                        add_post_meta($post_id, $key, $value);
                    }
                } else {
                    delete_post_meta($post_id, $key);
                    foreach ($value as $entry) add_post_meta($post_id, $key, $entry);
                }
            }
        }
    }

	// Add meta box
	function admin_init() {
        add_meta_box( "sites-meta", "Site", array( &$this, "meta_options" ), "site", "side", "low" );
    }

	// Admin post meta contents
	public function meta_options() {
		global $post, $url;
		$custom = get_post_custom($post->ID);
		$url = $custom["siteurl"][0];
		$myurl = trailingslashit( get_post_meta( $post->ID, 'siteurl', true ) );
		if ( $myurl != '' ) {
			// Check if url has http:// or not so works either way
			if ( preg_match( "/http(s?):\/\//", $myurl )) {
				$siteurl = get_post_meta( $post->ID, 'siteurl', true );
				$mshoturl = 'http://s.wordpress.com/mshots/v1/' . urlencode( $myurl );
			} else {
				$siteurl = 'http://' . get_post_meta( $post->ID, 'siteurl', true );
				$mshoturl = 'http://s.wordpress.com/mshots/v1/' . urlencode( 'http://' . $myurl );
			}
			$imgsrc  = '<img src="' . $mshoturl . '?w=250" alt="' . $title . '" title="' . $title . '" />';
		} ?>

		<p><label>Clean Url: <input id="siteurl" size="26" name="siteurl" value="<?php echo $url; ?>" /></label></p>
		<p><?php echo '<a href="' . $siteurl . '">' . $imgsrc . '</a>'; ?></p>

	<?php
	} // end meta options

	// Generate output for the url or image source for each site post
    public function mshot($mshotsize) {
        global $post, $url;
        $imgWidth = $mshotsize;
        $myurl = get_post_meta($post->ID, 'siteurl', true);
		if ( $myurl != '' ) {
			if ( preg_match( "/http(s?):\/\//", $myurl )) {
				$siteurl = get_post_meta( $post->ID, 'siteurl', true );
				$mshoturl = 'http://s.wordpress.com/mshots/v1/' . urlencode( $myurl );
			} else {
				$siteurl = 'http://' . get_post_meta( $post->ID, 'siteurl', true );
				$mshoturl = 'http://s.wordpress.com/mshots/v1/' . urlencode('http://'.$myurl );
			}
		}
        $mshotimg = '<img src="'.$mshoturl.'?w='.$imgWidth.'" alt="'.get_the_title().'" title="'.get_the_title().'" />';

        return array( $siteurl, $mshotimg );
	}

} // end of TypeSites{} class
?>

This concludes the building of your first fully functional and intuitive Php Class for constructing a WordPress 3.0 custom post type. Save the posttypes.php file, and make sure to include it into your functions.php file. Then go to your dashboard to check out the new post type that should be added and working as a new section of the site. Please let me know of any mistakes I may have made or bugs that might be found. Cutting this code up and disecting it makes it easy for this to happen.

Ask a question below
We will be using much of this same code with some minor changes, and replacing certain functions with others according to the different post type classes. But first let's try out this new class by adding some objects to a couple custom WordPress templates.

The next tutorial shows how to instantiate the class, and build a custom post type WordPress loop to query the custom post types posts that have been submitted.

Shortlink:

Get automatic updates! Subscribe to Our RSS Feed or Get Email Updates sent straight to your inbox!

About the Author

Jared is from Boston working as a web and graphic designer. Also owns the design blog Tweeaks.com, and has designed many other websites powered by Wordpress including the New2WP theme.

Level: Pro

User Comments

( ADD YOURS )

  1. Fascinating series of articles. Iam learning a lot here, but I am having a problem with line 58 of the full listing, it is producing an error:
    "Parse error: syntax error, unexpected T_ECHO in \functions\posttypes.php on line 58". Any ideas?


  2. UPDATED: I've updated the code which should work fine at this point

    Im glad you like it and it is helpful for you.
    I made a mistake in copying/pasting the columns code, I am surprised I managed to not make more mistakes than just this so far.

    Here's the fixed code that works

    // Create the columns and heading title text
    public function site_edit_columns($columns) {
           $columns = array(
    		'cb' 		=> '
    		<input type="checkbox" />',
    		'title' 	=> 'Site Title',
    		'url'	 	=> 'URL',
    		'category'	=> 'Category',
    		'post_tags' => 'Tags',
    		'siteurl' 	=> 'Screenshot',
           );
           return $columns;
       }
    // switching cases based on which $column we show the content of it
       public function site_custom_columns($column) {
    	global $post;
           switch ($column) {
               case "title" : the_title();
                   break;
               case "url" : $m = $this->mshot(100); echo '<a href="'.$m[0].'" target="_blank">'.$m[0].'</a>';
    			break;
               case "category" : the_category();
    			break;
               case "post_tags" : the_tags('',', ');
                   break;
               case "siteurl" : $m = $this->mshot(150); echo $m[1];
    			break;
           }
       }
    

    After looking at this again I don't know what I was thinking, but it was way off so I apologize. I have so many post type codes I am pulling this code from which I've been working on and writing for a while now. Easy to get lost in it all. Let me know if you still have problems.


  3. Compare your old code with this code, you'll see the differences, and just how far off I was. :}


  4. thx Jared, that fixed it. Looking forward to the next instalment :)


  5. Thanks for this articles!

    I found an error: the custom columns don't work. Realized that the filter used is manage_edit-portfolio_columns, instead of manage_edit-site_columns. Also the functions used with this filter and manage_posts_custom_column need to swap.

    I understand you copy from many codes, and appreciate the time that you spend making this series, but would be amazing if you could test it as well.

    Any date in mind for part 3?

    Thanks!


  6. Thanks, I remember fixing that filter before in this post with a code that I had tested. I must have somehow re-replaced it again. Apologies for the misinformation, and to make it up to you I've updated the post with some a new function, which is used to generate the custom columns, and that will be used in the next post.

    The updated code changes and additions in this post include the correct filters:

    		add_filter( 'manage_posts_custom_column', array( &$this, 'site_custom_columns' ));
    		add_action( 'manage_edit-site_columns', array ( &$this, 'site_edit_columns' ));
    

    The new and improved custom columns code:
    This invokes the newly added mshot() method to display and link the url of the site which is included in the post, and makes a column which shows a live screenshot of the site, simply with the use of the entered url.

    	// Create the columns and heading title text
    	public function site_edit_columns($columns) {
            $columns = array(
    			'cb' 		=> '
    			<input type="checkbox" />',
    			'title' 	=> 'Site Title',
    			'url'	 	=> 'URL',
    			'category'	=> 'Category',
    			'post_tags' => 'Tags',
    			'siteurl' 	=> 'Screenshot',
            );
            return $columns;
        }
    	// switching cases based on which $column we show the content of it
        public function site_custom_columns($column) {
    		global $post;
            switch ($column) {
                case "title" : the_title();
                    break;
                case "url" : $m = $this->mshot(100); echo '<a href="'.$m[0].'" target="_blank">'.$m[0].'</a>';
    				break;
                case "category" : the_category();
    				break;
                case "post_tags" : the_tags('',', ');
                    break;
                case "siteurl" : $m = $this->mshot(150); echo $m[1];
    				break;
            }
        }
    

    And a whole new function that makes the magic happen.
    This returns an array containing the url and image with the size of the image, which is passed to the function.

        public function mshot($mshotsize) {
            global $post, $url;
            $imgWidth = $mshotsize;
            $myurl = get_post_meta($post->ID, 'siteurl', true);
    		if ( $myurl != '' ) {
    			if ( preg_match( "/http(s?):\/\//", $myurl )) {
    				$siteurl = get_post_meta( $post->ID, 'siteurl', true );
    				$mshoturl = 'http://s.wordpress.com/mshots/v1/' . urlencode( $myurl );
    			} else {
    				$siteurl = 'http://' . get_post_meta( $post->ID, 'siteurl', true );
    				$mshoturl = 'http://s.wordpress.com/mshots/v1/' . urlencode('http://'.$myurl );
    			}
    		}
            $mshotimg = '<img src="'.$mshoturl.'?w='.$imgWidth.'" alt="'.get_the_title().'" title="'.get_the_title().'" />';
    
            return array( $siteurl, $mshotimg );
    	}
    

    Again, sorry for copying codes from other sites, I'm clearly better off just writing my own codes. Who cares anymore about how to make a portfolio site using custom post types anyways, since there's enough tuts out there that show how to, in exactly the same way.

    The code I had 'thought' I posted before, had been tested and working, though in writing, cutting and pasting this tutorial somehow a mistake or two was made. Guess I am human after all.

    I am hoping to get the next post in the series done early this week, I have been extremely busy lately working 3 jobs so I just haven't had the time to do it. But it will be soon, I promise.


  7. I have made a couple of fixes.

    In the template_redirect function, the variable $wp should be $wp_query, so I changed where it said $wp.

    The manage_post_custom_columns() function call should be manage_edit-site_columns(). I have written about this more above where the add_actions() are called for custom columns. Sorry for my poorly written post, I will be sure to triple check and test further posts for this.

    It's not easy to find much info on any of this as you may know, so it's easy to make mistakes.


  8. Hi man, great article. I would love to see the shift towards a more generalised class where you don't specify custom type name within the class and its methods but allow define them at the time of instantiating of the class like $bookCustomType = new CustomPostType('Books', 'Book', 'book)....... $bookCustomType->enableCoreTaxonomy('categories')..etc.


  9. Thanks. Yeah for a long while I worked on a way to try and abstract the class and use child classes which utilize the main functions in the parent class. I wasn't ever successful in my efforts, and since this worked I figured I'd share it.

    But I now know how to do that with some help. You should check this out, since it's pretty much what you're talking about, and an overall better way to do it OO. http://somadesign.ca/projects/smarter-custom-post-types/


  10. Nice one! Thanks for the link. I visit your site often - keep up great work, man!


  11. Thanks :) You should check out my newest big project site too if you haven't already.

    2010 WPhonors - http://2010.wphonors.com
    You can join, post, vote, and have a chance to be randomly chosen to win one of the 58 prizes that will be given away too.


  12. I've really enjoyed this series, it really covers all the pieces of using custom post types in one. However, i'm running into issues trying to create two different custom post types, duplicate class/variable names. I'm trying to duplicate and rename the duplicates, but it's hard for me to know which is which.

    Do you have any recommendations to creating two different custom post types using your OO method here.


  13. What I have always done is just copy the entire class and paste it below, then do find+relpace for all the things that you'd need to change.

    Then in the init function where you create variables
    global $site;
    $site = new TypeSite();

    just add another variable for each new post type class you create, with the name for it and then instantiate the class with the $variable = new TypeWhatever();

    That will initialize each of your classes.

  1. Avatar

    Your Name
    February 4


    CommentLuv badge