Menu Search
Jump to the content X X
Smashing Conf New York

We use ad-blockers as well, you know. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. upcoming SmashingConf Barcelona, dedicated to smart front-end techniques and design patterns.

Extending Advanced Custom Fields With Your Own Controls

Advanced Custom Fields1 (ACF) is a free WordPress plugin that replaces the regular custom fields interface in WordPress with something far more powerful, offering a user-friendly interface for complex fields like location maps, date pickers and more.

In this article I’ll show you how you can extend ACF by adding your own controls to tailor the experience to your needs.

How ACF Works Link

ACF is a collection of fields which can be added to a number of locations in WordPress, such as posts, taxonomies, users and so on. They share a common interface and a WordPress-compatible saving mechanism (using meta fields and options).

A map and a radio field created with ACF2
A map and a radio field created with ACF. (View large version3)

Each ACF field has field settings which you can think of as the back-end options for a field. Field settings allow you to control how the field behaves when displayed to the user. You may be able to control default values, how data is saved, and so on.

There is a default set shared by all fields, like field label, field name, field type, field instructions, required setting and conditional logic, but there are some field-specific settings as well.

A good example is the image field which allows users to select an image and save it. The field settings allow you to define the following:

  • Return value (image object, image URL or image ID)
  • Preview size (any defined image size)
  • Library (all or uploaded to post)
Settings for the image field in ACF4
Settings for the image field in ACF. (View large version5)

Fields are created within field groups which can be placed in a number of locations using powerful rule sets. Not only can you add your fields to edit profile pages, you can also restrict the visibility of a field group based on role, allowing access to admins only, for example.

Location rules for an ACF field group6
Location rules for an ACF field group. (View large version7)

What’s Available Out Of The Box Link

Right now ACF has 22 fields available, which includes basic fields like number, email, multi-select and radio, but more advanced ones as well, such as taxonomy selection and the user selector. You also get a few fields that use JavaScript to create a great interface, like the map field or the date and color-picker fields.

There are also quite a few fields available in the plugin repository. I’ve created six ACF plugins myself8, like the Google Font Selector and Sidebar Selector, but a quick search in the repository9 will come up with star rating fields, recent posts, link pickers, widget area fields and so on.

If you are a developer I can also heartily recommend the pro version of ACF10. It costs $100 – which is quite a bit – but you can use it in unlimited projects. It contains a repeater field, a gallery field, a flexible content field and the ability to add options to options pages very easily.

The great thing about ACF is that the pro version is nice, but you don’t need it in order to build something awesome. You can use the built-in fields or write your own if you need something different. Let’s look at how that can be done.

Extending Advanced Custom Fields Link

ACF can be extended by creating a separate plugin which integrates with the main ACF plugin. The heavy lifting is done by the ACF Field Type Template11 which you can grab from GitHub.

This includes all the files you need and has great inline commenting that walks you through the process. It also contains all the possible functions you can use. You won’t use all of them for each field, but you can leave the unwanted ones empty or delete them altogether.

In this tutorial I’ll show you the steps I follow when creating an ACF plugin. I’ll be creating a country selector which lets you select any country from a drop-down list. By the end you should be able to reproduce it and create your own, so let’s get cracking!

Step 1: A Local Environment Link

I like to do all my WordPress work locally. I won’t go into too much detail here – you can take a look at Rachel Andrew’s “A Simple Workflow From Development To Deployment12” if you need some help. In a nutshell, I use a simple Vagrant box to create a local server and I use virtual hosts to create multiple projects.

The one additional aspect of my workflow is using symlinks to manage my plugins. I do this for three reasons:

  • I can keep my plugins separate from my WordPress installs.
  • A plugin can be symlinked to multiple WordPress installs which means I update the plugin in a central location and all installations use that code.
  • I can use any folder structure I like which comes in handy when working with Git packages.

The process isn’t too difficult. Take a look at Tom McFarlin’s “Symbolic Links with WordPress13” article for more information. Since the template is designed to be a plugin, this step is not strictly necessary but I think symlinks are worth looking into for development purposes.

Step 2: Adding The Plugin And Renaming Link

Once you’ve grabbed the template from GitHub, copy and paste the whole directory into your plugins folder and rename it to acf-country_selector. You’ll see that some of the files have FIELD_NAME in them; this is a placeholder for your actual field name which should be the same as the name of the folder (after “acf-“). In our case the field name is country_selector.

Some of you may be wondering why I’ve used an underscore instead of a dash: it’s common practice to use dashes in file names. The use of an underscore relates to consistency and a rule for translatable strings.

We’ll need to replace FIELD_NAME inside files as well. In some cases the placeholder is part of a function name where we can’t use dashes. I could decide to use dashes in file names and underscores within files, but there would then be a problem with the text domain.

The text domain is set to acf-FIELD_NAME, which actually needs to be the same as the folder name. This excludes the use of a dash outside and an underscore inside files. Because of this I’ve decided to use underscores. There are simply fewer downsides to it.

Back to creating our plugin! Replace FIELD_NAME in all file names with country_selector. Once done, I recommend dropping the whole folder in a good editor such as Sublime14 or Atom15. Any editor that allows you to search and replace within multiple files at once will do.

Within our files there should be another set of placeholders: FIELD_NAME and FIELD_LABEL. There are a few more, but the others are used only once or twice and only in the readmes so you can replace those manually.

For this plugin I mass-replaced FIELD_LABEL with Country Selector and FIELD_NAME with country_selector.

Search and replace in a project in Atom16
Search and replace in a project in Atom. (View large version17)

Finally, open acf-country_selector.php and fill out the meta information in the header. This will ensure that the plugin shows up in the admin with the data you provide. In this plugin’s case I’ve filled it out like this:

/*
Plugin Name: Advanced Custom Fields: Country Selector
Plugin URI: http://danielpataki.com
Description: A plugin for ACF that allows you to select any country from a list
Version: 1.0.0
Author: Daniel Pataki
Author URI: http://danielpataki.com
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
*/

At this stage you can go to the plugins section and activate it. You won’t see any new fields just yet, but we can start chipping away at the code.

Our country selector plugin displayed in the admin18
Our country selector plugin displayed in the admin. (View large version19)

Step 3: Field Basics Link

You may have noticed that there are two similar files: acf-country_selector-v4.php and acf-country_selector-v5.php. One is for version 4 of ACF, the other for version 5. Right now, version 5 is actually the pro version, but soon the free version will also be updated to 5.

This doesn’t mean that the free version will have the premium fields, but it will run on a new and improved system. Since the free version is currently version 4, I will be looking at acf-country_selector-v4.php only. The methods for version 5 are very nearly the same, so it shouldn’t be too difficult to create both.

If you’re planning on releasing your plugin, I recommend using both files. Aside from some minor changes it’s really a matter of copying and pasting than anything else.

Our first stop, then, is acf-country_selector-v4.php, and the __construct() function within. Most of this is filled out for us. We’ll need to modify the value of $this->category. This determines which group our field type will be listed under. Since there’s a “Choice” group and we’ll be building a select field, let’s use Choice as the value here.

I also want to allow the person who creates the field to set an initial value for the country field. This will be really useful if the field is used on a website which predominantly uses a specific country for the field. Instead of having to go down and select “Hungary”, users could make that the initial value.

The $this->defaults array contains the defaults for our fields, like the initial_value field. I’ll make the default “United States”, which may be the most popular choice for the initial value. Here’s the full contents of the __contruct() function at the end of all that:

// vars
$this->name = 'country_selector';
$this->label = __( 'Country Selector' );
$this->category = __( 'Choice', 'acf' ); // Basic, Content, Choice, etc
$this->defaults = array(
	'initial_value' => 'United States'
);

// do not delete!
parent::__construct();

// settings
$this->settings = array(
	'path' => apply_filters('acf/helpers/get_path', __FILE__),
	'dir' => apply_filters('acf/helpers/get_dir', __FILE__),
	'version' => '1.0.0'
);

Step 4: Field Settings Link

The next step is to configure the field settings we’ll have. In our case this will be a single field for selecting the initial value of the front-end selector. This will be a selector with all countries selectable.

Before we create the selector, we need a list of countries. I created a little Gist20 which lists all of them in an array. I decided to create a method that returns all countries to make sure I can create country lists anywhere, without needing to add the large array more than once.

function get_countries() {
$countries = array( 'Afghanistan' => 'Afghanistan', 'Albania' => 'Albania', '...' );
return $countries;
}

I placed this function at the very end of the class, to separate it from the built-in ones. I can now use $this->get_countries within this class to return the array.

To add our initial value field we need to modify the contents of the create_options() method. Here’s the full code for that function:

function create_options( $field )
{
	$field = array_merge($this->defaults, $field);
	$key = $field['name'];


	// Create Field Options HTML
	?>
<tr class="field_option field_option_<?php echo $this->name; ?>">
<td class="label">
	<label><?php _e("Initial Value",'acf'); ?></label>
	<p class="description"><?php _e("The initial value of the country field",'acf'); ?></p>
</td>
<td>
	<?php

	do_action('acf/create_field', array(
		'type'		=>	'select',
		'name'		=>	'fields['.$key.'][initial_value]',
		'value'		=>	$field['initial_value'],
		'choices'	=>	$this->get_countries()
	));

	?>
</td>
</tr>
	<?php

}

The main thing to keep in mind is that the name of the field must be fields[field_key][field_name]. My particular case translates to fields[field_55e584fa90223][initial_value], but the key is generated for you, so you’ll need a variable to reference it. Hence the use of fields['.$key.'][initial_value].

If you need to add more field settings simply create some more defaults and add more fields following the pattern above. In version 5 the process is somewhat simpler as you don’t have to wrap the whole thing in a bunch of HTML – that is done for you.

Our custom setting for the country field21
Our custom setting for the country field. (View large version22)

Step 5: Field Front-End Link

All that’s left is to create the control the user will actually see. This is done in the create_field() method. You’ll need to create the HTML yourself here but it shouldn’t be too difficult to create a standard <select> field, right?

function create_field( $field )
{
	$field = array_merge($this->defaults, $field);
	?>
	<div>
		<select name='<?php echo $field['name'] ?>'>
			<?php
				foreach( $this->get_countries() as $country ) :
			?>
				<option <?php selected( $field['value'], $country ) ?> value='<?php echo $country ?>'><?php echo $country ?></option>
			<?php endforeach; ?>
		</select>
	</div>
	<?php
}

At this stage, your field is ready to go. If you add your field to a field group and view the field in action, you should see the country selector with the initial value selected. If you save the object you’ve added the field to, it will retain its value.

The completed country selector in action23
The completed country selector in action. (View large version24)

Step 6: Adding Scripts And Styles Link

This plugin doesn’t require any scripts and styles at the moment, but if we were to create a drop-down with the help of Chosen25, for example, we would need to leverage the input_admin_enqueue_scripts() function.

In many cases you don’t actually need to modify the code of this function. The enqueued script and style points to the already created files in the js and css folders – simply use those to add your code. If you enqueue third-party scripts like Chosen you should drop that in the js folder and enqueue it the same way.

Once downloaded, I placed the files required by Chosen in the correct directories. I put chosen.min.css in the css folder, chosen.jquery.min.js in the js directory, and the two images in the images directory. To make sure images are referenced correctly I replaced url( in the CSS file with url(../img.

With all the files in place, I used the input_admin_enqueue_scripts() to enqueue them, resulting in the following code.

function input_admin_enqueue_scripts() {

	// register ACF scripts
	wp_register_script( 'acf-input-country_selector', $this->settings['dir'] . 'js/input.js', array('acf-input'), $this->settings['version'] );

	// Chosen
	wp_register_script( 'chosen', $this->settings['dir'] . 'js/chosen.jquery.min.js', array('acf-input', 'jquery'), $this->settings['version'] );
	wp_register_style( 'chosen', $this->settings['dir'] . 'css/chosen.min.css', array('acf-input'), $this->settings['version'] );

	// scripts
	wp_enqueue_script(array(
		'acf-input-country_selector',
		'chosen',
	));

	// styles
	wp_enqueue_style(array(
		'chosen',
	));


}

Note that I removed the original CSS file that was added (input.css), but I kept the input.js file enqueued. We will need a little bit of JavaScript to apply Chosen, but we won’t need any additional CSS, apart from Chosen’s own.

The last step is to apply Chosen to our field, which is where input.js comes in handy. Opening up the file, you can see that it has a dedicated section for version 4 and for version 5. We’ll be using the version 4 section, adding the Chosen initiation code. It’s only one additional line which you need to add below initialize_field( $(this) );

$(this).find('select').chosen()

Once in place, you should be able to search within the select field’s values, making the UI that much better.

08-chosen-opt
Chosen applied to the select field. (View large version26)

Step 7: Modifying Values Link

There are a number of methods which serve to modify values after or before they are sent/received. These can be helpful for manipulating data between the user interface and the back-end logic. Here are the four most important methods, along with how they can be used:

  • load_value() is used to modify the value of the field after it is loaded from the database. This could be useful if you’re saving complex things like geolocation. The saved value may be an array containing the latitude and longitude, but you actually want to display a string.
  • update_value() modifies the value before it is saved in the database. If the user enters a comma-separated value of IDs, you may want to save that as an array. Using the update_value() function you can modify it easily.
  • load_field() modifies the whole field after it is returned from the database.
  • update_field() modifies the whole field before it is saved to the database.

Further Possibilities Link

I hope you can see that creating your own field is actually a pretty simple matter. If you want to add elaborate JavaScript to make things as user-friendly as possible, that’s all up to you – ACF supports it nicely. You can use a bunch of methods to play around with values and fields and much more. Browse through the template file for more information.

If that wasn’t enough, ACF also has powerful actions and filters27. Check out the documentation for more info.

If you’d like to check out a more complex field which contains JavaScript, styles and interaction with a third-party API, I invite you to check out the GitHub repository for my Google Font Selector Field28, which allows users to select fonts available from Google.

(ml, og)

Footnotes Link

  1. 1 http://www.advancedcustomfields.com/
  2. 2 https://www.smashingmagazine.com/wp-content/uploads/2015/09/01-map-and-radio-opt.png
  3. 3 https://www.smashingmagazine.com/wp-content/uploads/2015/09/01-map-and-radio-opt.png
  4. 4 https://www.smashingmagazine.com/wp-content/uploads/2015/09/02-image-field-settings-opt.png
  5. 5 https://www.smashingmagazine.com/wp-content/uploads/2015/09/02-image-field-settings-opt.png
  6. 6 https://www.smashingmagazine.com/wp-content/uploads/2015/09/03-field-location-rules-opt.png
  7. 7 https://www.smashingmagazine.com/wp-content/uploads/2015/09/03-field-location-rules-opt.png
  8. 8 https://profiles.wordpress.org/danielpataki#content-plugins
  9. 9 https://wordpress.org/plugins/search.php?type=term&q=advanced+custom+fields
  10. 10 http://www.advancedcustomfields.com/pro/
  11. 11 https://github.com/elliotcondon/acf-field-type-template
  12. 12 https://www.smashingmagazine.com/2015/07/development-to-deployment-workflow/
  13. 13 https://tommcfarlin.com/symbolic-links-with-wordpress/
  14. 14 http://www.sublimetext.com/
  15. 15 https://atom.io/
  16. 16 https://www.smashingmagazine.com/wp-content/uploads/2015/09/04-search-replace-opt.png
  17. 17 https://www.smashingmagazine.com/wp-content/uploads/2015/09/04-search-replace-opt.png
  18. 18 https://www.smashingmagazine.com/wp-content/uploads/2015/09/05-country-selector-plugin-opt.png
  19. 19 https://www.smashingmagazine.com/wp-content/uploads/2015/09/05-country-selector-plugin-opt.png
  20. 20 https://gist.github.com/danielpataki/2652bb42697b1a248761
  21. 21 https://www.smashingmagazine.com/wp-content/uploads/2015/09/06-country-settings-opt.png
  22. 22 https://www.smashingmagazine.com/wp-content/uploads/2015/09/06-country-settings-opt.png
  23. 23 https://www.smashingmagazine.com/wp-content/uploads/2015/09/07-country-field-opt.png
  24. 24 https://www.smashingmagazine.com/wp-content/uploads/2015/09/07-country-field-opt.png
  25. 25 'https://harvesthq.github.io/chosen/'
  26. 26 https://www.smashingmagazine.com/wp-content/uploads/2015/09/08-chosen-opt.png
  27. 27 'http://www.advancedcustomfields.com/resources/'
  28. 28 'https://github.com/danielpataki/ACF-Google-Font-Selector'
SmashingConf New York

Hold on, Tiger! Thank you for reading the article. Did you know that we also publish printed books and run friendly conferences – crafted for pros like you? Like SmashingConf Barcelona, on October 25–26, with smart design patterns and front-end techniques.

↑ Back to top Tweet itShare on Facebook

Hallo, my name is Daniel :) I build plugins, themes and apps - then proceed to write or talk about them. I'm the editor for the WordPress section here on Smashing Magazine and I contribute to various other online sites. When not coding or writing you'll find me playing board games or running with my dog. Drop me a line on Twitter or visit my personal website.

  1. 1

    Something that I like to do is include ACF in my theme via the functions.php file and then set the hide from menu to true. This ensures your client cant mess with the settings and break something. ;)

    3
    • 2

      Another great feature is local JSON. It makes ACF database independent and allows us to keep custom fields under version control.

      5
  2. 3

    Very informative when it comes to settings alter.

    1
  3. 4

    The best part of ACF is the price. While you mentioned you can use it on unlimited projects the part that makes me happier is that it doesnt expire* after the year as most of the premium plugins do for example Gravity Forms.

    *You stop receiving updates

    0
  4. 5

    Nice article. I use ACF on a daily basis, it’s such a powerful plugin. It is added to almost every WordPress install I do without even thinking, I always like to extend the back-end to make it as easy as possible for my clients to manage their content.

    0
  5. 6

    really very useful and easy.
    thanks for share this.

    -1
  6. 7

    That are very helpful and important information for me.Thank you so much for sharing article.

    2
  7. 8

    I think this is the next step to learn for me, thanks a lot for the article! ACF is by far the best and most useful plugin for wordpress, every wp developer should take a look at it.

    0
  8. 9

    Hemang Rindani

    November 18, 2015 3:48 pm

    Nice article.
    Advanced custom field in WordPress have virtually replaced the Native fields and make it easier for the developers to manage many complex content of a website. The field group in ACL provides opportunity to define custom fields, location rules and display options that could be managed easily through dashboard. It also supports multiple input types that helps a non-technical person to update the page information. Use custom field locator to reflect the changes into multiple pages at a time.
    Keep up the good work Daniel.

    1

↑ Back to top