Using an Interface to Save WordPress Custom Post Types

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 thought? Just build three separate functions: import_event(), import_location(), and so on. Seemed simple enough. And for a minute, it was. But after writing the second function, I realized I was basically 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(). It was the same dance, just with different partners. Total nightmare waiting to happen. If I needed to change the error handling, I’d have to do it in three different places. No, thank you.

The Problem with Just Using Arrays

WordPress handles post creation with the wp_insert_post() function, which takes a big array of data. It works, but it’s not exactly elegant, especially when you’re dealing with complex objects. You end up with a two-step process: first, you save the main post data, then you handle all the metadata separately. This becomes repetitive fast when you have more than one type of data to save.

The real issue is the lack of a contract. You’re just throwing arrays around and hoping for the best. This is where a proper PHP interface comes in. It enforces a structure, a contract that your data objects have to follow. Trust me on this, it makes your code predictable and a hell of a lot easier to maintain.

A Better Way: Using an Interface for Custom Post Types

Instead of three messy functions, I decided to define a single “contract” for any CPT I wanted to save. In PHP, that’s an interface. The idea is simple: any class that represents one of our custom post types must have a way to return its data in the format WordPress understands. This approach builds on a concept I first saw laid out by Carl Alexander on his blog.

Here’s the interface. It’s clean and simple. It dictates 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 this contract in place, we can write one generic function to handle saving *any* CPT that follows the rules. Look how clean this is:

<?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 if it’s saving an Event, a Location, or a Team Member. It only cares that the object you pass it (`$post`) knows how to provide post data and meta data, because the `PostTypeInterface` guarantees it.

So, What’s the Point?

This is about writing code that doesn’t paint you into a corner. By defining a clear contract with an interface, you create a system that’s predictable and scalable. When the client comes back in six months and wants to integrate a *fourth* API for a ‘Job Postings’ CPT, the process is simple:

  • Create a `JobPosting` class.
  • Implement the `PostTypeInterface`.
  • Define the `get_post_data()` and `get_post_meta()` methods for that class.
  • Pass your new object to the same `wp_insert_custom_post` function. Done.

No new saving logic. No repeated code. Just a clean, object-oriented approach that 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.

Leave a Reply

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