WordPress legacy and functional programming actually makes all websites, those hundreds of millions using this CMS in the world, stay alive. However, for those who use to manage and build a lot of websites, some routines prove to be a pain in the arse.
While inserting or updating posts is not complex, it remains laborious. Depending on what you intend to do in terms of programatically add posts, hundreds of lines and a handful of functions are necessary.
When you deal with different metadata… well, you know. So, my question is: couldn’t we make those two core functions a bit smarter? Maybe encapsulating both inside a class? Like Obama says: yes, we can.
wp_insert_post – the tip of the iceberg
No problem with this function. The problem, however, starts when you want to insert extra metadata, or set a new tag or category, especially if they don’t exist. At least 20 different functions can be used together to set all of those aspects. Some examples include:
- wp_set_post_categories()
- wp_set_object_terms()
- wp_update_term()
- update_post_meta()
- wp_set_object_terms()
Ideally, when you insert a new post, you should be passing all data together. This bunch of functions are then used sequentially, sometimes multiple times. That enlarges the code and increase the odds of typos, mistakes and errors.
Let’s try to build a class that can manage everything at once. Woocommerce actually does that with WC_Product class. Adding a new product can be accomplished by a new instance, and then all data can be added by functions that define the properties (price, name, discount and so on). After having all necessary data held by the class object, you can just use the methods save()
or update()
.
So that’s the task: let’s build, step by step and in parts (the class will make things easier, but the full tutorial would make this article really long).
Step 1 – Creating a new class
Creating, not extending. Especially because the original WP_Post or WP_Post_Type classes in WordPress are tagged as final, they can’t be extended. But it’s OK – we don’t want to do that. You can create a class with any name, but let’s be clear in our intentions and functionalities. Also, let’s start with properties that usually appear when inserting new posts.
/**
* Inserts, updates and deletes posts and all associated meta and terms
* @package WP_Add_Post
*/
class WP_Add_Post
{
protected $post_title;
protected $post_content = 'Not null';
protected $post_type = 'post';
protected $post_status = 'publish';
}
Posts or post types are usually inserted with the title being the main and first data. The post content cannot be null, or WordPress will throw and error. Also, as default, let’s consider the post type as being a ‘post’, and the status as ‘publish’. That means we could add a new post just passing the title.
In a second basic step for creating the class, we need a __construct() and a few methods. Let’s do that:
class WP_Add_Post
{
// Properties and eventual constants
protected function __construct(string $post_type = null)
{
if(!is_null($post_type)) {
$this->post_type = $post_type;
}
public function save() {}
public function update(int $post_id) {}
public function delete($post_id, $force = true) {}
}
First, I decided to pass the post type as an optional argument while instantiating the class. However, it could be managed in the same way we will manage the rest of the properties: getters and setters.
Besides the constructor, the basic methods will be save(), update() and delete(). Instead of giving the option of forcing or not the deletion (bypassing the trash), we could add another trash() method, as a faster way of not deleting for good.
Anyway, each of these main methods will perform one of the core post management actions in WordPress: insert, update or delete posts. Planning the next steps, these methods will need to concentrate all tasks related to those actions: adding or modifying any kind of metadata, taxonomies, attachments and even more, like ACF fields, Gutenberg blocks, etc.
Step 2 – initial jobs and class getters and setters
In a next step, we want our main methods to perform the basic operations. Also, we can start populating the class with the necessary getters and setters, which may involve new properties for metadata, post characteristics, taxonomies, etc.
We could start with the delete() method. As it just needs a post to exists, this method actually doesn’t use any of the class properties, unless we wanted it to delete posts by name, content or any other aspect. For now, let’s keep it simple:
public function drop($post_id, $force = false)
{
wp_delete_post($post->ID, $force);
}
The methods save() and update() are actually quite similar. save() won’t have any argument, as all data will be got or modified through getters and setters, and update() just take the post ID. So, all we need to do is getting all data, and use them to insert or update a post. However, let’s think together – we need some few tweaks besides the simply use of the native WordPress functions:
- For
save()
, we need to check if the post already exists - In both methods, we want all post meta to be added together
- Not now, but we also need to take care of possible attachments
- Categories and tags must be not only added, but created if they don’t exist
- Later, we can think of adding ACF fields update, Yoast settings, and so on
Getting and setting
Inside the class we can add methods to get and set any of the variable, which can be used afterwards, as long as we declare them public. I’ll use the title and the content as examples:
class WP_Add_Post
{
// ...
public function setTitle(string $title)
{
$this->post_title = $title;
}
public function getTitle()
{
return $this->post_title;
}
public function setContent(string $content)
{
$this->post_content = $content;
}
public function getContent()
{
return $this->post_content;
}
Also, we can add getters to group properties we are working with. Something like getPayload(), to return all set variables and use them altogether on the methods save()
and update()
.
public function getPayload()
{
return [
'post_title' => $this->post_title,
'post_status' => $this->post_status,
'post_type' => $this->post_type,
'post_content' => $this->post_content
];
}
Once we managed all properties inside the object, we can write, finally, the saving and updating code.
Saving or updating
Now, let’s write the basic code for adding new posts or changing existent ones. Before adding, though, we want the method to check whether the post already exists or not. So let’s do that using the title, and just then inserting the post. The core task performed by each method, of course, will be related to the functions wp_insert_post()
and wp_update_post()
.
public function save()
{
if(post_exists($this->getTitle())){
return;
}
$post = $this->getPayload();
$post_id = wp_insert_post($post);
}
public function update($post_id)
{
$post = $this->getPayload();
$postarr = array_merge($post, ['ID' => $post_id]);
wp_update_post($postarr);
}
For updating, we need the $post_id for the existent post. And then, inside the method, we use array_merge(), so we can repass the ID together with the rest of the payload, for wp_update_post()
.
Using the class
We will improve the class in a second article, but we can already use it as it is. For such, all we need to do is to require or autoload the class, and then create a new instance, like this.
add_action('admin_init', 'run');
function run()
{
$post = new WP_Add_Post();
$post->setTitle('A title as example');
$post->setContent('Any content you want');
$post->save();
}
For Part II
In the second part of this tutorial, we will deal with various meta, terms, taxonomies and also something that has been different since WP 5.0 – adding content with particular Gutenberg blocks.
Hi, Thank you for your post, however at step 1 you said : delete but at step 2 you marked “drop”
Oops, I’ll change it. Anyway, it is just a name given for a method. It could be anything else, like purge() or kill().
Really good tutorial, thanks. I’m really looking forward to the second part of it.