Programmatically importing thousands of featured image post thumbnails into WordPress

Summary:Here's a deep dive into the featured thumbnail feature of WordPress and a technique for converting thousands of images into WordPress thumbnail attachments.

Yes, ladies and gentlemen, DIY-IT is about to rock some code on the house!

WordPress, in versions 2.9 and greater, supports a UI feature called post thumbnail. They also call it "featured image," just to keep things interesting. The purpose of this UI feature is to give theme developers a standard way to get and display a thumbnail or main image for a given post.

Prior to when this feature became available, most theme developers had to find their own approach to store and reference a thumbnail, which resulted in a lot of incompatibility problems for users when switching themes.

Since I want my AI Editor and AI Publisher code to be as generalized as possible, I decided to spend a weekend updating my code to support featured images, rather than my own hacked variant of thumbnail storage and retrieval. This would allow me to switch themes without having to go into a theme's code and spend hours rewriting the thumbnail display module for every theme I wanted to use.

How I figured it out

I had a heck of a time finding any documentation about how to turn an attachment into a featured image. The WordPress Codex has some good information for theme writers about using a featured image, but the general assumption is that an actual user will click the UI and upload an image. I had 12,776 featured images I needed to create, so I wasn't about to do it by hand.

One way to find out how WordPress does something is to step through the code. But that's very time-consuming. So, if possible, it's best to find another way. What I did was open up the database in a MySQL viewer (I like HeidiSQL) and watch what changed as I added data.

I always use a dummy WordPress install, so it's easy to see if a record is added or deleted. When I added an image to a post, I noticed that a new post record was created, with the parent_id pointing to the parent content post. It also set the post type to 'inherit'. The problem is, when you upload an image using the featured image UI, the exact same behavior happened. There was absolutely no difference in the posts table.

The next thing I did was look through the other WordPress tables, paying special attention to the postmeta table, which adds key/value pairs to a given post. As it turned out, while no new postmeta record was created when I just added an image, when I added a featured image, a record was created with the key '_thumb_id' set to the ID value of the inherited image post.

I further tested it by removing that record and I noticed that the featured image disappeared from the UI. When I put the record back, the featured image came back as well.

Now, as with all hacks, it's important to understand that since this isn't documented, the core developers might change this at any time. No matter, for now it works, and that's all you can ask for on a long, holiday weekend.

Implementation

Now, before I show you my code, understand that everyone has their own coding style, this is intended as one-time use code, and -- as you all well know -- programming is not my main job responsibility. So, if you have constructive suggestions about coding, I'd love to hear them. Also, while we're at it, here's another disclaimer. I don't guarantee this will work for you, I can't debug your code, and your mileage may vary. May the Force be with you.

My code needs to do more than just take an existing media asset and register it as featured image. My code is doing a full import process. So I built a one-time use plug-in that loops through every published post and retrieves an image file name I'd previously planted in a custom field. It then runs the following chunk of (sanitized) code, which "inserts" the image as a WordPress attachment to the post.

$wp_filetype = wp_check_filetype($filename, null );
$mime_type = $wp_filetype[type];
$attachment = array(
'post_mime_type' => $wp_filetype['type'],
'post_title' => preg_replace('/\.[^.]+$/', '', basename($filename)),
'post_name' => preg_replace('/\.[^.]+$/', '', basename($filename)),
'post_content' => '',
'post_parent' => $post_id,
'post_excerpt' => $thumb_credit,
'post_status' => 'inherit'
);
$attachment_id = wp_insert_attachment($attachment, $filename, $post_id);
if($attachment_id != 0) {
$attachment_data = wp_generate_attachment_metadata($attachment_id, $filename);
wp_update_attachment_metadata($attachment_id, $attach_data);
update_post_meta($post_id, '_thumbnail_id', $attachment_id);
}
You may have noted, in the previous paragraph, that I put the word "inserts" in quotes. That's because the wp_insert_attachment function doesn't actually insert an attachment. What it does is update the function records to indicate that an attachment would be there, if it had actually been moved in place. The wp_update_attachment_metadata attempts to record further information about the image attachments, but it turns out it didn't work in my application without a little special sauce.

WordPress uses the concept of function filters, which are simply PHP functions it calls during the execution of specific WordPress functions. One such filter is the _wp_relative_upload_path filter, which WordPress calls during the wp_insert_attachment process, when it needs to create a relative upload path (i.e., a path from a current directory, rather from root).

This filter proved essential, because I had discovered that wp_insert_attachment didn't actually move the image files into place in the /files directory, where WordPress expected them.

So, by creating the following filter, I was able to do a one-time move of those files to where they were supposed to be, and then pass back a valid relative file specification.

add_filter('_wp_relative_upload_path', 'zenpress_wp_plugin_convertaithumbs_upload_path');


function zenpress_wp_plugin_convertaithumbs_upload_path($old_file) { $uploads = wp_upload_dir();

$file_name = basename($old_file); $new_file = $uploads[path] . '/' . $file_name; if(!file_exists($new_file) and !is_dir($new_file) and !is_dir($old_file)) { // copy this bad boy into the new location copy($old_file, $new_file); } // prepend the month and day subdirectory to the file name, and return it up the chain $new_url = $uploads[subdir] . '/' . $file_name; $new_url = ltrim($new_url, '/'); // get rid of extra / in path return $new_url; }

Finally, once I'd managed to get the image to register properly with WordPress's media system, it was time to take advantage of the featured image capability. To turn something into a featured image, simply set '_thumb_id' to the value of the post image ID, as I did here:
update_post_meta($post_id, '_thumbnail_id', $attachment_id);
One final note: my first run exceeded PHP's allowed execution time, so I first thought I'd go in and modify my php.ini. But I really like reentrant code, so instead, I decided to just run a check at the beginning to see if '_thumbnail_id' existed. If it did, I'd skip processing that post. Worked like a charm. There are better ways to make sure PHP doesn't overdo its processing time, but as I mentioned, this was one-time-use code, so I just went for the expedient solution. That's the DIY-IT spirit for you -- first, and foremost, get it done.

So, there you go. I'll be working on a lot more PHP and WordPress over the coming months, so stay tuned for other deep dives into interesting features.

The image in the featured image UI above courtesy Flickr user ralph and jenny.

Topics: Software Development

About

In addition to hosting the ZDNet Government and ZDNet DIY-IT blogs, CBS Interactive's Distinguished Lecturer David Gewirtz is an author, U.S. policy advisor and computer scientist. He is featured in The History Channel special The President's Book of Secrets, is one of America's foremost cyber-security experts, and is a top expert on savi... Full Bio

zdnet_core.socialButton.googleLabel Contact Disclosure

Kick off your day with ZDNet's daily email newsletter. It's the freshest tech news and opinion, served hot. Get it.

Related Stories

The best of ZDNet, delivered

You have been successfully signed up. To sign up for more newsletters or to manage your account, visit the Newsletter Subscription Center.
Subscription failed.