Skip to content

Supports ACF

Alex Prokopenko edited this page Sep 18, 2018 · 3 revisions
- Article is under construction

Advanced Custom Fields is a famous plugin to create custom fields for Pages, Posts, Custom Post Types, Taxonomy terms, Users and Option pages.

This plugin is really cool, however moving it still has few issues to resolve.

  • Moving fields configuration between environments
  • Fields UI

Fields configuration

ACF stores its configuration inside wp_posts/wp_postmeta/wp_options tables in database. Collecting all these settings together from the database takes time. And you need to export/import your configuration when you have several environments (like develop/production). ACF has an option to import settings as a PHP file, however it's too hard to edit and support such code.

We believe that fields configuration have to be stored under VCS, this makes all processes easier. We found a good package which can help you with defining fields inside the code: https://github.com/StoutLogic/acf-builder/wiki

It's easy to use, however still not good enough :) To stay DRY (Don't Repeat Yourself), you need to break duplicated definitions in separate files, then include them, you need to move init hook somewhere as well. That's why we created few classes, which do all dirty job for you.

Inside our framework we have 2 main classes to work with ACF fields:

  • ACF_Definition is a base class for repeatable fields definitions like Flexible Content Layouts, generic fields (for example Color or Background settings).
  • ACF_Register is a base class for registering all fields together and match them with a content types, user form or custom options page.

Both classes has 2 similar methods has() and build().
has() method is used to add fields definitions to an internal property (some type of cache).
build() method is a wrapper for new FieldsBuilder($name, $config) statement.

Definitions

To create a re-usable field definition you need to create a child class and write init() method with combination of has() and build() calls.

For example let's create a module to print a Hero banner fields

{theme}/app/Fields/Modules/Module_Hero.php

<?php

namespace Boilerplate\Theme\Fields\Modules;

use JustCoded\WP\Framework\ACF\ACF_Definition;

class Module_Hero extends ACF_Definition {
	/**
	 * Init fields configuration method
	 */
	public function init() {
		$this->has(
			$this->build( 'module_hero' )
				->addText( 'hero_title' )
				->addImage( 'hero_image' )
				->addWysiwyg( 'hero_text' )
		);
	}
}

And one more example of common fields, which can be used inside different modules:

<?php

namespace Boilerplate\Theme\Fields\Modules;

use JustCoded\WP\Framework\ACF\ACF_Definition;

class Fields_Generic extends ACF_Definition {
	/**
	 * Init fields configuration method
	 */
	public function init() {
		$this->has(
			$this->build( 'subtitle' )
				->addText( 'subtitle', ['label' => 'Subtitle'] )
				->setRequired(true)
				->setInstructions('Smaller title, usually printed under primary title or name.'),

			$this->build( 'color' )
				->addSelect( 'color' )
				->addChoice( 'red' )
				->addChoice( 'blue' )
				->addChoice( 'green' )
				->setDefaultValue( 'blue' )
		);
	}
}

Note: Each build() call have to use a unique name. Using this name you will be able to get this field group later._

As you can see if we use standard ACF admin UI or manual functions to create configs - we will need to repeat such general configurations and will duplicate them a lot of times.

Now to get a field group object you need to call a static method get() like this:

  • Module_Hero::get() will return a full group.
  • Fields_Generic::get('subtitle') will return a single 'subtitle' field definition.

Field groups objects can be used inside other builders to add fields with addFields() method. (If you have an ACF PRO version and use Flexible Content, then you can also use them inside addLayout() method).

Registration

With help of another base class we can register them. Actually a base class just has acf/init hook registratino and a call of a standard acf_add_local_field_group() function.

It works absolutely the same as Definition class - you need to write an init() method.

The only difference - you also need to specify a location property for your fields.

For example let's define extra fields for a Page:

<?php

namespace Boilerplate\Theme\Fields;

use Boilerplate\Theme\Fields\Modules\Fields_Generic;
use Boilerplate\Theme\Fields\Modules\Module_Hero;
use JustCoded\WP\Framework\ACF\ACF_Register;

class Page_Fields extends ACF_Register {

	/**
	 * Init fields configuration method
	 */
	public function init() {
		$this->has(
			$this->build()
				->addFields( Fields_Generic::get( 'subtitle' ) )
				->addFields( Module_Hero::get() )
				->setLocation( 'post_type', '==', 'page' )
		);
	}
}

One more extra feature of a Register class is a method to quickly disable a standard content editor, which is a popular operation, when you use ACF and Flexible Content field. To do this you need to call a special method $this->remove_content_editor() with a post type name as a parameter, like this:

	public function init() {
		$this->remove_content_editor( Employee::$ID ); 
		
		$this->has(
		    // ...

Theme Options page example

If you want to define an Options page - you can use add_options_page() method to define it (it's actually an ACF wrapper, which will check for ACF installed).

And after that you define your fields in similar way as before.

<?php

namespace Boilerplate\Theme\Fields;

use JustCoded\WP\Framework\ACF\ACF_Register;

class Theme_Fields extends ACF_Register {

	/**
	 * Init fields configuration method
	 */
	public function init() {
		$this->add_options_page( 'Theme options' );

		$this->has(
			$this->build()
				->addTab( 'Socials Links' )
				->addFields( $this->socials_tab() )
				->addTab( '404 Page' )
				->addFields( $this->page_404_tab() )
				->setLocation( 'options_page', '==', 'acf-options-theme-options' )
		);
	}

	protected function socials_tab() {
		return $this->build( 'socials_options' )
			->addText( 'social_fb', [ 'label' => 'Facebook Page' ] )
			->setDefaultValue( 'http://facebook.com/my-page' )
			->addText( 'social_twitter', [ 'label' => 'Twitter account' ] )
			->setDefaultValue( 'http://twitter.com/@some-username' )
			->addText( 'social_gplus', [ 'label' => 'Google+' ] )
			->setDefaultValue( 'https://plus.google.com/-unique-profile-id-' )
			->getRootContext();
	}

	protected function page_404_tab() {
		return $this->build( '404_options' )
			->addText( 'page_404_title', [ 'label' => '404 Page Title' ] )
			->addWysiwyg( 'page_404_content', [ 'label' => '404 Page Content' ] )
			->getRootContext();
	}
}

User Interface improvements

If you used ACF for building Landing Pages (with field groups or Flexible Content) then you dealed with a problem that you get a super long-scroll edit form, which is too hard to navigate.

And finally if you declare your fields within a code - you don't need admin UI, which is enabled by default.

Another issue - that each field is placed on one row and in case you have a lot of options - it takes too much space.

To resolve all these issues we also created several helpers.

Fields grid

We created a simple grid, which you can use to place fields in a same row. And comparing with standard ACF width property - it's fully responsive and you can manage your page from a mobile for example.

To set field width you need to use method setWidth() for any field inside your builder configuration. You can use values 25%, 50%, 75%, 33%, 66%. To create a 2, 3 and 4 columns grids.

Usage example:

<?php
class Module_Colors extends ACF_Definition {
	/**
	 * Init fields configuration method
	 */
	public function init() {
		$this->has(
			$this->build()
				->addGroup('Colors')
					->addColorPicker( 'text_color' )
						->setWidth('33%')
					->addColorPicker( 'background_color' )
						->setWidth('33%')
					->addColorPicker( 'hover_color' )
						->setWidth('33%')
		);
	}

Custom styles

To make our grid system works we prepared some pack of styles for admin panel. You need to create an instance of ACF_Support class.

Collapsed sections

Also ACF_Support class adds a behavior to collapse your field groups by default. So you can see all of them on a page when you edit. This will help you to find required section faster.

Add this one to your Theme init() method:

    if ( ACF_Support::check_requirements() ) {
        ACF_Support::instance();
    }

Note: Don't forget to define full class name in "use" section under namespace declaration.