# Designing entities using WordPress custom post types

[Custom post types](https://codex.wordpress.org/custom_post_type) are one the most powerful features of WordPress. You can use them to save any type of data that you want in the [`wp_posts`](https://codex.wordpress.org/Database_Description#Table:_wp_posts) table. Most plugins that build complex features on top of WordPress (e.g. [WooCommerce](https://woocommerce.com)) rely on them. But custom post types aren't just useful for developing new features on top of WordPress. They also allow us to rethink how we use object-oriented programming with WordPress. And, as we'll see, this is an important step in your journey [learning object-oriented programming with WordPress](https://carlalexander.ca/object-oriented-programming-next-step-wordpress-developer/). That's because having a different view of custom post types will expand your object-oriented design horizons. They'll help you build new types of classes that you might not have considered before. And this can be a game-changer when creating larger object-oriented plugins or themes. # Reviewing how to use an interface with custom post types

Before we can begin, we're going to need to review how we can use [interfaces](https://carlalexander.ca/polymorphism-wordpress-interfaces/) with custom post types. This is a subject that we covered in a [previous article](https://carlalexander.ca/saving-wordpress-custom-post-types-using-interface/). That said, a lot of what we're going to do in this article uses design elements from that article. That's why we're going to take a moment to review it. We'll look at some of the design decisions that this article suggested. We'll use them as the foundation for what we'll see in this article. ## The PostType interface

Let's begin by looking at the interface that we created for custom post types. We had called it `MyPlugin_PostTypeInterface`. It looked like this: ```
/**
 * Interface for WordPress custom post types.
 */
interface MyPlugin_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();
}
```

The `MyPlugin_PostTypeInterface` interface defined two methods: `get_post_data` and `get_post_meta`. We used these two methods to extract the data stored in the objects that implemented the interface. This, in turn, allowed us to save that data inside the WordPress database. But why did we need two methods? Well, it's because these two methods target two different ways to save data in the WordPress database. And, with few exceptions, we'll need them both to save that data inside our object. The `get_post_data` method extracts the data that we to save in the `wp_posts` table. It's an array of post data that we want to pass to the [`wp_insert_post`](https://developer.wordpress.org/reference/functions/wp_insert_post/) function. Meanwhile, the `get_post_meta` method extracts the data that we want to save in the `wp_postmeta` table. It returns an array of keys and values that we want to save using the [`update_post_meta`](https://codex.wordpress.org/update_post_meta) function. ## Using the PostType interface

The article also went over how we used the `MyPlugin_PostTypeInterface` interface. The main interaction point was the `wp_insert_custom_post` function. You can see it below: ```
/**
 * Insert or update a custom post type.
 *
 * @param MyPlugin_PostTypeInterface $post
 * @param bool                                            $wp_error
 *
 * @return int|WP_Error
 */
function wp_insert_custom_post(MyPlugin_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 `wp_insert_custom_post` function has two parameters: `post` and `wp_error`. The `post` parameter is [type hinted](https://secure.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration). This means that it has to be an object implementing our `MyPlugin_PostTypeInterface`. The `wp_error` parameter is just a boolean value that mirrors the `wp_error` parameter in the `wp_insert_post` function. We use it to determine whether the `wp_insert_post` function should return a [`WP_Error`](https://codex.wordpress.org/Class_Reference/WP_Error) object when there's an error. By default it's `false` which means that the `wp_insert_post` function doesn't return a `WP_Error` object. The `wp_insert_custom_post` function itself shows how to use both methods from the `PostTypeInterface` interface. We use the `get_post_data` method first. It's to get the array that we pass as the first argument to the `wp_insert_post` function. We store the post ID returned by the `wp_insert_post` function in the `post_id` variable. We then use a [guard clause](https://carlalexander.ca/mastering-php-conditionals/) to check if `post_id` is valid. If it's is either `0` or an [instance of](https://secure.php.net/manual/en/internals2.opcodes.instanceof.php) `WP_Error`, we return it. However, if `post_id` doesn't meet either of those conditions, we know it's valid. We can then use the array from `get_post_meta` method. We loop through it using a [`foreach`](https://secure.php.net/manual/en/control-structures.foreach.php) loop using both a `key` and a `value` for each iteration. We then use those three values (`post_id`, `key` and `value`) as arguments for the `update_post_meta` function. This allows us also to update all the post meta values used by our object implementing the `MyPlugin_PostTypeInterface` interface. # Thinking about domains

Now that we've reviewed how we handled custom post types using the `MyPlugin_PostTypeInterface` interface, let's dial things back a bit. We need to think about the larger picture around custom post types. And to do that, we need to talk about the idea of [domains](https://en.wikipedia.org/wiki/Domain_(software_engineering)). So what are domains? Well, they're collections of knowledge surrounding a specific aspect of an application. These collections of knowledge often contain (but aren't limited to) business requirements, specific terminology (also known as [ubiqutous language](https://martinfowler.com/bliki/UbiquitousLanguage.html)) and application functionality. For example, if you're an e-commerce platform like WooCommerce, you could think of shipping as a domain. To ship an order to someone, WooCommerce must meet specific business requirements. There's application functionality that developers designed to meet those requirements. And to design that functionality, it's important for those developers to know specific terminology surrounding shipping. # But it's really about domain logic

What we've just described about domains is, in essence, [domain-driven design](https://en.wikipedia.org/wiki/Domain-driven_design). However, the goal here isn't to go over domain-driven design but to talk about domain logic. Domain logic is the application functionality that solves business requirements. You might also know it as [business logic](https://en.wikipedia.org/wiki/Business_logic). The big problem with domain logic is that it's hard to know where to put it. And it's hard regardless of whether [you use object-oriented programming](http://carlalexander.ca/object-oriented-programming-next-step-wordpress-developer/) or not. But the difference is that you can do something concrete about it if you do use object-oriented programming. We can create classes that represent various parts of our domain. And then those classes can manage the specific domain logic that applies to them. In domain-driven design, we would call these types of classes entities. Entities are an essential building block in domain-driven design. But they're a powerful concept on their own. Outside domain-driven design, you might know entities as models. ## Entity vs Model

The term model comes from [object-relational mapping](https://en.wikipedia.org/wiki/Object-relational_mapping) frameworks. It's what they call these types of classes. In practice, there's a conceptual difference between the two terms. That said, most developers use the terms interchangeably. But, for us, it's important to know that difference. With an object-relational mapping framework, a model is a class that also maps to the database in some way. This creates a [tight coupling](https://carlalexander.ca/coupling-cohesion-wordpress/) between your models and the database. The concept of entities isn't related to the database or any form of [persistence](https://en.wikipedia.org/wiki/Persistence_(computer_science)) at all. In fact, you can even use entities without ever persisting them. (Persisting is a fancy way of saying saving.) What defines an entity is what it represents. An entity is always something that needs a unique representation within our domain. If we continue with our WooCommerce example, an order would be an entity. But a shipment would one as well. They're both things that need a unique representation according to our WooCommerce domain. This idea of unique representation is often why there's a confusion between entities and models. Since an entity needs to have a unique representation, it's often persisted in a database like you would a model. But how you persist an entity doesn't matter. In fact, even if you don't persist it anywhere, something can still be an entity. # Persisting entities using custom post types

And now, we've gone full circle! This is how we can rethink how we use WordPress custom post types. We can use them as a way to persist our entities. Which is why we reviewed the article on the `MyPlugin_PostTypeInterface` interface. This is the method that we'll use to persist entities using a custom post type. But if you have another way that you want to use, that's ok too! (Like we said, how we persist an entity shouldn't matter. This is just one way to do it.) # Building a Product entity

But first, we need to build an entity. Let's keep going with the e-commerce domain that we were talking about earlier. We won't create an entity for an order or a shipment. Let's pick something a bit simpler: a Product. ```
class MyPlugin_Product
{
}
```

Above is our empty `MyPlugin_Product` class that we'll use as our product entity. Now, let's add a few properties to it. What would we say are properties that define a product? ```
class MyPlugin_Product
{
    /**
     * Flag to see if the product is available or not.
     *
     * @var bool
     */
    private $available;

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

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

    /**
     * The product's universal code.
     *
     * @var string
     */
    private $product_code;
}
```

So we've upgraded our `MyPlugin_Product` entity class and added four properties to it. First, we have the `available` which is a boolean flag that lets us know if the product is available or not. Next, we have `name` and `price` which are for the name and price of the product. (Pretty self-explanatory!) The last property is `product_code`. It's our product's [universal product code](https://en.wikipedia.org/wiki/Universal_Product_Code). Those are the barcodes that you see everywhere. ## Changing the price of a product

Now, we need to add some business logic to our product entity. For example, you might want to change its price. So let's create a method to do that: ```
class MyPlugin_Product
{
    /**
     * Change the price of a product.
     *
     * @return string
     */
    public function change_price($new_price)
    {
        $this->price = $new_price;
    }
}
```

### Thinking about the concept of price

So far, there isn't any business logic in our `change_price` method. We're just assigning `new_price` to the `price` property like you would with a [setter method](https://en.wikipedia.org/wiki/Mutator_method). But this is incorrect because we all know that a price isn't a normal data type like a string or an integer. We can't just assign it blindly. We first need to validate the `new_price` argument before assigning it to the `price` variable. This means that we need to figure out what defines the price of a product. While some elements of a price might vary from business to business, here are some common ones that apply to most prices: - Prices are numeric.
- Prices aren't negative.
- Prices only have two decimal places of precision.

### Adding our price business logic

Our `change_price` method doesn't account for any of these business requirements right now. So let's make some changes to it. That way we can at least cover the three requirements that we just talked about. ```
class MyPlugin_Product
{
    // ...

    /**
     * Change the price of a product.
     *
     * @param mixed
     */
    public function change_price($new_price)
    {
        if (!is_numeric($new_price)) {
            throw new \InvalidArgumentException('A product must have a "price" that is a non-negative number.');
        } elseif ($new_price < 0) {
            throw new \InvalidArgumentException('A product must have a "price" that isn\'t negative.');
        } elseif (!is_float($new_price)) {
            $new_price = (float) $new_price;
        }

        $this->price = number_format($new_price, 2, '.', '');
    }
}
```

Alright, so there's a lot more going on in the modified `change_price` method above now! First, our `change_price` method now starts with three [guard clauses](https://carlalexander.ca/mastering-php-conditionals/). The two first guard clauses are there to cover the two first business requirements that we had for prices. The first one checks whether `new_price` is numeric or not. The [`is_numeric`](https://secure.php.net/manual/en/function.is-numeric.php) PHP function is great for detecting that. If it determines that `new_price` isn't numeric, we throw an [`InvalidArgumentException`](https://secure.php.net/manual/en/class.invalidargumentexception.php). The second guard clause builds on the previous one. We know that `new_price` is numeric at that point. So all that we have to check now is whether `new_price` is negative or not. If it's negative, we throw another `InvalidArgumentException`. The last guard clause checks whether our numeric value is a [float](https://secure.php.net/manual/en/language.types.float.php) or not. If it's not, we [cast](https://secure.php.net/manual/en/language.types.type-juggling.php#language.types.typecasting) it as a float. We need this to meet the last business requirement surrounding our price. That business requirement was that a price should only have two decimal places of precision. A float is a numeric value with decimal precision, so we have that part covered. The issue is that a float has more than two decimal points of precision. So we need a way to ensure that our `new_price` only has two decimal places of precision. That's why we pass it through the [`number_format`](https://secure.php.net/manual/en/function.number-format.php) PHP function. Only after doing that, do we assign it to the `price` property. ## Persisting our entity

It's not too far-fetched to imagine our `MyPlugin_Product` class filled with methods like our `change_price` method. But, as you can see, methods like it don't retrieve or save data about a product. It's all about business logic and nothing more. ### Using the PostTypeInterface

This is where the idea to save a custom post type using an interface comes into play. This is the technique that we'll use to persist our product entity. This means that we need to have our `MyPlugin_Product` class implement the `MyPlugin_PostTypeInterface` interface. ```
class MyPlugin_Product implements MyPlugin_PostTypeInterface
{
    // ...
}
```

Next, we have to decide where we want to save the different properties of our entity. There's no clear rule to follow for this. You can do it whichever way makes sense for you. ```
class MyPlugin_Product implements MyPlugin_PostTypeInterface
{
    /**
     * The post type used by products.
     *
     * @var string
     */
    const POST_TYPE = 'myplugin_product';

    /**
     * Flag to see if the product is available or not.
     *
     * @var bool
     */
    private $available;

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

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

    /**
     * The product's universal code.
     *
     * @var string
     */
    private $product_code;

    // ...

    /**
     * Get the post data as a wp_insert_post compatible array.
     *
     * @return array
     */
    public function get_post_data()
    {
        return array(
            'comment_status' => 'closed',
            'post_name' => $this->name,
            'post_title' => $this->name,
            'post_type' => self::POST_TYPE,
            'post_status' => $this->available ? 'publish' : 'draft',
        );
     }

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

Here's our updated `MyPlugin_Product` class. We added the two methods from the `MyPlugin_PostTypeInterface` interface. We also added the `POST_TYPE` class constant which we'll discuss in a moment. ### Implementing the PostTypeInterface methods

The easiest method to talk about is the `get_post_meta` method. We use it to save data that doesn't quite fit in the `wp_posts` table. And the two properties that don't quite do are `price` and `product_code`. The method with more things going is the `get_post_data` method. This is the method that we use to map properties to the `wp_posts` table. But we also use it to give some more information about our product to WordPress. That's why our array contains `comment_status` and `post_status`. We want to set `comment_status` to `closed` so that people can't comment on our product. We also want to use the availability of our product to determine whether we set `post_status` to `publish` or `draft`. For the rest, we're going to use the `name` of our product for the `post_name` and `post_title`. The only thing left is the `post_type` or custom post type. This is where we use our `POST_TYPE` class constant. ### About using a class constant

But why are we using a class constant for `post_type`? Well, to begin with, we want to allow others to access this information. It's not just `MyPlugin_Product` class that might want to know what `post_type` it uses. That said, allowing others to know what `post_type` the `MyPlugin_Product` class uses is one thing. It doesn't mean we should allow anyone (even the class itself) to modify it. That's why it makes sense to use a class constant for `post_type`. # Why not extend the WP\_Post class?

So the last thing that we'll talk about is a bit of the elephant in the room. If you weren't aware of it, WordPress already has a class that it uses to represent posts. It's the [`WP_Post`](https://codex.wordpress.org/Class_Reference/WP_Post) class. If WordPress has a class that it uses to posts from the `wp_posts` database table, why aren't we using it? Why don't we have our `MyPlugin_Product` class [extend it](https://carlalexander.ca/using-inheritance-wordpress/)? Why don't we combine it with our `MyPlugin_PostTypeInterface` interface? These are all questions that we shouldn't leave unanswered. The major reason why we can't use the `WP_Post` class is due to the fact that it's final. So even we wanted to use it, we wouldn't be able to because of the [`final`](https://secure.php.net/manual/en/language.oop5.final.php) keyword. But even if `WP_Post` wasn't final, we'd still have issues trying to use it. That's because of the design of the `WP_Post` class. If you dig into the `WP_Post` class, you'll see that it has almost no business logic to speak of. All of it is in specific WordPress functions. Instead, WordPress uses `WP_Post` objects more like [data transfer objects](https://en.wikipedia.org/wiki/Data_transfer_object). If you're not familiar with the concept, data transfer objects are objects whose sole purpose is to transfer data. (The name is pretty self-explanatory!) Here, WordPress uses it to transfer post data from the MySQL database. But this job transferring data from the MySQL database isn't something that we care about with entities. In fact, it's something that we're trying to avoid since entities shouldn't care about their persistence. They should only care about business logic. That's why we don't want to use the `WP_Post` class with our entities even if it's tempting to do so. Our `MyPlugin_PostTypeInterface` is more than enough to deal with the persistence of our entities. And there's no need to tie it to the `WP_Post` class either. (Not that we could right now anyway.) # Where business logic lives

And this wraps up our look at entities and how we can use them with custom post types. This isn't as easy of a concept to understand as some of the other articles that you might have read. That said, this doesn't make it any less important to understand. As developers, we often need to solve business problems for our clients or the company that we work for. This becomes an issue when you're trying to design classes for WordPress. The logic used to solve these business problems needs to be somewhere. In a lot of situations, entities are the solution to that problem. That's why they're an important tool in your object-oriented design toolbox. And now you have a way to persist them using WordPress custom post types. *Photo Credit: [Christopher Gower](https://unsplash.com/photos/m_HRfLhgABo)*