Hooking into wordpress image

Add a Custom Field to Each New Post in WordPress

In one recent project, I wanted to allow users more control over the blog and taxonomy pages of a WordPress theme by letting them select from two possible layouts for each post in the index. One of the layouts would have the standard featured image, title, and excerpt in that order. This would be the ‘normal’ layout, the default for all new posts. The ‘alternative’ layout would just be the title on top of the featured image as a background. I made a new template part for each design, added some CSS, and was happy with how both designs came out. But how should users actually choose between them?

illustration of two WordPress post layouts
Left: the ‘normal’ layout. Right: the ‘alternative’ layout

I thought about using the Customizer for this. You could make a new panel containing a bunch of number controls. A user could assign posts to the alternative layout by inputting their Post IDs. Making new customizer panels, settings, and controls is a lot of typing, though, and adding too many panels makes it overwhelming for the user.

Instead, I decided to add a custom field (post meta field) to each new post the user created. This has a large convenience benefit compared to the customizer option. The custom field shows up in the post editor and users don’t need to worry about tracking down post ID numbers.

The Save_Posts Hook

Ideally, the custom field will be ready and waiting every time the user starts writing a new post. In order to make this happen, we’re going to use the save_post hook. This hook fires, as you might guess, every time a post is saved, including when WordPress autosaves immediately after creating it. Note: you could just as easily use the wp_insert_post hook, which is functionally the same. Functions hooked to save_post take three arguments:

  • Post ID
  • $post (the object)
  • $update

That last argument, $update, is a boolean with a value of true when the post in question is a revision of previously-existing post, and false when it’s new. Hm, seems like it could be useful in this situation! We can now make a function that gets called on every save. Inside the function goes a conditional which only runs when $update is false, i.e., when the post is newly created. The conditional prevents the custom field value from being overwritten with the default on subsequent saves. Inside that, we add our custom field.

function mytheme_post_layout( $id, $post, $update ){
	if ( !  $update ){
		add_post_meta( 
                         $id, 
                         'mytheme_post_layout', 
                         'normal', 
                          true 
                );
	}
}

We initialize the custom field with a value of ‘normal’ for the default layout. If the user wants to use the alternative layout, they have only to set the value on that post to ‘background_image’, or whatever other string we decide should represent it. And then, to hook the function to save_posts:

add_action( 'save_post', 'mytheme_post_view', 10, 3 );

Make sure $update is defined when the function is called by setting the $accepted_args parameter to 3. I tend to forget $accepted_args exists a lot of the time but you definitely need it here. Now, in our template files, we can check if we should use the alternate layout by calling get_post_meta within The Loop. Declare global $post first so you’ve got the post object available to reference.

if ( get_post_meta( 
        $post->ID , 
        'mytheme_post_layout', 
        true ) === 'background_image' 
){//include template part for alternate layout here}

And that’s pretty much it! We’ve now allowed for a little more customization within the theme. Don’t forget to sanitize the custom field value before saving it to the database. I’ll drop the sanitization function I used below.

function mytheme_sanitize_post_layout_meta( $layout ){
	return $layout === 'background_image' ? 
                           'background_image' : 
                           'normal';
}	

add_filter('sanitize_post_meta_mytheme_post_layout', 'mytheme_sanitize_post_layout_meta');