How To Save WordPress Submenu Page Options

If you’re using functions such as add_menu_page or add_submenu_page to create pages in the WordPress dashboard, it’s pretty easy to get something up and going, but what if you’re looking to actually save WordPress submenu page options?

Submenu Page Options

That is, let’s say you’re creating a page in the dashboard that’s tied to a new menu item – perhaps something that’s being added to the Tools menu – and you want to display some options among some other text or some other element or set of elements that you’re displaying.

It’s completely possible to use the Settings API to do exactly that, but it may also be a little heavy-handed for saving a couple a small set of options.

Luckily, WordPress has a hook that’s available that makes it pretty easy to save information like this that’s completely usable outside of the Settings API.

Save WordPress Submenu Page Options

When working through this scenario, there are a number of things that we need to take into account:

  1. Adding the submenu page item
  2. Rendering the display for the submenu page
  3. The content for the submenu page
  4. Saving the options displayed on the submenu page
  5. Making sure that the user has permission to save the options

I’ll break down each of these into their own set of code samples (if you’re reading this in an RSS reader that doesn’t support GitHub gists, then you may want to hop into the browser to read the remainder of this post), with comments.

Of course, I can’t predict each and every use case so I’ve done what I can to comment the code as best as possible where it will vary depending on your implementation.

1. Add The Submenu Page

First, assume that a new submenu page has been added to the WordPress dashboard via the add_submenu_page API. To do this, we’ll use the admin_menu hook and then define a function like this:

If you’ve used this hook and this API call before, then there’s nothing that should be particularly interesting about the code above; however, if you’re new to this function then you may see the manage_options argument.

This is the capabilities requires to view this menu. For more information about this particular argument, see this page in the Codex.

2. Render The Display For The Submenu

Next, we need a function that will render the content of the page that’s related to our submenu page. This is the acme_render_submenu_page argument in the previous section.

I generally define these types of functions as simple as possible:

Notice that I’m only making a single call to include_once and it pulls in an external PHP file. I do this because I think including marking in the context of a function inside of a PHP file makes the file hard to follow, difficult to trace, and generally mixes too many different languages in a single file.

3. The Submenu Page

To that end, I usually create an external file. The file will still include certain calls to the WordPress API (such as wp_nonce_field) but usually consists of more markup than PHP.

For example:

Make sure to notice the HTML comment in the code above. In some cases, this may be something as simple as an input element; other times, it may be a little more complex.

Whatever the case, name the elements carefully as they matter when it comes to saving the options.

4. Saving Options

In order to save the input from the user, we generally have to make sure the user has permission, then we have to sanitize the input, then we save it (or delete it).

Heads Up…

Perhaps the key to this entire post lies with the name of this hook: load-tools_page_acme-submenu-page which you can see defined at the top of the file.

This is a dynamic hook that WordPress provides that allows us to hook into the display of the submenu page. You can read a little bit more about this in the Codex but suffice it to say that this function will fire whenever the page with the specified ID (in our case, acme-submenu-page) loads.

This is why we need to check to see if the user has permission to save as it will check to see if the $_POST collection is even set. If it is, then something is being submitted.

Okay, Back To The Code…

Notice that we’ve defined the $action and the $nonce values based on the arguments we passed to wp_nonce_field in the previous section. I’ve also got a function called acme_user_can_save that we’ll look at momentarily.

In the meantime, the important thing to note here is that whenever you’re inspecting the $_POST collection, you need to make sure that you’re checking the index of the array based on the name attributes of the elements that you’ve defined when creating the markup for the page.

Finally, you may not want to delete options if the value doesn’t exist in $_POST; however, I usually opt to do this because it means the user has unset, cleared, or reset the element from whatever value it previously had.

If you opt not to do this, then the value may always be set.

5. Make Sure The User Has Permissions

Finally, as a security measure, we want to make sure that the user has permission to save the information.

Here, we check two things:

  1. That the nonce value is set in $_POST
  2. If the value is set in $_POST, then it validates against what WordPress is expecting

If both of these values are true, then we’ll assume the user has permission to save their information; otherwise, the function will return false and whatever has been submitted will not be saved.

 

10 Replies to “How To Save WordPress Submenu Page Options”

  1. Great post, Tom (as always). I’ve used a similar check on the nonce, but this feels more polished. Question, though: in my searches, I ended up concluding that I needed to also use a line such as:

    check_admin_referer( 'acme-submenu-page-save', 'acme-submenu-page-save-nonce' );

    Do you feel this is redundant, or unnecessary?

    1. I think using this function is fine — it’s probably easier to read or to deduce what the code is doing in contrast to validating the nonce value, but I wouldn’t use this and a nonce verification function just because that would be redundant.

      I’d use either/or, but I don’t really know if one outweighs the other. They seem to practically do the same thing.

  2. Hi Tom,

    Great tutorial! Just curious on your reasoning for using the load-tools hook? This will fire every time the page is loaded.

    It seems that it can be done with less code just using the settings API- just set the form action to options.php and use register_setting in the admin_init hook. This also makes sanitization very simple.

    Maybe I’m missing something?

    1. Great tutorial! Just curious on your reasoning for using the load-tools hook? This will fire every time the page is loaded.

      Thanks! And you’re right — it will fire on every page load which is why I think it’s good to use a conditional early in the function.

      I also think the function should be really light in terms of what it’s doing. There’s no need to do a lot of heavy-handed work for conditional logic on something like this.

      It seems that it can be done with less code just using the settings API- just set the form action to options.php and use register_setting in the admin_init hook. This also makes sanitization very simple.

      Less code might be a stretch because of the nature of registering the setting, registering the section, adding the option, etc, etc. The Settings API is nice, but it’s not easy to understand.

      The admin_init hook also fires on every dashboard page load, as well so that may be debatable.

      If you have more than a single option, then I think it makes sense to use it, but for something this lightweight, :shrugs:, I think you can get away with a load-* hook.

      Maybe I’m missing something?

      Nope – not at all! You bring up good points and one that’s worth considering, IMHO. I think it’s something that people should consider when building something like this.

      Sometimes you may choose one way to solve it; sometimes, it’s another way. Depends on the needs of what you’re doing.

      1. Thanks Tom, all good points. Definitely agree on the Settings API not being easy to understand (one of your previous tutorials helped me out a great deal on that).

        Just to clarify on one thing from my comment above – you don’t necessarily need to use a full implementation of the Settings API (with add_settings_section, add_settings_field, etc.) – register_setting also works with “hardcoded” options as shown in the example above if the form action is set to options.php.

        But agreed – depending on what you’re trying to do there may be a few different approaches.

        1. Sorry! One last thing – your approach does seem a lot more flexible – maybe a user can’t submit to options.php, or maybe they need to run additional logic before/after save. Your approach should work 100% of time.

        2. > Just to clarify on one thing from my comment above – you don’t necessarily need to use a full implementation of the Settings API (with add_settings_section, add_settings_field, etc.) – register_setting also works with “hardcoded” options as shown in the example above if the form action is set to options.php.

          Ah, yes. You’re right! I’m so used to end-to-end with the Settings API that I often don’t think of it like this.

          Thanks for bringing this up.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.