Creating Maintainable WordPress Meta Boxes: Save and Retrieve

As we come up on the end of this series, we have two more topics to cover:

  1. Saving information to and retrieving information from the database
  2. Refactoring the code so that it becomes more maintainable for us

In the previous article, we looked at validation, sanitization, and implementing this functionality for the elements that we've displayed on the front-end. In this article, we're going to continue the process by saving the information to the database, retrieving the information, and displaying it on the front-end.

Along the way, we'll also look at some of the built-in WordPress API functions designed to help make this a bit easier for us as well as some tips for double-checking our work in the database to verify our information is being saved exactly as we expect.

We've got just a bit more to go in order to bring this plugin to life, so let's get started.

Saving Data

In order to display data on the front-end, we obviously need to get it into the database first. Since we're working with meta boxes, then we can use functions that are available via the Meta Box API in order to save this information.

Specifically, we're going to be working with the following functions:

There is another function, add_post_meta, that is also available for writing information to the database; however, update_post_meta does the same thing if the data does not already exist in the database.

Another question that I've seen come up when it comes to deleting post meta data is why? That is, why delete information rather than save an empty value? 

You can argue this is a personal preference - and it is - but if you're working with an elaborate plugin that has a number of different fields, and the fields have no value, then it makes sense not to maintain an empty row.

Furthermore, unless the absence of a value is meaningful for something on the user interface, then there's also no reason to maintain an empty value as we're allowing the database to more closely mirror the information that's being displayed on the screen. 

Keeping the user interface, application layer code, and database as consistent as possible is helpful when trying to write maintainable code. 

So with that said, let's look into the process of saving the fields for each of our input fields.

1. Draft

Recall from the previous post that the Draft tab contains a single textarea that is meant to be a place for authors to collect various notes and URLs from around the web that are relevant to the content they are preparing to publish.

When we last left this code, we had the following:

Here, we're looking to see if the content of the $_POST array is populated. If so, we then sanitize the information using trim and esc_textarea.

At this point, we're ready to write it to the database so let's replace the line that reads // More to come... with the following code (note that we'll take a more in-depth look at the code after the block):

Here, we're using the update_post_meta function in order to add or update the content in the database. Note that the function takes three parameters:

  1. The post ID that is used in order to associate this information with the post
  2. A meta key that's used to uniquely identify the value
  3. The actual meta value associated with the meta key

Notice also that if the value of the $_POST array is empty then we check to see if there is a value for the draft in the database and, if it exists, then we remove it.

2. Resources

Because we've already laid all of the ground work for sanitizing the information and we've seen how to both update and delete information in the database, doing the same for the Resources tab is more of the same.

The one exception is that since we're dealing with a dynamic set of information, we need to dynamically associate the post with a unique ID based on how many resources we're saving.

In the previous post, our code looked like this:

When it comes to dynamically processing information, a foreach loop works great; however, when saving information, we need to associate a unique key with each value. 

One option would be to setup a for loop in order to suffix the meta key with a a unique key (by using the iterator for each value in the loop), but this can cause problems when it comes to deleting information. Specifically, if the user inputs a value for the first, second, and third input but then removes the second input leaving only the first and third when updating the post, we need to properly delete those empty values and shift all of the records accordingly.

This can be done in a number of different ways, but it's actually easier to save a single serialized array to a unique index rather than try to do anything fancy with database rows, queries, and so on.

As such, we update the code above to look like this:

If you peek into the database and look at this specific key, you should see something like this stored as the value:

We'll see how this plays out when we retrieve the information from the database later in this article. Note also that we need to account for the case when a user has removed all instances of resources.

Just as we did in the first part of the article, we simply delete the post meta data if a value exists. This can be done using very similar code:

Now we need to save the values for the last meta box.

3. Published

The final tab of the meta box, the Published tab, is going to be the easiest one for us to update as it pulls together everything we've looked at thus far in the article.

Specifically, we're iterating through a collection of values, writing them to an array, and then serializing the array to the database. Perhaps the most important thing to note is that we're using an associative array and we're indexing each value by the value of the comment ID.

As we'll see later in the article, this will make it much easier to set the values on the user interface.

Just as we did in the previous section, if there's nothing specified in the $_POST array, then we check the existence of the values in the database and, if they exist, we delete them:

As mentioned, this final example pulls everything together that we've seen for the last two tabs, so it should be relatively clear code to follow at this point.

A Word About The Database

Before going any further, let's take a moment to understand how we may end up querying information from the database. 

Let's say you have a post with the ID of 9000 (yours will vary based on your setup). You can take that ID and look in the wp_postmeta table to see all of the meta information that's associated with the post.

The Post Meta Data Table

Furthermore, you can specify the key to pull back only the information associated with the post ID and key.

If you only specify the post ID, you'll see all meta information associated with the post. If you specify only the key, then you'll see all post IDs that contain content for their drafts. If you specify both the post ID and the key, you'll pull back only the draft information that you've specified for a single post.

Speaking of retrieving data, let's look at the steps necessary to display the post meta data in the dashboard of our plugin.

Retrieving Data

Now that all of the information has been saved to the database, we can introduce code that will retrieve it and display it in the proper tab of each plugin. The nice thing about this is that it will be using functions and constructors (like get_post_meta and for) that we've already been using.

1. Draft

Locate admin/views/partials/drafts.php. Assuming you've been following along with everything up to this point, the code should look like this:

To populate this textarea, we need to make a call to get_post_meta using the current post ID and the key that we used to save information earlier in this article. Take a look at the following code:

Note that we're passing in three parameters:

  1. The first is the post ID which is retrieved by using the get_the_ID function.
  2. The second is the key that we've specified when saving the data to uniquely identify it.
  3. The third is a boolean value true that's telling the function to return us the value as a string rather than in an array.

If the value doesn't exist, then it simply returns an empty string so the textarea is empty.

2. Resources

For Resources, we make a similar call; however, this time we want to iterate through the results so that we can dynamically create the user interface.

Because of the way WordPress serializes the array, we still want the information returned in a string format (though it will be a de-serialized array) that will allow us to use a foreach loop to iterate through it.

In short, we retrieve the information from the database, loop through it creating an input element for each value and then rendering it to the page. 

This also allows us to remove elements by simply deleting a value and then updating the post. From there, the display will re-render itself such that there are no empty input elements.

3. Published

Arguably, this is the easiest part of the plugin. Since we've already got so much code in the template, the only thing we really need to do is determine if the value for the checkbox is set in the meta data array.

Since we're using the comment ID as the numerical index of the array, we can simply check to see if the comment ID is contained in the array of meta keys that's returned from the meta data.

Here's how:

Notice that we retrieve the value from the database, again passing true as the third value. 

Next, we take the current comment ID and check to see if that value is contained in the array keys (by using array_key_exists) of the post meta data that was returned. If so, we mark the checkbox as checked; otherwise, we don't do anything.

Up Next

At this point, we have a fully functioning plugin that fulfills all of the requirements that we set out to build starting with the first article in the series.

But is the plugin itself maintainable? That is, does it fulfill the primary objective of this series?

In someways, yes but there is room for improvement. Since part of development has to do working with code that we inherit, we're going to take a look at how to refactor some of the code that we've written to make it easier to understand and more maintainable.

Additionally, we'll be looking at reasons for why we're performing some of the refactoring that we're doing. After all, it wouldn't make much sense to simplify code or to move it around without any rationale.

But before doing that, go ahead and work through this article and take a look at the code from the associated GitHub repository and leave any comments, questions, or general feedback below.



Related Articles