Posted on

Default Featured Image for Posts & Products

Learn how to display a default featured image for WordPress posts, products, pages, and any other content that doesn’t have a featured image set. Although you can do this with a plugin, we’re just going to add a couple of simple code snippets to our theme. This helps us to reduce the number of plugins we’re relying upon, and it keeps the number of database calls down – which keeps our site nice and fast. Here’s an example based on our Post Grid WordPress Plugin tutorial.

Missing featured image
Missing featured image for our Charlotte’s Web post

To follow this tutorial, you’ll need to be using a custom child theme so you can edit your functions.php file. You’ll also want an image to use as your fallback/default image. We’re going to keep our code in a separate PHP file so we can use it in other projects and keep our functions.php file clean.

Finding The Right WordPress Filters

If you’re not interested in the technical details on how we find out which filters need to be hooked, just skip ahead to the code and start copying-and-pasting.

The function get_post_thumbnail_id() is a core WordPress function that returns the media ID for a post, page, product, whatever. If you look at the documentation for this function, you see that it uses the get_most_meta() function, which then calls the get_metadata() function. So, when WordPress tries to display a post’s featured image (full-size or thumbnail), it calls a bunch of functions in a chain, eventually calling get_metadata_raw(). If a post doesn’t doesn’t have a thumbnail, it’ll go on to call called get_metadata_default(), checking if there is a fallback image ID.

So… what we need to do is hook something inside the get_metadata_default() so we can “inject” our own response if we think we can support a default image ID. This all comes down to a single WordPress filter. But there’s one other thing we need to be aware of here…

Usually, before a theme or template calls a function like the_post_thumbnail(), it will check to see if there actually is a thumbnail in the first place, by calling has_post_thumbnail(), so if we find a post that doesn’t have a thumbnail, but we have a default featured image ID, we’ve got to make sure has_post_thumbnail() returns true.

So now we know we’ve got to add two filter handlers…

Let’s Write Some Code

In your custom child theme’s folder, create a new file called functions-missing-featured-image.php and paste the following into it.

<?php

/**
 * For posts/pages/products, enable fallback default image IDs, specified by
 * post_type.
 *
 * Just include this file from your theme's functions.php file, and then
 * define HW_MISSING_FEATURED_IMAGE_IDS in your functions.php like this:
 *
 * define('HW_MISSING_FEATURED_IMAGE_IDS', array(
 *    'post' => 123,
 *    'page' => 456,
 *    'product' => 789,
 *    'my_custom_post_Type' => 361,
 * ));
 */

// Block direct access.
if (!defined('WPINC')) {
	exit('Do NOT access this file directly.');
}

/**
 * Always return true for has_post_thumbnail() if the post-type has a fallback
 * default image ID.
 */
function custom_has_post_thumbnail($has_thumbnail, $post, $thumbnail_id) {
	if (is_admin() || defined('DOING_CRON')) {
		// Only filter $has_thumbnail if we're in the front-end.
	} elseif ($has_thumbnail) {
		// We already know this post has a thumbnail.
	} elseif (!defined('HW_MISSING_FEATURED_IMAGE_IDS')) {
		// We don't have any default emdia IDs.
	} elseif (empty($post_type = get_post_type($post))) {
		// This post doesn't have a post-type?
	} elseif (!array_key_exists($post_type, HW_MISSING_FEATURED_IMAGE_IDS)) {
		// We don't have a default medai ID for this post type.
	} else {
		$has_thumbnail = true;
	}

	return $has_thumbnail;
}
add_filter('has_post_thumbnail', 'custom_has_post_thumbnail', 10, 3);

/**
 * If our post's post_type has a default image image in
 * HW_MISSING_FEATURED_IMAGE_IDS then return it now.
 */
function custom_default_post_metadata($value, $object_id, $meta_key, $single, $meta_type) {
	if (is_admin() || defined('DOING_CRON')) {
		// Only return a fallback thumbnail ID if we're in the front-end.
	} elseif ($meta_key != '_thumbnail_id') {
		// We're not looking for a featured image.
	} elseif (!defined('HW_MISSING_FEATURED_IMAGE_IDS')) {
		// We don't have any default emdia IDs.
	} elseif (empty($post_type = get_post_type($object_id))) {
		// This post doesn't have a post-type?
	} elseif (!array_key_exists($post_type, HW_MISSING_FEATURED_IMAGE_IDS)) {
		// We don't have a default medai ID for this post type.
	} else {
		$value = HW_MISSING_FEATURED_IMAGE_IDS[$post_type];
	}

	return $value;
}
add_filter("default_post_metadata", 'custom_default_post_metadata', 10, 5);

You can see our 2 filter handlers here are quite small – they each only do one simple thing, and they need to execute very quickly, so we don’t want much code in there.

Now we just need to reference this new file from your theme’s functions.php file and specify the media IDs, like this:

// Media/Image IDs for when a post_type doesn't have a featured image.
define('HW_MISSING_FEATURED_IMAGE_IDS', array(
	'post' => 2113,  // Picture of a question mark in the media library.
));

require_once 'functions-missing-featured-image.php';

In this example we’re only specifying images for Posts that don’t have a featured image. If you’re using WooCommerce then you can just extend the above code like this:

// Media/Image IDs for when a post_type doesn't have a featured image.
define('HW_MISSING_FEATURED_IMAGE_IDS', array(
	'post' => 2113,  // Picture of a question mark in the media library.
	'product' => 2114,  // Picture of a missing product.
));

require_once 'functions-missing-featured-image.php';

That’s it. Your fallback default images should now be showing on post archive pages and single post pages, all without having to install yet another plugin. It’s a nice little refinement for any WordPress website, I think.

Happy coding! 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *