Advanced Custom Fields (ACF) has been around for quite a while now, and it’s super popular. We’re on version 5.x now, and the earliest dated reference documentation I could find was for ACF 2.0 in 2011. There have been some bumps along the way.
One of the more recent issues was compatibility of the Select2 JavaScript library versions. It was mostly because two other popular plugins, Yoast’s WordPress SEO and WooCommerce, also use Select2. What is Select2? It’s the kick-ass library that lets you transform boring dropdowns to amazing searchable and formattable ones:
So what’s the big deal with ACF? Well, Select2 has gone through some major revisions as well, most notably the latest Select2 Version 4, which is not backwards compatible with Select2 version 3.x. So, if ACF brings in Version 4 and WooCommerce brings in Version 3, we’ve got problems.
Thankfully, ACF came up with an elegant way to deal with these incompatibilities by checking to see if another plugin had enqueued Version 3 and falling back to using a Select2 Version 3 adapter.
Future-proof it or just fix it?
So, what does this have to do with custom ACF fields? Well, in my case there was an old custom-built ACF field that used Select2. To add the Select2 library to the dropdown, it used a JavaScript call to acf.add_select2()
to add it. If you notice in the gist above, acf.newSelect2()
was added in ACF 5.6.5, and it replaces acf.add_select2()
. In the future, how can we avoid running into this problem? In practice, there are two options:
- Extend an existing ACF field that is close to what you want to do.
- Create a new (or updating an existing) custom field and use ACF to add Select2.
Your first option is the best one. Here’s why: it simplifies your code investment by leveraging as much as possible from ACF. But, I’m also including the quick-fix instructions specific to the Select2. That way, if you have an existing field that you just need to get working for the time being, you can do that as well.
Option 1: Simplify by extension
One of the fields I needed to fix that used acf.add_select2()
was strikingly similar to an existing ACF field—the relational field called Taxonomy. The built-in ACF Taxonomy field also uses Select2. The only difference between the custom field that needed repair and the built-in one is that the custom field would display terms from multiple categories, instead of just one. I had an a-ha moment on a call with Corey Collins when he suggested aloud to let ACF handle the hard work and to just tweak what I needed. Brilliant idea, Corey!
The custom field used to look like this:
Because the AcfFieldMultipleCategories
class was essentially a copy of class-acf-field-taxonomy.php
, it was almost 600 lines of code.
Instead of extending acf_field
, I was able to instead extend acf_field_taxonomy
and reduce the PHP code by half and get rid of the custom JavaScript entirely.
What’s different?
Here’s a brief breakdown of the things that are different in the AcfFieldMultipleCategories
class and why. You can compare much of the code above to what’s in class-acf-field-taxonomy.php for reference.
- The class doesn’t get defined until
init
. This is because ACF doesn’t load the parentacf_field_taxonomy
class right away. - Using
initialize
instead of__construct
: ACF 5.6.0 switched to usinginitialize
instead of__construct
so I wanted to follow suit. Our class callsparent::initialize()
to get all of the defaults from the taxonomy field, then sets only the fields we want to override. - AJAX actions for
wp_acf
: These actions are present on the parent, but when they’re registered, they reference a specific instance of an object—the parentacf_field_taxonomy
class. We redefine them here to get called on ourAcfFieldMultipleCategories
instance. - Extra CSS classes in
wrapper_attributes
: We need our custom field to be rendered with a CSS class ofacf-field-taxonomy
. This is what the ACF Select2 library is looking for to attach to fields. Select2 will behave exactly how it would on a taxonomy field; we’re just changing the query that happens on the back end. get_ajax_query
has been overridden: It gets called from the parentajax_query
method. We override this to get a result set of terms from multiple taxonomies.load_value
has been overridden and simplified to remove filtering.render_field
has been overridden and simplified to only render a select field because that’s the only option we’re allowing.render_field_select
has been overridden to call a customget_term_by_id
function.-
render_field_settings
has been overridden to remove the Appearance setting (remember we only want it to display a dropdown). We also removed the Create Terms option, as we won’t be saving any new terms because they’re coming from multiple taxonomies.
Is it a lot? Sure, but it’s far less duplication than before and through simplification, making it less prone to bugs and errors. But if that’s still too much, can you take the easy way out…
Option 2: Just update Select2 calls
This option is an easier short-term solution but may postpone further compatibility issues. Just switch any calls from add_select2
to newSelect2
. Any parameters to that function that were previously in snake_case
need to be changed to camelCase
. Here’s a diff of one that I updated:
- acf.add_select2( $select, { + acf.newSelect2( $select, { ajax: 1, - ajax_action: "acf/fields/post_object/query", - allow_null: 0, + ajaxAction: "acf/fields/post_object/query", + allowNull: 0,
That’s it. I chose this option for a different and far more complex custom ACF field. It had four inputs in one field, which is nothing like any built-in fields. This one is just going to stay as-is and will try to follow the ACF JavaScript conventions.
Getting Permission
When possible, I prefer the first method of doing things because it’s a better long term solution. You can do Option 2 in less than an hour and then still have time left over to convince your manager that Option 1 is better for the long term. Get some time scheduled to fix it the right way, and it’s a win-win for you and your project.