cc licensed flickr photo shared by Thomas Hawk

Yikes, where did the time go? I’ve let slide the next piece in my now fractured series on using the custom post types added in WordPress 3.0. As a recap, what I am sharing is what I learned back in April (using the beta version of WP3) to create the NMC MIDEA web site. This site had a need to create, besides the usual page and blog post types, other content for projects, organizations, and events, each with its own extra custom meta-data.

Before I go crazy with PHP here, I want to note that this will get technical and is written really for myself as a set of notes and others who have no hesitation to roll up their sleeves and muck around with templates and code. This is not the optimal way to use the new WordPress features, and it is my expectation that what I am writing will be done more easily (and probably more efficiently) by plugins. I did it “my way” more to show what was possible.

And also, I am far from a deep WP engineering expert- I just keep tinkering til it works, and you can bet there are better ways to code than what I did.

In this series, I started with an overview of the plans for the MIDEA project, we set up the places to create the new content types, and introduced the benefits using child themes. Then we took it up a notch to dive into the code to create the interface and data elements to add/edit the custom meta data for each of the three content (post) types.

Now I turn to what it took to integrate and display stuff form my custom content into the MIDEA site. This requires a bit of understanding of what is called The Loop in WordPress.

cc licensed flickr photo shared by jayRaz

Understanding a bit of The Loop is a key bit of foundation for understanding templates (see the Loop in Action from the WordPress Codex). This is my take on it.

Just about everything WordPress does starts with it talking to the database, and saying, “get me this kind of stuff” (stuff might be recent blog posts, all posts with a certain tag, the results of a search, a single page, etc). When the database returns something, WordPress then says, “Hmmm, for each one of this, pull these bits out and display them” This is a loop, sometimes The Loop.

Typically you get a built in Loop w/o needing to strictly ask for it- the front page is “give my the 10 most recent blog posts”, and other variations are set up by the context of whatever is being served (e.g. in an archive from December 2009, you get just those posts).

The most basic version of The Loop runs something like:

if (have_posts()) :
   while (have_posts()) :
      the_post();
      // do something for each post
   endwhile;
endif;

In our code “posts” is the placeholder for whatever we are fetching- blog posts, pages, custom content types. We first need to check that we have anything, and them if so, we cycle through them (while…)– the_post() grabs all the info about the item, (title, description, link, date posted, etc). Inside that are, we have a whole series of built in functions we can use to display and wrap formatting around- the_title() , the_author(), the_permalink()

In my MIDEA project, and maybe in all, I used four variations of looping.

(1) The default loop for whatever kind of content we are working with (front page, category page, search results). This uses the structure listed above

(2) Change the condition of the main loop with query_posts. This basically makes a modification to whatever is the default behavior, so you can do things like display a different number of posts, or limit to a category.

//The Query
query_posts('posts_per_page=3');

//The Loop
if ( have_posts() ) : while ( have_posts() ) : the_post();
  // do stuff for each post
endwhile; else:
 // what to do if there is nothing found
endif;

//Reset Query
wp_reset_query();

In this case, query_posts is used to change the number things retrieved from its defaults to be exactly 3. Note the wise use of wp_reset_query() in case we want to use the default loop conditions elsewhere.

(3) Completely customize the parameters of the Loop, or make a second (or third, or fourth loop). In this case, we are not working with the default Loop for the current content, ut we are designing our own. This often happens in sidebars and/or widgets- Little Loops are done to get recent comments, recent posts, etc.

You will see a lot of these below, but the method uses the WP_Query function to set up the database request, and a slightly different variant of the default Loop structure.

An example of this is what I used on some sidebars to get the most recent blog post (setting showposts=1) from the news category:

$my_query = "showposts=1&category_name=news";
$showNews = new WP_Query();
$showNews->query($my_query);

while ($showNews->have_posts()) : $showNews->the_post(); ?>

 // do stuff to display here
endwhile; 
wp_reset_query();

The Query_posts() reference page lists all the things you can use ti set up the query.

(4) The get down and dirty call the database query direct method. There are just some things you cannot get to via query_posts(). In the MIDEA site, I had to use this for anything that defined a loop based on the custom content meta data, or another area where I wanted to use some MySQL magic to make sure organization names that started with “The” were not alphabetized in the T”s.

Now I’m going to outline the places I used this and some of the loops used. I’m not going to detail what goes on inside the loops- this is really dictated by your template and your knowledge of how to pull and use the variables you can get for a loop item.

And this post is going to scroll for about 1000 feet.

The Projects Content Type

One of our custom content types represents projects, each with extra meta data of a URL, an id for the organization who it is affiliated with, and a rating of 1-5 (5 being highest). The general page for projects is a gallery, http://midea.nmc.org/proj with the following parts that needed custom code. The right side is a sidebar- the main content is set out by template file proj.php. Our function midea_template_redirect() we added last time, sets up for each content type, proj for projects, org for organizations, event for events, the name for its template (proj.php,org.php,event.php) as well as the templates for a single item of the type- single-proj.php, single-org.php, and single.event.php.

So here is the skeleton of the proj,php display:

(A) Each time the page loads we get 9 projects selected at random, and we want ones that are rated higher than 2 (we have some that are in progress and they get a 1 rating). The display in the squares was just used elsewhere in the Modularity template. But the idea is for a visual gallery puling projects at random.

(B) Each time we reload the page, we pull in the full information for one selected featured project. The way this is done is to select another random project, but only ones that are rated 4 or higher. In addition, we need to track the ones we used in (A) so we don’t repeat.

(C) The sidebar provides an alphabetical list of all projects in the site. This might be the easiest thing to do except for one wrinkle we discovered elsewhere on the project; the problem of projects (and organizations) that start with “The”, like “The Blue Dog” — we do not want to alphabetize on “T” but on “B:. This required a bit of custom mySQL in our loop.

Are you ready to loop?

For (A), the grid of 9 projects, we use the wordpress capability to modify the “stock” selection for the Loop. Even though this is a custom type, WordPress still provides a built in loop that will select projects in reverse chronological order. We don’t want that, so we use the method of modifying the main loop:

query_posts('post_type=proj&orderby=rand&meta_key=midea-proj-rating&meta_value>2&posts_per_page=9');

$randy_projects = array(); // keep a rolling list of projects listed here

This php above the loop logic says, “instead of what you normally do, Loop, I want t make sure I get only content that are of type proj, they should be chosen at random rather than reverse chronologically, and I want you to look at the meta data of rating, and only get me ones with a value of 2 or greater. Oh, and I only want 9 of these”

I also set up a variable to keep track of the 9 that are chosen, and will store these in an array.

We then just run our basic wordpress loop, and use it to display the image for the project and link it. The only thing we add inside the loop is something to track the project ids, using the function get_the_ID(), which in a loop returns the id number for this item.

$randy_projects[] = get_the_ID();

That was easy, eh?

Now we move on to grabbing our featured project. We need to set up arguments to send to the wp_query() function:

$args = array(
	'post_type' => 'proj',
	'posts_per_page' => 1,
	'orderby'=> 'rand',
	'meta_key' => 'midea-proj-rating',
	'meta_value' => '4',
	'meta_compare' => '>=',
	'post__not_in' => $randy_projects,
);
$blog_query = new WP_Query($args);

It is similar but different to the first one we did. On this one, we only want to grab 1, its still random, but in this case we use the options to ask for ones with a rating of 4 or higher, and the trickier part, use post_not_in to make sure it is not among the ones in our 9 item array. Got it? the last line runs the query. From here we can run our loop.

Except… we need to look back at our functions.php file. We need a few utility functions to deal with our project data. The first one, midea_get_project_info($id) is used to fetch the data for a project (identified by $id). Now what our database stores for this content type is a url, an id number for the organization, and an integer rating. We need to fetch this info, but also, we need to look up the actual name of an organization.

function midea_get_project_info($id) {
	$my_proj_fields = midea_proj_fields(); // fields for this content type
		
	foreach ($my_proj_fields as $key => $value) {
		// load values into our array
		$my_proj_fields[$key] = get_post_meta($id, 'midea-proj-' . $key, true);
	}

	$my_proj_fields['orgname'] = ($my_proj_fields['org'] > 0) ? get_the_title((int)$my_proj_fields['org']) : 'MIDEA';
	$my_proj_fields['id'] = $id;
	return ($my_proj_fields);
}

First we set up an empty array $my_proj_fields to store the data. We can then loop over our meta-data names to fill in their values using get_post_meta(). Next, since all we have is an id for the organization (stored as $my_proj_fields[‘org’]) we can add a string name (‘orgname’) to our data array by using the nifty wordpress function get_the_title() we just pass it the idea of a post or content type, and we get a string back,

Lastly, because we might need ti elsewhere, we add another data element to store the id number of this project.

Now we are back to our proj.php, where we have set up the query to get one featured project. We still run a loop, although it will go through once because we asked only for one item. Below IU will show this code, but it has the template formatting simplified to make it easier to follow

< ?php if ($blog_query->have_posts()) : while ($blog_query->have_posts()) :  $blog_query->the_post(); ?>

  < ?php $pinfo = midea_get_project_info(get_the_ID());?>
	
  <p>< ?php echo $pinfo['orgname']?></p>
	
  <h2><a href="<?php the_permalink() ?>">< ?php the_title(); ?></a></h2>
  <div class="entry">
  < ?php the_content(); ?>
	
  < ?php if ($pinfo['url']):?>
  <p>Project URL: <a href="<?php echo $pinfo['url']?>" target="_blank">< ?php echo $pinfo['url']?></a></p>
  < ?php endif; ?>

  <p><a href="<?php echo get_permalink($pinfo['org'])?>">Learn more about < ?php echo $pinfo['orgname']?>...</a></p>

< ?php endwhile; endif; wp_reset_query(); ?>

What we have in line (01) is a loop but using the variables defined in our query. In line (3) we get our project info, and in line (5) we echo the organization name as lead in to line (7) where we echo the project title. Line (09) just outputs whatever is written in the body for the project entry (like the body of a blog post). Lines (11-13) echo the URL if there is one. Line 15 adds a link to the organization the project is associated with (note the use of the get_permalink() function, very handy of you know an item’s id).

Lastly, note in line (17) as we end our loops controls, we reset the main query (this is the default query WordPress generates for this content type, this is a good practice to do if other parts of your template need access to its data).

Now we move on to our sidebar. We are using a special sidebar template for the projects page, so we call it via:

< ?php get_sidebar('proj'); ?>

which tells WordPress instead fg using the template sidebar.php to use sidebar-proj.php.

This is the one where we want to list all projects in alpha order, but do the special ordering thing to skip alphabetizing on “The”. To do this we need to run the Type 4 variations of The Loop, where we throw it a full mySQL query. To do this you need to have a sense of how the WP database tables are set up.

<ul>
< ?php 
global $wpdb;
global $post;

// custom query to get posts of type proj in alpha order by trimming the leading "The " in a title
$projposts = $wpdb->get_results("SELECT wposts.* 
   FROM $wpdb->posts as wposts
   WHERE wposts.post_type = 'proj' and wposts.post_status='publish' 
   ORDER BY TRIM(LEADING 'The ' FROM wposts.post_title)", OBJECT);
?>
< ?php if ($projposts): ?>

< ?php foreach ($projposts as $post): ?>
< ?php setup_postdata($post); ?>
	<li><a href="<?php the_permalink();?>">< ?php the_title()?></a></li>
 < ?php endforeach;  endif;?>
</ul>

I am not sure why, but I had to declare the global variables in lines (02-03). Lines (07-10) are are raw mySQL query to get content of type proj, only ones that are published, and to list them in alpha order by title, but to trim any “The” from the ordering. I cant explain it, but found it used in some other project. Lines (12-15) set up the loop in the manner you have to do for using this method. Again, thats all I know except it works, and populates our loop with data we can use like any other loop.

In this case we are doing a simple list with a link on a title.

So next we have to code up the content for a single project. The parts of this are shown schematically:

(A) the basic content for this project, we add a few extra bits like the organization name and the URL.

(B) On our sidebar, we list/link the organization this project is associated with, using the icon associated with the organization content type.

(C) We list other projects this organization has on our site.

This template again is single-proj.php.

The main part of it (A) is given by the basic loop, so we dont have to do anything fancy; and the format is pretty much in the same manner we did above for the featured project (B) on the projects gallery above.

We are going to request a custom sidebar template sidebar-single-proj.php. First we do (B) getting the organization information displayed.

< ?php 
$pinfo = midea_get_project_info(get_the_ID());

// set up query to get org data
$my_query = "post_type=org&p=" . $pinfo['org'] . "&posts_per_page=1";
$showOrg = new WP_Query();
$showOrg->query($my_query);
while ($showOrg->have_posts()) : $showOrg->the_post(); 
	get_the_image( array( 'link_to_post' => true, 'class' => 'thumbnail' ) );
endwhile;
?>
<p><a href="<?php the_permalink();?>">< ?php echo $pinfo['orgname']?></a></p>

In line (02) we again use our function to get the project information, based on the current ID in the main loop. Next in lines (05-08) we run another loop to access the data for the organization associated with this project. This template has a built in function to get the icon image associated with a piece of content, which is what we output via get_the_image() function inside the one item loop. And in line (12) we echo a link to the organization.

Now we move on to (C) to get all other projects associated with this same organization.

<ul>
< ?php
$my_query = 'post_type=proj&posts_per_page=-1&orderby=title&order=ASC&meta_key=midea-proj-org&meta_value=' . $pinfo['org'];

$showProj = new WP_Query();
$showProj->query($my_query);
?>
	
< ?php if ($showProj->have_posts()) : while ($showProj->have_posts()) : $showProj->the_post(); ?>

	< ?php if (get_the_ID() == $pinfo['id']) : ?>
		<li class="selected">&raquo; <strong>< ?php the_title();?></strong></li>
	< ?php else: ?>
		<li><a href="<?php the_permalink();?>">< ?php the_title()?></a></li>
	< ?php endif ?>

< ?php endwhile; endif; ?>
</ul>

In lines (03-06) we set up a new wp_query() to find all projects (posts_per_page value of -1 means all of ’em), and we will list them in alpha order by title (I’m lazy and not doing the fancy order by stripping “The”…). The key here is the last bits where we tell the query to select just ones where the meta data for the project matches the organization ID of the main project being displayed. In lines (11-13) we are just doing the output, but if the project id matches the one this page represents, we dont provide a link, and add a CSS class so we can style it to indicate that its the one on this page.

This is just the basic code for one of the three content types I created. I f I write them all up, my poor blog (and body) will fall over. So I will have to come back to talk about the code used for the organizations (and the nifty plugin that inserts a google map based on latitude and longitude custom data) and for the event (where we have fun sorting by dates).

It’s a lot to explain! And this is not even to mention the various sidebars where I do things like list three random projects, but the first one is always one rated 5 and the others are all rated 3 and above. Maybe that’s an exercise for the student.

More to come. I promise/hope.

You can find all the posts in this series at http://cogdogblog.com/2010/07/23/roundup-wp3/

The post "Dressing up and Displaying WordPress 3 Custom Post Content (a)" was originally rescued from the bottom of a stangant pond at CogDogBlog (http://cogdogblog.com/2010/07/dressing-up-wp3-content-a/) on July 21, 2010.

4 Comments

Leave a Comment

All fields are required. Your email address will not be published.