I develop websites (and web applications) with the WordPress CMS (Content Management System), and when a new project lands you can guarantee that there will be a requirement for me to develop ‘Custom Meta Boxes’ to allow the user to have fine control over the sites content and layout.
I will detail how I built the CMB2 control Link Picker for CMB2 (available from all good WordPress plugin repositories). A screenshot of which can be seen below.
The Link Picker fires the built in WordPress ‘Insert/edit link’ dialog when you click the ‘Choose’ button. This can be seen in the screenshot below:
I am sure you will agree that having a control like this is incredibly handy if you want to give your site editors the ability add a link and also search WordPress for its internal links, rather then them having to cut and paste the links into a link field.
Introduction / History
For those not in the know, a meta box lives on the editor screen of a WordPress post, and will likely contain various form controls (text boxes, dropdown lists, checkboxes etc…). These controls let your website users easily be able to change a custom piece of text or functionality on the site.
WordPress lets you create meta boxes using functions (such as
add_meta_box), but creating meta boxes in this way can be a lengthy process, with lots of code repetition (especially if you want to use the same form controls in multiple projects).
Some of you may have heard of Advanced Custom Fields (ACF) which provides a GUI (Graphical User Interface) that lets you create meta boxes directly with WordPress.
ACF in my opinion is not a great tool for any web solution that scales. The plugin has too much reliance on data being stored within the database. This causes pain when deploying changes to a site, as you cannot just push up your code and see the changes instantly. Instead you have to do the work again on the various deployment environments (staging, live, et al). So we needed a solution that let us create meta boxes programatically. Enter CMB2.
We loved HM Custom Meta Boxes, and with the simplest snippets of code, we could quickly create custom Meta Boxes to do pretty much anything!
So why the move to CMB2? Well, HM Custom Meta Boxes unfortunately wasn’t getting a whole lot of love (I spoke to its lead developer and he is a very very busy man), whereas CMB2 was moving ahead with new features, new controls, and it had gained traction in the WordPress community with many people adopting it and releasing plugins to extend it (including several of our partner agencies).
Finally, as you might have gathered, working with CMB2 is as incredibly simple as we had become accustomed to, as both platforms share a common ancestor.
Before we begin, everyone has their own set of ideals on how to create a WordPress plugin, and I’ve tried a fair few, however the tutorial on ‘Root Composition in WordPress‘ by Tom J Nowell, completely changed the way I work. I find its approach clean, simple, and it makes future maintenance of any plugin a cinch. If you grab the source of the Link Picker for CMB2 plugin (also available on GitHub), you can see the methods he teaches in practice.
Building the Form
To build the form that renders the Link Picker, the first thing we need to do is hook into the
cmb2_render_[control_name] action. As I have called this control ‘link_picker’ we can complete the hook like so:
For those of you who don’t really understand the
add_action hook, it works as follows:
- The first argument
cmb2_render_link_pickeris the name of the hook we want to hook into.
- The second argument
array( $this, 'cmb2_render_link_picker' )is the function we want to call when this hook runs. Note that I am wrapping this in an array, with
$thisas the first parameter, because I am calling the function inside a class. If you are not working with classes you can just use the function name
10, is the order that we want the function to fire (the lower the number, the sooner it fires when the action is called).
5is the amount of parameters that will be passed to the function that I am calling (this will become clear shortly).
Next up we create the function that will render the form:
I have left the ‘DocBlock’ in the code above that describes what each of parameters passed into the
cmb2_render_link_picker() function does.
Note that my function begins with the
public declaration. This again is because I am working within a class. If you are not working with classes you can omit this.
The value of this field is passed into the function via the
$value parameter. In the case of this field, we will be passing through an array, as our control has three separate elements to it:
- The text
- The URL
- If the link opens in a new window (or not)
$value is not always set (for instance the first time the control is rendered) we need to initialise it with some default values. We do this with the following bit of code:
We can then get to work rendering out the form. Here is an example of the first text input control:
Phew! That looks a bit messy doesn’t it? Lets break it down, line-by-line:
- The opening paragraph tag.
- The opening label tag for the control, but with the
forattribute automatically set by the
_idparameter. This will automatically generate an ID for the control when it is rendered.
- The text of our label, built using text from the controls options array (or falls back to the word ‘Text’).
- The closing label tag
- The closing paragraph tag.
- Start PHP declaration
- Use an input control (part of the
$field_type_objectto create a form input (default type will be text).
- Start the array of parameters
- Set the class of the input.
- Set the name of the input, again using the
- Set the ID of the input, to the same ID that was set on the label tag.
- Get the value from the
$value, as this is an array, we want the ‘text’ key for this control.
- Close the array.
- Close the input function.
- Close the PHP declaration.
The URL form field markup is much the same, only to use HTML5 input types we can set an additional parameter of ‘type’ to ‘url’:
Finally we want to implement a dropdown. The markup is very familiar:
Note that the
$field_type_object function we are using is
select to generate a dropdown. Also note that on line 6 we have a new attribute of
options. Into this we are passing a string of ‘options’. This is generated prior to this control like so:
Then all we need to do is wrap it in some
<div>‘s and we have our fully rendered control:
And thats it! We have made our control! CMB2 automatically handles all the data we want to save, so nothing to do there.
The screenshot of the control we are creating (near the top of this post) has a few custom styles applied to it so it renders inline. I will not go into how to style the form today, but if you are curious you can download the plugin and view the source (also available on GitHub).
Making the control repeatable
For those of you who want to get a little more advanced, you can make the control work with CMB2’s repeatable regions. To do that you need to do a little bit of array mapping. To do that use the code below:
Choosing a Link
Of course the whole point of the link picker is to integrate into WordPress’s own link choosing functionality, making the ‘Insert/edit link’ dialog screen pop-up when the ‘Choose’ button is clicked.
In the above example the constants
MKDO_LPFC_TEXT_DOMAIN represent the root of the plugin and the text domain (used for translation) of the plugin, respectively.
As you can see, a lot of the internal WordPress libraries rely on jQuery to load the popup, so it makes sense for our pop-up trigger to do the same. This is done via the
/js/plugin.js which is loaded on line 10 of the above example.
Using the Control
So, after looking through the tutorial above, and possibly after reviewing the source code of the Link Picker for CMB2 plugin (also available on GitHub), or just downloading my version, you may now be wondering how to use the thing with CMB2. Well, it couldn’t be easier: