As we come up on the end of this series, we have two more topics to cover:
- Saving information to and retrieving information from the database
- 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:
-
update_post_meta
for saving information to the database -
delete_post_meta
for removing information from the database
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:
<?php // If the 'Drafts' textarea has been populated, then we sanitize the information. if ( ! empty( $_POST['authors-commentary-drafts'] ) ) { // We'll remove all white space, HTML tags, and encode the information to be saved $drafts = trim( $_POST['authors-commentary-drafts'] ); $drafts = esc_textarea( strip_tags( $drafts ) ); // More to come... }
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):
<?php // If the 'Drafts' textarea has been populated, then we sanitize the information. if ( ! empty( $_POST['authors-commentary-drafts'] ) ) { // We'll remove all white space, HTML tags, and encode the information to be saved $drafts = trim( $_POST['authors-commentary-drafts'] ); $drafts = esc_textarea( strip_tags( $drafts ) ); update_post_meta( $post_id, 'authors-commentary-drafts', $drafts ); } else { if ( '' !== get_post_meta( $post_id, 'authors-commentary-drafts', true ) ) { delete_post_meta( $post_id, 'authors-commentary-drafts' ); } }
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:
- The post ID that is used in order to associate this information with the post
- A meta key that's used to uniquely identify the value
- 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:
<?php // If the 'Resources' inputs exist, iterate through them and sanitize them if ( ! empty( $_POST['authors-commentary-resources'] ) ) { $resources = $_POST['authors-commentary-resources']; foreach ( $resources as $resource ) { $resource = esc_url( strip_tags( $resource ) ); // More to come... } }
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:
<?php // If the 'Resources' inputs exist, iterate through them and sanitize them if ( ! empty( $_POST['authors-commentary-resources'] ) ) { $resources = $_POST['authors-commentary-resources']; $sanitized_resources = array(); foreach ( $resources as $resource ) { $resource = esc_url( strip_tags( $resource ) ); if ( ! empty( $resource ) ) { $sanitized_resources[] = $resource; } } update_post_meta( $post_id, 'authors-commentary-resources', $sanitized_resources ); }
If you peek into the database and look at this specific key, you should see something like this stored as the value:
a:3:{i:0;s:22:"http://tommcfarlin.com";i:1;s:19:"http://tutsplus.com";i:2;s:17:"http://google.com";}
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:
<?php // If the 'Resources' inputs exist, iterate through them and sanitize them if ( ! empty( $_POST['authors-commentary-resources'] ) ) { $resources = $_POST['authors-commentary-resources']; $sanitized_resources = array(); foreach ( $resources as $resource ) { $resource = esc_url( strip_tags( $resource ) ); $sanitized_resources[] = $resource; } update_post_meta( $post_id, 'authors-commentary-resources', $sanitized_resources ); } else { if ( '' !== get_post_meta( $post_id, 'authors-commentary-resources', true ) ) { delete_post_meta( $post_id, 'authors-commentary-resources' ); } }
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.
<?php // If there are any values saved in the 'Resources' input, save them if ( ! empty( $_POST['authors-commentary-comments'] ) ) { $comments = $_POST['authors-commentary-comments']; $sanitized_comments = array(); foreach ( $comments as $comment_id => $comment_value ) { $comment = strip_tags( stripslashes( $comment_value ) ); $sanitized_comments[ $comment_id ] = $comment; } update_post_meta( $post_id, 'authors-commentary-comments', $sanitized_comments ); }
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:
<?php // If there are any values saved in the 'Resources' input, save them if ( ! empty( $_POST['authors-commentary-comments'] ) ) { $comments = $_POST['authors-commentary-comments']; $sanitized_comments = array(); foreach ( $comments as $comment_id => $comment_value ) { $comment = strip_tags( stripslashes( $comment_value ) ); $sanitized_comments[ $comment_id ] = $comment; } update_post_meta( $post_id, 'authors-commentary-comments', $sanitized_comments ); } else { if ( '' !== get_post_meta( $post_id, 'authors-commentary-comments', true ) ) { delete_post_meta( $post_id, 'authors-commentary-comments' ); } }
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.
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:
<div class="inside"> <textarea id="authors-commentary-drafts" name="authors-commentary-drafts"></textarea> </div>
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:
<div class="inside"> <textarea id="authors-commentary-drafts" name="authors-commentary-drafts"><?php echo get_post_meta( get_the_ID(), 'authors-commentary-drafts', true ); ?></textarea> </div>
Note that we're passing in three parameters:
- The first is the post ID which is retrieved by using the
get_the_ID
function. - The second is the key that we've specified when saving the data to uniquely identify it.
- 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.
<div class="inside hidden"> <div id="authors-commentary-resources"> <?php $resources = get_post_meta( get_the_ID(), 'authors-commentary-resources', true ); ?> <?php foreach ( $resources as $resource ) { ?> <input type="text" value="<?php echo $resource; ?>" /> <?php } ?> </div><!-- #authors-commentary-resources --> <p><input type="submit" id="authors-commentary-add-resource" value="Add Resource" class="button" /> </div>
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:
<div class="inside hidden"> <?php $comments = $this->load_post_comments(); ?> <ul id="author-commentary-comments"> <?php foreach ( $comments as $comment ) { ?> <li> <label for="authors-commentary-comment-<?php echo $comment->comment_ID ?>"> <?php $comments = get_post_meta( get_the_ID(), 'authors-commentary-comments', true ); ?> <input type="checkbox" name="authors-commentary-comments[<?php echo $comment->comment_ID ?>]" id="authors-commentary-comment-<?php echo $comment->comment_ID ?>" <?php echo array_key_exists( $comment->comment_ID, $comments ) ? 'checked="checked"' : ''; ?> /> This comment has received a reply. </label> <p> <em><?php echo $comment->comment_author; ?></em>: <?php echo $comment->comment_content; ?> </p> <hr /> </li> <?php } ?> </ul> </div>
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.
Comments