Using WordPress Pods to Manage Custom Data

Last week I posted about relating a WordPress post to your Pods data. But I realize, for a lot of people, the question is still: Why use Pods to manage your custom data in the first place?

There are a ton of benefits to using PodsCMS. Here’s a thread worth reading on the forum. But I thought it might be helpful to see an example.

Let’s go back to our related books tutorial. Why couldn’t we do this using custom fields and custom post types? That would be simple enough. We’d first create a post type called “books”. Then we’d need to create a meta box that would add the post ID of the related book to our post.

No big deal. We could easily query the related books lik so (I am not testing this code, so don’t try and reuse. This is purely for example):

$bookid = get_post_meta($post->ID,'related_books', false); 
$args = array('post__in' => $bookid, 'post_type' => 'books');
$books = new WP_query($args);

Then of course, we’d use a while loop:

while($books->have_posts()) : $books->the_post();

Pretty simple. But what happens when we start adding complexity? What if we want to feature books that are either related by ID, or have a certain author?

Let’s assume for the moment that we’re using a custom field to save the author name in the books post type. This scenario is a little more challenging. The query above is restrictive to books that have been selected as related. But since we want to do an “OR” query we’re probably going to need a custom select query:

global $wpdb;
$id = $post->ID; //the current post id
$query = "SELECT DISTINCT p.ID FROM wp_posts p INNER JOIN wp_postmeta AS m ON p.ID =  m.post_id WHERE p.post_type = 'books' AND p.post_status = 'publish' AND ( (m.meta_key = 'book_author' AND m.meta_value = 'William Shakespeare') OR p.ID IN(SELECT DISTINCT meta_value FROM wp_postmeta WHERE meta_key = 'related_book' AND post_id = '$id') )";
$results = $wpdb->get_col($query); 

In theory this custom select query gets us an array of post IDs we can then pass on to the standard WP query using the post__in argument we used above. Notice that just a modest increase in the complexity of the request leads to major increase in the complexity of the query.

But what if we are using a post_type for authors and need all the authors who have written certain kinds of books. Say we have a category “childrens books” and we want to get a list of all authors who have written a children’s book. This is where WordPress starts to get pretty convoluted. We could use a custom select query, like we did before. But we could also combine some WP queries.

//get the books in the childrens book category
$cbooks = get_posts('post_type=books&category_name=Childrens Books');
foreach($cbooks as $cbook) {
  $cids[] = $cbook->ID;
}
$authors = query_posts(array('post_type'=>'author','post__in'=>$cids)); 
?>

This is a a pretty inefficient use of code and it only get worse. Imagine needing to see a list of books in a certain category, with a certain author, with a certain number of pages, that was written in 2010.

You’re using custom fields for the publication year and the pages, a custom post_type for author and a category for the genre. Think of the mental power that will go into putting this query together. You’ll have to do a custom select query.

global $wpdb;
$query = "
SELECT DISTINCT ID FROM wp_posts p
INNER JOIN wp_term_relationships AS tr ON tr.object_id = p.ID
INNER JOIN wp_term_taxonomy AS tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
INNER JOIN wp_terms AS t ON t.term_id = tt.term_id
INNER JOIN wp_postmeta AS m1 ON m1.post_id = p.ID AND m1.meta_key = 'related_author'
INNER JOIN wp_postmeta AS m2 ON m2.post_id = m1.post_id AND m2.meta_key = 'publication_year'
INNER JOIN wp_postmeta AS m3 ON m3.post_id = m2.post_id AND m3.meta_key = 'num_pages'
WHERE p.post_status = 'publish' AND p.post_type = 'books'
AND t.slug = 'childrens-books' 
AND m1.meta_value = 'William Shakespeare'
AND m2.meta_value = '2010'
AND m3.meta_value > 200
ORDER BY p.post_title ASC
";
$results = $wpdb->get_col($query);
query_posts(array('post__in'=>$results)); 

I’m not even sure if this query would work, but the query that would work would look a lot like this.

So you get the point. WordPress has no method of simplifying complex queries.

If you are the client and know exactly how you plan to use your site then this isn’t such a big worry. But if you’re a developer working for a client using WordPress as a CMS, you don’t know what the client might eventually request. Nevertheless, it’s your job to be able to meet that request.

Pods is a CMS framework built specifically to deal with these complexities. Let’s perform the query above using the Pods approach.

$pod = new Pod('books');
$pod->findRecords('name ASC',-1, "author.name = 'William Shakespeare' AND t.year = '2010' AND t.pages > 200 AND cat.slug = 'childrens-books'"); 
echo $pod->showTemplate('book_list');

Seriously, that’s it. And of course, you could substitute “OR” for any of the “AND” operators above to get a more inclusive result.

Pods gets a lot of criticism for being a WordPress hack. If you can do what you want to do with WordPress why introduce an unnecessary plugin? I hope I’ve demonstrated here that the advantages to using the PodsCMS Framework are enormous in terms of time and mental energy saved.

And it’s key to remember that Pods is a development Framework, not really a plugin. It can be used to manage minor amounts of custom data in your WordPress posts or a complex web of related content types that are outside of the Posts system altogether.

Down and Dirty WordPress Pods Related Content

Update: Looking at this now, I realized there are some advantages to taking a slightly different approach.

This thread at PodsCMS.org asks how to relate posts to Pods content. This is something that will get easier in future versions of pods. But the beauty of Pods is that it’s already pretty damn easy if you are familiar with how WordPress manages custom fields.

Getting Down!

So let’s say you have a pod called “books” and you want to be able to relate blog posts to the books you are organizing via pods. I’m going to assume for the sake of this tutorial that you already know how to create Pods content and so you’ll have no problem with the first step:

1. Books pod:

2. Posts pod.

Next you’ll want to set up a pod to relate to posts, you might call it “posts”, or “post_relations”, or “post_meta”:

Again, if you’re familiar with Pods this should be pretty straight-forward. Though you might ask why are we creating a pod called “posts” when it’s the books we want to relate to posts. Well we could just relate the books to posts. But the trouble is relating the other way, posts -> books, gets difficult without a pod to store all the relationships. Plus, we might want to add more “meta” fields to our posts pod that we can also associate with a post. Basically, if we’re using pods, we might as well take advantage of the flexibility.

Let’s also create a pod field called books and make it a pick field related to the books pod.

So in our posts pod, we have a pick column relating to posts and one relating to books.

3. Multidirectional?

We can now return to the book pod and set up a “posts” field there to relate to the posts pod as a bidirectional field.

Why not relate to the posts directly? Again, we want to leverage pods to give us as many options as possible. As long as one pod as is related to posts, we’re “plugged” in so to speak.

4. Create the metabox function:

So here’s where it gets a little more difficult … but still not too bad. We’ll need to create a function that will build the content of our meta box. Add this function to your functions.php.

function books_related_pods() {
global $post;
echo '
'; echo ''; echo '
'; }

Notice that we’re creating one Pods object as ‘$old’ so that we can access the existing values. Then we’re creating a second as “options” which we use to build our select menu options.

5. Create a meta box callback function.
Now we need a function to build the metabox itself. This function will be hooked into the WordPress API. This is an easy one. If you want to know more about meta boxes, read the WordPress Codex article.

function books_related_mb() {
add_meta_box( 'pods_related_mb', "Related Books", 'books_related_pods', 'post', 'normal', 'high', '' );
}

Note that the third parameter sets the “callback” function for the meta box. In this case we are using function we created above. Also note that the fourth parameter is where you specify the post_type on which you wish to use the meta box.

6. Hook into the WordPress API:

Just add a line in your functions.php:

add_action('add_meta_boxes','books_related_mb');

So at this point you should be able to go to your post screen and see the new meta box. If you can’t your doing something wrong. But we still need to save the meta box.

6. Save the pods!

Now you’ll see the advantage of the seemingly complicated pods set up. All we have to do is find the post’s related pod and add the values using the Pods API.

add_action('save_post','books_related_save_pod');
function books_related_save_pod($post_id) {
//
if( !wp_is_post_revision( $post_id ) && !wp_is_post_autosave( $post_id ) ) {
        if(isset($_POST['related_books'])) {
$vals = implode(',',$_POST['related_books']);
$pod = new Pod('posts');
$pod->findRecords('id DESC',1,"post.ID = '$post_id'" );
while($pod->fetchRecord()) { 
$pid = $pod->get_pod_id();
}
if($pid) {

//if a pod exists, save the values
$api = new PodAPI();
$params = array(); 
$params['datatype'] = 'posts';
$params['pod_id'] = $pid;
$params['columns']['books'] = $vals;
$params = pods_sanitize($params);
$api->save_pod_item($params);

} else {

$new = new PodAPI(); 
$params = array();
$params['datatype'] = 'posts';
$params['columns']['name'] = $_POST['post_title']; // You must set a name when saving a new pod
$params['columns']['post'] = $post_id;
$params['columns']['books'] = $vals;
$params = pods_sanitize($params);
$new->save_pod_item($params);
}

}
           }
}
?>

So there you have it. Down and dirty.