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.

Flutter to Custom Post Types

So I’m working a project right now where I’m converting a WordPress installation from Flutter to the native WordPress custom post type functionality. The reasons for the switch are numerous. For one, it’s always good rule of thumb to use as few plugins as possible. The fewer plugins, the fewer php warning messages, the fewer javascript conflicts, the fewer hooks and filters getting processed on every load, and the fewer hits on the Database server. Secondly, flutter is particularly hard on the database, sometimes adding an extra 5 or more queries for every custom field.

The really great thing about flutter is that it still stores the field values in the  wp_postmeta table which means you don’t have to worry about any real data migration. So you have the luxury of taking it a step at a time.

The first step is to convert those key categories attached to “write panels” into post types. I did this by registering the post type as usual and simply running the following function on the init hook. This function performs a simple DB query to update posts with the specified cat slug into post_types.


function convert_cats_to_types($cat,$type) {
global $wpdb;
$query = $wpdb->prepare("
UPDATE wp_posts SET post_type = '$type' WHERE ID IN
(
SELECT DISTINCT object_id FROM wp_term_relationships rel
INNER JOIN wp_term_taxonomy AS tt ON tt.term_taxonomy_id = rel.term_taxonomy_id AND tt.taxonomy = 'category'
INNER JOIN wp_terms AS t ON t.term_id = tt.term_id AND t.slug = '$cat'
)
");
$results = $wpdb->query($query);
}

There’s no risk of losing data here, we are just changing where the posts in question are show up. The second thing to do is to replace the field-specific metaboxes. We’ll cover that in the next post.

Registration Forms: What’s New in 1.5

Last week I released version 1.5 of my Simplr Registration Forms plugin. The new version includes some big fixes and requested features. Particularly, this version now supports WP Multisite and has a few addition profile fields that can be added to the default form. It also includes better security, via WP nonces, and better field validation.

But the most important change is that it includes hooks and filters that allow it to be extended by you, the user.

For instance, let’s add a field to our form that requests the user’s zip code. First, in your functions.php file create a function for displaying the field:

function sample_zip_field($form) {
 $form .=  '<div>';
 $form .=  '<label for="zip">Zip Code:</label>';
 $form .=  '<input type="text" name="zip" value="'.$_POST['zip'] .'"/><br/>';
 $form .=  '</div>';
 return $form;
}

Note that this function receives the parameter $form and then returns $form. Failing to return the form will make the entire registration form disappear. To add this form to the registration use:

add_filter('simplr_add_form_fields', 'sample_zip_field');

But then we also need to make sure this data gets saved when the for gets saved. So you’ll need to create a function for that as well.

function sample_save_meta($user_id) {
if(isset($_POST['zip'])) {
 add_user_meta($user_id, 'user_zip', $_POST['zip']);
 }
return $user_id;
}

Note that in order for this function to work properly it has to receive the $user_id. It is also good practice to return the $user_id at the end of the function, though not necessary.

To make sure your save function is called use the hook:

add_action('simplr_profile_save_meta','sample_save_meta');

With these two “hooks”, you can customize the registration form however you want. You could even set up your field function to only display on certain pages, making it form-specific.

Finally, I’ve also added filters to the labels on the default form fields so you can change them at will. For instance, to change username to “screen name” use the following.

function sample_label_username($label) {
 $label = "Screen name: ";
 return $label;
}
add_filter('simplr_label_username','sample_label_username');

I hope you find the changes useful.

Custom WordPress Registration Pages (v 0.1.7)

Key Links:

Installation Instructions:

Download and unzip the package. Upload the simplr_reg_page folder into your wp-content/plugins directory. Go to your plugins dashboard and click “Activate”.

Usage Instructions:

To use this plugin simply employ the shortcode [Register] on any WordPress post or page. The default role is “subscriber”. To apply another role to the registration simply use the the role parameter, for instance: [Register role="editor"]. If you have created custom roles you may use them as well.

You can also use shortcode so specify a custom confirmation message for each form:

[Register role="teacher" message="Thank you for registering for my site. If you would like to encourage your students to register, please direct them to https://www.domain.com/students"]

Finally, you can specify emails to be notified when a new user is registered. By default site admins will receive notice but to notify others simply use the notify parameter:

[Register role="teacher" message="Thank you for registering for my site. If you would like to encourage your students to register, please direct them to https://www.domain.com/students" notify="email1@email.com,email2@email.com"]

Plugin Options:

Please feel free to suggest future options for this plugin in the comments. The current options are:

  • Admin Email: Specify the FROM email you would like to use for the registration confirmation message.
  • Confirmation Message: Customize the message you would like to appear in the confimation email.
  • Style: Overrides the default stylesheet included with the plugin.

Introduction to WordPress Pods CMS

Scott Clark, the tireless developer of Pods CMS, has put together a tutorial on using PodsCMS. Where does he find the time.

Introduction to the Pods CMS Framework from Pods CMS Framework on Vimeo.

In this video, Scott Kingsley Clark gives an introduction to the Pods CMS Framework. He goes over what Pods is, setting up your Pods and displaying them on your site, and provides some helpful tips and pointers.

Theming Custom Post Types in WordPress 3.0

Please note this post is way way out of date. WordPress has since integrated CPTs fully into the template engine so that you can simple create a single-posttype.php file. [See Template Heirarchy

So today I developed a simple strategy for dealing with some of the theming issues that arise out of WordPress 3.0 and the new Custom Post Type functionality.

The Issue: You’re working on a site designed long before Custom Post Types were an itch in Matt’s cerebellum. But now that the functionality is there and you want to incorporate it into your theme. Previously you used conditional code to theme your single.php file.

<?php if(in_category('foo')) { ?>
   Do something.
<?php } elseif(in_category('bar')) { ?>
   Do something else.
<?php } else { ?>
   Do yet another thing.
<?php } ?>

The thought of adding yet another layer of conditionals makes you sick to your stomach. Custom Post Types need to be treated completely different. They have different categories, taxonomies, and even different sidbars. Arg!

Solution: One simple function. Call it whatever you want, but here’s what it looks like. When I say simple … I mean simple.

function get_post_in_context() {
global $post;
$type = $post->post_type;
include(TEMPLATEPATH .'/layouts/single-'.$type .'-content.php');
}

Now just create a directory in your theme called “layouts”. Then copy and paste all the markup in your single.php file between the get_header() tag and the get_sidebar() tag to a file named single-post-content.php and save the file to your layouts folder.

Now place a the get_post_in_context() tag into your single.php  where you want the markup from single-post-content.php to show up.

<?php get_header(); ?>
<?php get_post_in_context(); ?>
 <?php get_sidebar(); ?>
<?php get_footer(); ?>

The beauty of this is that you can now create a new single-POSTTYPE-content.php (example:single-events-content.php) file for any of your custom post types and it will automagically get called instead of the single-post-content.php without any more changes to the single.php file.

I can’t take total credit for the idea. Some theme frameworks already employ a version of this strategy. But I need to adapt an existing theme rather than start from a framework. This function above will allow you to scale your existing theme to accomodate new post types without having to redesign your whole site.

Meta Tag Titles and Descriptions for Your WordPress site.

There are many good plugins out there the help you easily add meta tags to your site. All-in-one-SEO is probably the most popular of all the options. But there several reason why you may want to manually add this code to your template.

For one, you may be a template designer and want your templates to come pre-installed with this SEO friend feature.

Second, you may be having issues with Facebook link sharing not working. WordPress plugins use the wp_head() hook to access your template. But you have no control over the order. If you are using more than a dozen plugins your meta tags can get buried under tons of other code. This can prevent some bots from finding them. Indeed on several of my site Facebook’s bot was not finding them.

Third, it is just always good to do things without plugins if possible. Call it Occum’s Razor of web site building, the less code you use, the better.

Luck for us, it is extremely simple to add code for meta tags and descriptions without knowing much about wordpress or PHP.

Open the header.php file in your theme and paste in the following code:

<?php if(is_singular()) { ?>
<?php
global $post;
?>
<?php $recent = new WP_query('p='.$post->ID);
while($recent->have_posts()) : $recent->the_post(); ?>
<meta name="title" content="<?php the_title(); ?>">
<meta name="description" content="<?php the_content_rss('', TRUE, '', 50); ?>">
<?php endwhile; ?>
<?php } else { ?>
<meta name="title" content="<?php bloginfo('title'); ?>">
<meta name="description" content="<?php bloginfo('description'); ?>">
<?php } ?>

So what’s going on here? First, we’re checking to see if the post is a single post using the WordPress Conditional tag <?php if(is_singular()); ?> because we’ll to pull the title and the description of the individual post for our meta tags. But to do this, we need to get some information about the post, which is why we use the call global $post. This will give us information about the current post. Particularly it allows us access to $post->ID to query information about the post using WP_query.

The query we us looks like this:

$recent = new WP_query('p='.$post->ID); 

Once we have the query we put it into the standard wordpress loop:

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

Then we use the standard WordPress tag <?php the_title(); ?> to pull in the META TITLE and <? the_content_rss() ?> to get the description. Notice, we are not using <?php the_excerpt(); ?>. This is because this WordPress tag prints the excerpt with a “read more” link in it. This will seriously screw up your theme. Using <?php the_content_rss(); ?> allows us to specify how many words of the content to pull in. But the second parameter has to be set to TRUE to avoid pulling in a “read more” link.