John Watson

Hello! My name is Watson and I’m a freelance web developer. I create web sites using the latest tech for clients both huge and small. Rent my brain and I’ll help you build your dream project.

Building a better WordPress portfolio

It turns out to be extremely easy to teach WordPress how to handle more than just blog posts and pages. In this post, I’m going to describe how I converted my hand-coded, static portfolio/CV home page into a WordPress page powered by custom post types. This isn’t a full step-by-step tutorial but rather a medium-level overview of the general approach with some code examples.

Prior to yesterday, my home page was hand-coded outside of WordPress and WordPress was responsible solely for the content in the /blog directory. (I’d created the site a long time ago and decided to add a blog to it later.) Whenever I needed to update or rearrange my portfolio, I’d get the old HTML editor out and move a bunch of markup around. Adding a new entry involved a lot of copy and paste. So I decided to add a custom post type specifically for Projects.

The first step is to register your custom post types. I did this in my theme’s functions.php file:

add_action('init', 'watson_init');

function watson_init() {
  register_post_type('project',
    array(
      'labels' => array(
        'name' => __('Projects'),
        'singular_name' => __('Project'),
        'add_new_item' => __('Add New Project'),
        'edit_item' => __('Edit Project'),
        'new_item' => __('New Project'),
        'view_item' => __('View Project'),
        'search_items' => __('Search Projects'),
      ),
      'public' => true,
      'exclude_from_search' => true,
      'has_archive' => false,
      'menu_position' => 5,
      'supports' => array(
        'title', 'editor', 'thumbnail', 'custom-fields', 'revisions'
      )
    )
  );
}

That code adds a Projects menu which allows you to manage “Projects” in the WordPress admin just like you can manage Posts and Pages. It also excludes those posts from search results and customizes which blocks are visible in the editor. You can read up on all of the options in the Codex.

From the new Projects menu, I can add, update, trash, draft, publish, and unpublish Projects just like I can with Posts. It’s very cool.

The next step was to create a custom page template to show those projects. Here’s the entire template for my home page:

<?php
/*
Template Name: Projects
*/
?>

<?php get_header(); ?>

<div id="recentprojects">
    <h2>Recent projects</h2>

    <div id="projectlisting">
        <?php
        $projects = get_posts(
            array(
                'numberposts' => -1,
                'post_type' => 'project',
            )
        );
        global $post;
        foreach($projects as $post) {
            setup_postdata($post);
            ?>
            <div class="project">
                <?php
                if (has_post_thumbnail()) {
                    the_post_thumbnail(array(75, 75), array('class' => 'icon'));
                }
                ?>
                <div class="details">
                    <h3>
                    <?php
                    $url = get_post_meta(get_the_ID(), 'url', true);
                    if ($url) {
                        printf('<a href="%s">%s</a>', $url, get_the_title());
                    } else {
                        the_title();
                    }
                    ?>
                    </h3>
                    <?php the_content() ?>
                </div>
            </div>
            <?php
        }
        ?>
    </div>

    <?php
    if (have_posts()) {
        the_post();
        the_content();
    }
    ?>
</div>

<?php get_footer(); ?>

Here are the important bits: Lines 14-19 loads all of posts in the the ‘project’ post type defined earlier. Lines 20-21 setup the loop for the posts. Line 22 allows you to use functions like the_content() within your loop just like any other WordPress template. Lines 49-54 show the content associated with the actual page using this template.

That’s basically it. Register your post types, create a template. Easy as 1, 2.