# Saving WordPress custom post types using an interface

Custom post types are a powerful WordPress functionality. Everyone that works with WordPress long enough ends up using them. A custom post type can be anything. That flexibility is the source of much of its power. When using custom post type, you're only limited by your imagination (trademark pending). This flexibility also makes it a great use case for designing an [interface](http://carlalexander.ca/polymorphism-wordpress-interfaces). This article will put you in the interface creator seat. Exciting, I know! As the interface creator, you're in charge of designing the interface contract. This means that you get to dictate how someone use your interface (insert evil laughter here). It's not all (evil) roses though. The job gives you quite a lot of responsibility. This can make it hard for you to know where to start or what to do. Lucky for you, that's what this article will help you with.

# How WordPress handles post data

Before we go any further, let's look at WordPress post data management. In particular, we want to look at post data in two contexts. What WordPress does when it retrieves and saves post data. The two (retrieving and saving) are quite different. ## Retrieving a post

WordPress has a few ways to retrieve a post. You can use functions like [`get_post`](http://codex.wordpress.org/Function_Reference/get_post) or a [query](http://codex.wordpress.org/Class_Reference/WP_Query). Whatever method you choose to use, you'll always get [`WP_Post`](http://codex.wordpress.org/Class_Reference/WP_Post) objects back. The `WP_Post` class handles both the regular post data and all post meta information. That said, `WP_Post` is [final](https://secure.php.net/manual/en/language.oop5.final.php). This prevents us from extending it and replacing it with another class. Because of this, it's hard to use an interface when WordPress retrieves a post from the database. That’s why the article won’t cover it. ## Saving a post

[`wp_insert_post`](http://codex.wordpress.org/Function_Reference/wp_insert_post) is the primary function that WordPress uses to save a post. Unlike when you retrieve a post, it works using a post data array. It's important to note that the `wp_insert_post` function doesn't handle post meta. You need to handle those yourself once it returns to you with a post ID. You can view an example of this two-step process below. It's the cornerstone of the contract that we'll create with the interface. ```
$post_id = wp_insert_post($post_data);

foreach ($post_meta as $key => $value) {
    update_post_meta($post_id, $key, $value);
}
```

# The setup

Ok, now that you're familiar with how WordPress manages post data. Let's start working on our interface and its contract. The easiest way to do that is to start with an empty interface. This will act as the blank canvas that we'll fill up during the rest of the article. ```
namespace MyPlugin;

/**
 * Interface for WordPress custom post types.
 */
interface PostTypeInterface
{
}
```

That poor interface is all sad and lonely (on top of being empty). So let's add a bit more to the mix. Let's build a small `Product` class as well (yay friends!). ```
namespace MyPlugin;

/**
 * A Product.
 */
class Product implements PostTypeInterface
{
    /**
     * The key used by the product post type.
     *
     * @var string
     */
    const POST_TYPE = 'product';

    /**
     * The description of the product.
     *
     * @var string
     */
    private $description;

    /**
     * The name of the product.
     *
     * @var string
     */
    private $name;

    /**
     * The price of the product.
     *
     * @var float
     */
    private $price;

    /**
     * Constructor.
     *
     * @param string $name
     * @param float  $price
     * @param string $description
     */
    public function __construct($name, $price, $description = '')
    {
        $this->description = $description;
        $this->name = $name;
        $this->price = $price;
    }
}
```

It's nothing too fancy so far. The class has a constructor, a constant and three internal variables: `description`, `name` and `price`. It also implements our `PostTypeInterface` that we just created. We'll use this class for our custom post type. ```
register_post_type(Product:POST_TYPE);
```

This code uses the [`register_post_type`](http://codex.wordpress.org/Function_Reference/register_post_type) function to register a "product" post type. It does so by using the constant from the `Product` class. We're keeping this simple for this article. We just need WordPress to know that our custom post type exists. This is what this does. # Let's put that interface to work

Now that we have a nice foundation. Let's find work for that empty interface. It's time it earned its keep (no one likes slackers). Let's look at two common scenarios that it needs to handle. ## Inserting a custom post type

WordPress inserts posts into the database using the [`wp_insert_post`](http://codex.wordpress.org/Function_Reference/wp_insert_post) function. We'll use it to insert our own custom post types. So let's create our own `wp_insert_custom_pos` to do that. ```
namespace MyPlugin;

/**
 * Insert or update a custom post type.
 * 
 * @param PostTypeInterface $post
 * @param bool              $wp_error
 *
 * @return int|WP_Error
 */
function wp_insert_custom_post(PostTypeInterface $post, $wp_error = false)
{
}
```

The function takes two parameters like `wp_insert_post`. The difference is that our first parameter is now a [type hint](http://php.net/manual/en/language.oop5.typehinting.php) of our interface. We still need a way to convert our custom post type to a `wp_insert_post` compatible array. That'll be the first job of our interface. ```
namespace MyPlugin;

interface PostTypeInterface
{
    /**
     * Get the post data as a wp_insert_post compatible array.
     *
     * @return array
     */
    public function get_post_data();
}
```

Our interface now has the `get_post_data` method. This method allows us to convert a class using our interface to a `wp_insert_post` compatible array. Let's update our `wp_insert_custom_post` function. ```
function wp_insert_custom_post(PostTypeInterface $post, $wp_error = false)
{
    return wp_insert_post($post->get_post_data(), $wp_error);
}
```

Once that's done, we need to take a look at our `Product` class. It's missing the `get_post_data` method that we defined in `PostTypeInterface`. The method will return the relevant data we want to pass to `wp_insert_post`. ```
namespace MyPlugin;

class Product implements PostTypeInterface
{
    // ...

    /**
     * Get the product data as a wp_insert_post compatible array.
     *
     * @return array
     */
    public function get_post_data()
    {
        return array(
            'post_content' => $this->description,
            'post_title' => $this->name,
            'post_status' => 'publish',
            'post_type' => self::POST_TYPE
        );
    }
}
```

Looking at the array, you can see that we assigned our object variables to post data keys. We assigned our product name to the post title and the product description to the post content. We also needed to add the `Product` post type (using the constant) and the post status. By default, WordPress publishes a post as a "draft". This isn't ideal because the post type doesn't have an edit page. Instead, we want WordPress to publish our product right away. That's why post status is set to "publish". ## Dealing with post metas

But what happens to the price of our product? That's the type of information that goes in a post meta. We'll need to add another method to our interface to handle that. ```
namespace MyPlugin;

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();
}
```

This is the goal of the `get_post_meta` method that we added to the interface. We can use it in our `wp_insert_custom_post` function. It'll allow us to add the post meta of a custom post type. ```
namespace MyPlugin;

function wp_insert_custom_post(PostTypeInterface $post, $wp_error = false)
{
    $post_id = wp_insert_post($post->get_post_data(), $wp_error);

    if (0 === $post_id || $post_id instanceof WP_Error) {
        return $post_id;
    }

    foreach ($post->get_post_meta() as $key => $value) {
        update_post_meta($post_id, $key, $value);
    }

    return $post_id;
}
```

The first thing we need to do is save the post ID returned by `wp_insert_post`. We add some error validation code to check for the two possible error values. If we have an error, we return it and stop there. Otherwise, we use the `get_post_meta` interface method to add all the post meta. We cycle through the array and add each post meta using the [`update_post_meta`](http://codex.wordpress.org/Function_Reference/update_post_meta) function. The array key acts as the post meta key. ```
namespace MyPlugin;

class Product implements PostTypeInterface
{
    // ...

    /**
     * Get the product data as a wp_insert_post compatible array.
     *
     * @return array
     */
    public function get_post_data()
    {
        return array(
            'post_content' => $this->description,
            'post_title' => $this->name,
            'post_status' => 'publish',
            'post_type' => self:POST_TYPE
        );
    }

    /**
     * Get all the post meta as a key-value associative array.
     *
     * @return array
     */
    public function get_post_meta()
    {
        return array(
          'price' => $this->price
        );
    }
}
```

We also have to update our `Product` class and add the `get_post_meta` method. The method returns an array with the price of the product. The `wp_insert_custom_post` function will then convert that array into a post meta. It'll create it using "price" as the post meta key and our product price as the value. # Putting our interface to use

So now that we have everything built, what does it look like in practice? Let's say you want a new product called "My awesome product". It costs 49.99. How would you create the product and save it? ```
namespace MyPlugin;

$product = new Product('My awesome product', 49.99);
wp_insert_custom_post($product);
```

The first line creates our `Product` object with the name and price of the new product. The second line uses our new `wp_insert_custom_post` function to save it into the WordPress database. # And there you have it!

You managed to take a WordPress problem and build an interface around it (Awww yeah!). You can find all the beautiful (and complete) code from the article just below. ```
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();
}

/**
 * A Product.
 */
class Product implements PostTypeInterface
{
    /**
     * The key used by the product post type.
     *
     * @var string
     */
    const POST_TYPE = 'product';

    /**
     * The description of the product.
     *
     * @var string
     */
    private $description;

    /**
     * The name of the product.
     *
     * @var string
     */
    private $name;

    /**
     * The price of the product.
     *
     * @var float
     */
    private $price;

    /**
     * Constructor.
     *
     * @param string $name
     * @param float  $price
     * @param string $description
     */
    public function __construct($name, $price, $description = '')
    {
        $this->description = $description;
        $this->name = $name;
        $this->price = $price;
    }

    /**
     * Get the product data as a wp_insert_post compatible array.
     *
     * @return array
     */
    public function get_post_data()
    {
        return array(
            'post_content' => $this->description,
            'post_title' => $this->name,
            'post_status' => 'publish',
            'post_type' => self:POST_TYPE
        );
    }

    /**
     * Get all the post meta as a key-value associative array.
     *
     * @return array
     */
    public function get_post_meta()
    {
        return array(
          'price' => $this->price
        );
    }
}

/**
 * Insert or update a custom post type.
 *
 * @param PostTypeInterface $post
 * @param bool              $wp_error
 *
 * @return int|WP_Error
 */
function wp_insert_custom_post(PostTypeInterface $post, $wp_error = false)
{
    $post_id = wp_insert_post($post->get_post_data(), $wp_error);

    if (0 === $post_id || $post_id instanceof WP_Error) {
        return $post_id;
    }

    foreach ($post->get_post_meta() as $key => $value) {
        update_post_meta($post_id, $key, $value);
    }

    return $post_id;
}

/**
 * Registers our product custom post type.
 */
function register_custom_post_product() {
    register_post_type(Product::POST_TYPE);
}
add_action('init', 'register_custom_post_product');
```