Creating a Custom CMB2 Link Picker Control for WordPress

Creating a Custom CMB2 Control
Standard

In this tutorial I will look at how you can create a custom control to extend the functionality of CMB2 (Custom Meta Boxes 2) by WebDevStudios.

At Make Do we only 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 us 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.

Link Picker CMB2 Control

The ‘Link Picker’ CMB2 control in action

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:

Link Picker Pop-Up

Hitting the button lets you choose from a link (or add your own)

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.

An example of a meta box with various form controls

An example of a meta box with various form controls

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).

Why CMB2?

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.

Before we adopted CMB2, we previously used HM Custom Meta Boxes from those lovely humans at Human Made (which began as a fork of WebDevStudio’s forerunner to CMB2, ‘Custom Meta Boxes’).

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!

HM Custom Meta Boxes Markup Example

HM Custom Meta Boxes Markup Example (this is the markup for the Instagram Meta Box in the first Screenshot)

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.

Tutorial

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:

  1. The first argument cmb2_render_link_picker is the name of the hook we want to hook into.
  2. 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 $this as 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 cmb2_render_link_picker.
  3. The 10, is the order that we want the function to fire (the lower the number, the sooner it fires when the action is called).
  4. The 5 is 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)

Because the $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:

  1. The opening paragraph tag.
  2. The opening label tag for the control, but with the for attribute automatically set by the $field_type_object _id parameter. This will automatically generate an ID for the control when it is rendered.
  3. The text of our label, built using text from the controls options array (or falls back to the word ‘Text’).
  4. The closing label tag
  5. The closing paragraph tag.
  6. Start PHP declaration
  7. Use an input control (part of the $field_type_object to create a form input (default type will be text).
  8. Start the array of parameters
  9. Set the class of the input.
  10. Set the name of the input, again using the $field_type_object helper.
  11. Set the ID of the input, to the same ID that was set on the label tag.
  12. Get the value from the $value, as this is an array, we want the ‘text’ key for this control.
  13. Close the array.
  14. Close the input function.
  15. 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.

Styles

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.

To make this happen we rely heavily on JavaScript. In particular I am using jQuery to make things happen.

Before I show you the JavaScript that launches the dialog, we must first enqueue WordPress’s own internal JavaScript first, that will pre-load the modal’s and libraries that our code depends on. That looks a little something like this:

In the above example the constants MKDO_LPFC_ROOT and 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 classes that we wrapped around our form controls, the JavaScript targets the controls, and pushes the selected result from the link picker pop-up into the relevant control fields.

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:

Matt Watson

Technical Lead at Make Do
Matt Watson is the co-founder and technical lead of WordPress agency Make Do. Matt loves writing and learning about code, and considers himself lucky to be doing what he loves for a living. Find out more about Matt, or get in touch to hire Matt for your project.

3 thoughts on “Creating a Custom CMB2 Link Picker Control for WordPress

  1. This is amazing! So much better than the controls I was creating …

    Just out of curiosity, is it possible to turn fields on or off? For example, I am creating a “View Project” button, so I need to select the page URL. But the button will only say “View Project” so there’s no need for a text box to change the button text. Can I turn the text field off for each instance that it’s not needed?

    • Hi Nate,

      Sure, you could create a settings page, then check for the various options when the control is rendered to determine what should show, and what shouldn’t.

      You may need to tinker with the JavaScript too to check if the relevant elements are there.

Leave a Reply