I had a client come to me with a classic problem. They needed to pull in data from three different external APIs and save them as three different WordPress custom post types. One was for ‘Events’, another for ‘Locations’, and a third for ‘Team Members’. Each API had its own messy data structure. My job was to make it all play nice with WordPress.
My first instinct was to build three separate functions: import_event(), import_location(), and so on. That seemed simple, and for a minute it was. But after writing the second one I realized I was copy-pasting the core logic. Both functions connected to an API, mapped some fields, called wp_insert_post(), and then looped through another array to save post meta with update_post_meta(). Same steps, different data. If I ever needed to change the error handling, I’d have to change it in three places, and that was not a maintenance job I wanted.
The problem with just using arrays
WordPress creates posts with the wp_insert_post() function, which takes a big array of data. It works, but it isn’t elegant, especially with complex objects. You end up with a two-step process: first you save the main post data, then you handle the metadata separately. That gets repetitive once you have more than one type of data to save.
There’s no contract here. You’re throwing arrays around and hoping for the best. A PHP interface fixes that: it enforces a structure your data objects have to follow, which makes the code predictable and a lot easier to maintain.
Using an interface for custom post types
Instead of three messy functions, I defined a single “contract” for any CPT I wanted to save. In PHP, that’s an interface. Any class representing one of our custom post types has to return its data in the format WordPress understands. I first saw this approach laid out by Carl Alexander on his blog.
Here’s the interface. It says that any CPT object we create must be able to provide its post data and its meta data.
<?php
namespace MyPlugin;
/**
* Interface for WordPress custom post types.
*/
interface PostTypeInterface
{
/**
* Get the post data as a wp_insert_post compatible array.
*
* @return array
*/
public function get_post_data();
/**
* Get all the post meta as a key-value associative array.
*
* @return array
*/
public function get_post_meta();
}With that contract in place, we can write one generic function that saves any CPT following the rules:
<?php
namespace MyPlugin;
function wp_insert_custom_post(PostTypeInterface $post, $wp_error = false)
{
$post_id = wp_insert_post($post->get_post_data(), $wp_error);
if ( is_wp_error($post_id) || $post_id === 0 ) {
return $post_id;
}
foreach ($post->get_post_meta() as $key => $value) {
update_post_meta($post_id, $key, $value);
}
return $post_id;
}This function doesn’t care whether it’s saving an Event, a Location, or a Team Member. It only needs the object you pass it ($post) to know how to provide post data and meta data, which the PostTypeInterface guarantees.
What this approach buys you
It’s about writing code that doesn’t paint you into a corner. A clear contract through an interface gives you a system that’s predictable and easy to extend. When the client comes back in six months wanting a fourth API for a ‘Job Postings’ CPT, the steps are short:
- Create a
JobPostingclass. - Implement the
PostTypeInterface. - Define the
get_post_data()andget_post_meta()methods for that class. - Pass your new object to the same
wp_insert_custom_postfunction. Done.
You write no new saving logic and repeat no code. The approach cleanly separates the “what” (the data in your CPT class) from the “how” (the reusable function that saves it).
Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.