Advanced Custom Fields
FacetWP includes built-in support for Advanced Custom Fields (ACF). It supports basic field types (Text, Select, Checkbox, etc.), Relationship fields and even Repeater sub-fields.
Create your field group
The first step is to create an ACF field group. In the screenshot below, we’re adding a new “Event Fields” group, and attaching it to the Event post type.
Use your field group
After you’ve added your field group, the next step would be to add some content. Edit some of your posts to make sure that your fields are appearing correctly.
Create your facet
After you’ve added some content using your new custom fields, go to Settings > FacetWP, click the Facets tab, then click the Add New button.
For the facet’s Data source, scroll down until you see the ACF heading, then select the appropriate choice.
Alternatively, use the built-in search box for quickly finding the desired field.
Using Repeater fields
Facets can be added using data from repeater sub-fields.
Repeater values are tied back to the parent post. This means that repeater rows aren’t directly filterable.
For example, let’s say you have a “Speakers” facet based on a repeater field:
If the user selects “Jim”, the other choices (Bob, Dan) will remain too. This is because the 3 choices are tied to the parent post.
Using Advanced Custom Fields with a Map facet or Proximity facet
If you are using Advanced Custom Fields with a Map facet or Proximity facet, you can use ACF’s Google Map field type to attach a location to your posts. If you use this ACF field, in the Map / Proximity facet’s “Data source” settings choose this field underneath the “ACF” header.
If your locations don’t show up on the map when using an ACF Google Maps field, make sure the field is directly in the main Field Group, or at most one level deep in a nested Field Group. Google Maps fields in Field Groups that are nested deeper than one level, will not work.
Using multiple data sources / locations per post
It is possible to index multiple locations per post, by using a multi-value custom field, like an ACF repeater field, or for example a checkboxes/dropdown field (if you have a select number of locations to choose from).
If you use an ACF Repeater field with a Google Maps sub field, you need to choose the Google Maps sub field's name from the Map facet's Data Source dropdown (my_map
in the example to the right).
Make sure to choose the field under the "ACF" heading in the Data Source dropdown.
Using an ACF Checkbox field as meta_query filter
If you are using an ACF Checkbox field to filter your (custom) query by, you will notice that this does not work out of the box. This is because in order to handle multiple values, ACF stores Checkbox values as a serialized string in the database. Serialized values are difficult to do anything with.
In a Listing Builder listing you would probably try to get it working with a query filter, like this:
The above will not work with ACF Checkbox fields.
To be able to use a Checkbox field to filter the query, first click the red "Convert to query args" button at the bottom, which will convert the query settings to PHP query arguments. With the above example settings, the arguments will look as follows. Note that this will still not work:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
<?php /* THIS DOES NOT WORK, DON'T USE! */ return [ "post_type" => [ "product" ], "post_status" => [ "publish" ], "meta_query" => [ [ "key" => "my_acf_checkbox_field", "compare" => "IN", "type" => "CHAR", "value" => [ "my_value" ] ] ], "orderby" => [ "title" => "DESC" ], "posts_per_page" => 10 ];
To make these arguments work with a serialized Checkbox field value, you need to make three manual changes:
- Change the
compare
value fromIN
toLIKE
. - Remove the array brackets surrounding the value.
- Wrap the double-quoted value with single quotes.
The result should look like this:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
<?php return [ "post_type" => [ "product" ], "post_status" => [ "publish" ], "meta_query" => [ [ "key" => "my_acf_checkbox_field", "compare" => "LIKE", // Changed to "LIKE" "type" => "CHAR", "value" => '"my_value"' // Removed the array brackets. And wrapped the double quotes with single quotes. ] ], "orderby" => [ "title" => "DESC" ], "posts_per_page" => 10 ];
If you need to filter by multiple Checkbox values, you cannot add them as an array. Each value will need its own meta_query
argument. You'll also need to add a relation
argument:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
<?php return [ "post_type" => [ "product" ], "post_status" => [ "publish" ], "meta_query" => [ 'relation' => 'OR', // Needed for multiple values [ "key" => "my_acf_checkbox_field", "compare" => "LIKE", "type" => "CHAR", "value" => '"my_value"' ], [ "key" => "my_acf_checkbox_field", "compare" => "LIKE", "type" => "CHAR", "value" => '"my_other_value"' ] ], "orderby" => [ "title" => "DESC" ], "posts_per_page" => 10 ];
If you have too many values to do this, it would be better to use a custom taxonomy instead of a Checkbox field.
Using a Date Picker or Date Time Picker field
FacetWP supports the ACF Date Picker and Date Time Picker field types. It will automatically process the way these fields store dates in the wp_postmeta
database table (as YYYYMMDD
for Date Picker, and YYYY-MM-DD HH:MM:SS
for Date Time Picker). These fields' "Display Format" and "Return Format" settings have no influence on this.
Facets that use a Date Picker field as their data source will by default display their date options in a YYYY-MM-DD
format. To reformat this to January 17, 2024
for example, add the following snippet to your (child) theme's functions.php. Make sure to re-index after adding these snippets:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'facetwp_index_row', function( $params, $class ) { if ( 'my_date_facet' == $params['facet_name'] ) { // Replace 'my_date_facet' with the name of your facet $raw_value = $params['facet_value']; $params['facet_display_value'] = date( 'F j, Y', strtotime( $raw_value ) ); // Use "January 17, 2024" format for the facet's display value. For other date formats, see: https://www.php.net/manual/en/function.date.php } return $params; }, 10, 2 );
Facets using a Date Time Picker field will automatically display their date options in a YYYY-MM-DD HH:MM:SS
format. To reformat this to January 17, 2024 at 17:15:05 PM
for example, you can use the following snippet:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'facetwp_index_row', function( $params, $class ) { if ( 'my_date_facet' == $params['facet_name'] ) { // Replace 'my_date_facet' with the name of your facet $raw_value = $params['facet_value']; $params['facet_display_value'] = date( 'F j, Y \a\t H:i:s A', strtotime( $raw_value ) ); // Use "January 17, 2024 at 17:15:05 PM" format for the facet's display value. For other date formats, see: https://www.php.net/manual/en/function.date.php } return $params; }, 10, 2 );
To use any other date format, see the examples on the PHP.net date() documentation page.
Note that the Date Range facet has a Display format setting to format its date display.
Using a Color Picker field
If you use an ACF Color Picker field as a facet's data source, you may want the use the color value itself to style the facet choice label. You could for example set a CSS text color
or background-color
to the label, or create a color swatch element before or after the label text.
You can do this with the facetwp_facet_display_value hook. Add the following snippet to your (child) theme's functions.php to give the facet choice label a CSS background-color
based on the display value of the label.
Note that the snippet uses facet_display_value
for the color value and not facet_value
, because the facet_value
(the technical value as shown in the URL) will be hashed to a random string of numbers (because of unsafe characters in the URL).
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'facetwp_facet_display_value', function( $label, $params ) { if ( 'my_color_facet' == $params['facet']['name'] ) { // Replace 'my_color_facet' with the name of your color-picker-based facet. $val = $params['row']['facet_display_value']; // Use the display value here to get the unhashed hex or rgb(a) color value. $label = '<span style="background-color:' . $val . ';">' . $label . '</span>'; // Add a span to the facet choice with the color as CSS background color. } return $label; }, 20, 2 );
The above code works both with the "Enable Transparency" setting disabled (outputting a hex string) and enabled (outputting an RGBA string).
If you have the "Enable Transparency" setting disabled (so the field is outputting a hex string) and you want to prevent hashing/sanitizing of color values in the URL, you can remove the hex color's #
character (which causes the hashing) with WP's sanitize_hex_color_no_hash()
function from the facet_value
with the facetwp_index_row hook, while indexing. The following snippet shows how to do that. Make sure to re-index after adding this snippet and making changes to it.
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'facetwp_index_row', function( $params, $class ) { if ( 'my_color_facet' == $params['facet']['name'] ) { // Replace 'my_color_facet' with the name of your color-picker-based facet. // Get the color value (with the '#') $colorvalue = $params['facet_value']; // Remove the '#' from the hex color value to prevent hashed/sanitized color values in the URL $params['facet_value'] = sanitize_hex_color_no_hash( $colorvalue ); } return $params; }, 10, 2 );
Use a color taxonomy with a Color Picker term field
If you want readable color names (e.g. "green") in your facet choice labels, instead of the actual hex color values, you could create a custom "color" taxonomy (containing color names as terms), and set your facet's data source to use this taxonomy. Then, set up an ACF Color Picker field as term meta field, by setting the field's Field Group's location to this "color" taxonomy.
This will make the facet choices display color names, but you cannot yet use the color values from the Color Picker field yet to style them. To set this up, add the two snippets below to your (child) theme's functions.php.
The first part indexes the hex color value of the term meta field into the facet choice's facet_value
(the technical value as shown in the URL). While doing so, in line 11 we remove the #
character from the color value, with WP's sanitize_hex_color_no_hash()
function, to prevent the color value from being hashed/sanitized. After re-indexing, the color value will now show as a 6-digit color code without the #
(e.g. 000000
for black), in the URL when the facet choice (e.g. "black") is selected.
Next, we use the facetwp_facet_display_value hook to set a CSS background-color
. For this to work, we first get the color value from facet_value
value. Then we add the #
character back in, on line 20 to get a valid hex color value. The result is a facet with color names as choice labels, styled with a CSS background-color
set on each of the choice labels:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'facetwp_index_row', function( $params, $class ) { if ( 'my_color_facet' == $params['facet']['name'] ) { // Replace 'my_color_facet' with the name of your color-picker-based facet. // Get the taxonomy term $term_id = (int) $params['term_id']; // Get the color value from an ACF Color Picker term meta field $colorvalue = get_term_meta( $term_id, 'term_color_picker_field', true ); // Replace 'term_color_picker_field' with the name of the Color Picker term field // Remove the '#' from hex color value to prevent hashed/sanitized color values in the URL $params['facet_value'] = sanitize_hex_color_no_hash( $colorvalue ); } return $params; }, 10, 2 ); add_filter( 'facetwp_facet_display_value', function( $label, $params ) { if ( 'my_color_facet' == $params['facet']['name'] ) { // Replace 'my_color_facet' with the name of your color-picker-based facet. $val = $params['row']['facet_value']; // Use the facet_value here to get the unhashed hex color value, without the '#' $label = '<span style="background-color: #' . $val . ';">' . $label . '</span>'; // Add a span to the facet choice with the color as CSS background color. Needs the '#' before the color value. } return $label; }, 20, 2 );
Note that this setup only works when the "Enable Transparency" setting is disabled (so the field is outputting a hex color string). If you need this to work with RGBA color strings, you'll have to destructure the RGBA string into four values, and store them as a string (with e.g. a dash as separator) in the first snippet. Then restructure these four values into a valid RGBA string again in the second snippet.
Using a File or Image field
If you use an ACF Image field in a Listing Builder listing item, make sure the "Return Value" of the field is set to "Image Array" if your goal is to display the actual image.
If you use an ACF File or Image field as a facet's data source, the indexed and displayed facet value will be the file ID
or image ID
, no matter what "Return Value/Format" type you choose in the field's settings.
To create a facet that filters by any file/image feature other than its ID
, you'll have to use the facetwp_index_row hook to first use the ID
to get that feature, then index it for the facet choice's facet_value
(the technical value as shown in the URL when selected) and the facet_display_value
(the choice label as shown in the facet). The second example below shows how to do this.
When using an ACF Image field, you can also use the indexed image ID
to get the image URL, and create a facet showing the actual images:
Create an image facet
If you want to create an image facet, with facet choices showing the images selected with the ACF image field, add the following snippet to your (child) theme's functions.php. Make sure to replace my_image_facet
in line 4 with the name of your image facet. A good facet type for this would be a Checkboxes or Radio facet. When you use an fSelect facet, read the yellow banner below the code.
The code uses the facetwp_facet_display_value hook to transform the facet choice labels from the indexed image ID to an HTML <img>
tag. To do this, we first get the image ID from the indexed facet_value
. Then we use the wp_get_attachment_image_src() function to get the image URL from the image ID. This function takes the desired WordPress image size as the second argument. In this example, we set the image size to thumbnail
, in line 10. You can choose any existing WP image size here.
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'facetwp_facet_display_value', function( $label, $params ) { // Repace "my_image_facet" with the name of your image field based facet if ( 'my_image_facet' == $params['facet']['name'] ) { // Get the image ID from the indexed facet value $image_id = $params['row']['facet_value']; // Specify the desired WP image size. Replace "thumbnail" with the WP image size you want $image_size = 'thumbnail'; // Get the image URL in the specified size $image_url = wp_get_attachment_image_src( $image_id, $image_size ); if ( $image_url ) { $image_url = $image_url[0]; } // Get the alt text of the image, as set in the Media library $alt_text = get_post_meta( $image_id, '_wp_attachment_image_alt', true ); // Output the facet label as an image in the specified size // Note: for fSelect facets, use esc_html() around the label output, otherwise the HTML will be stripped out, causing the output to be empty $label = '<img src="' . $image_url . '" alt="' . $alt_text . '" />'; } return $label; }, 20, 2 );
Note that WP image sizes can be set in Settings > Media. If these default four WordPress image sizes (full
, large
, medium
, thumbnail
) are not enough, or you need more control, like fine-tuning the way images are cropped to the defined width and height, you can use WordPress' built-in add_image_size() function. Some themes also offer settings to add additional custom image sizes. There are also plugins to create and/or regenerate image sizes.
Create a file/image extension facet
The following snippet shows how to use the facetwp_index_row hook to create a facet displaying file or image extensions, when using an ACF File or Image field.
This example works for two facets, named my_file_facet
and my_image_facet
. The code uses the file/image ID
to get the file/image URL, and then extracts the file extension from the URL (e.g. jpg
, png
, pdf
). This extension is then indexed for both the facet_value
and the facet_display_value
.
The code needs to be added to your (child) theme's functions.php. Make sure to re-index after adding or changing the code:
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
add_filter( 'facetwp_index_row', function( $params, $class ) { // Add or remove file/image facets to/from the array. Replace 'my_file_facet' and 'my_image_facet' with the name(s) of your ACF file/image facet(s). $facets = [ 'my_file_facet', 'my_image_facet' ]; if ( in_array( $params['facet_name'], $facets ) ) { $id = $params['facet_value']; // Get the ID $url = wp_get_attachment_url( $id ); // Get the URL $extension = pathinfo( $url, PATHINFO_EXTENSION ); // Get the extension from the URL $params['facet_value'] = $extension; $params['facet_display_value'] = $extension; } return $params; }, 10, 2 );
Creating custom post types with ACF
As of ACF 6.1, it is possible to register custom post types and custom taxonomies with it.
If you are registering a custom post type this way, make sure you disable the "Exclude From Search" setting located under Post Types > Edit Post Type > Advanced Settings > Visibility:
This is needed because FacetWP only indexes searchable post types. If your post type is not searchable, you will get empty facets or a "The index table is empty" error.
If you enabled this setting because you don't want this post type in your search results, it is also possible to force FacetWP to index non-searchable post types, with the facetwp_indexer_query_args hook.
Using taxonomy term custom fields as facet data source
With Advanced Custom Fields you can add custom fields to taxonomy terms. (The same is true for the Pods plugin, the instructions mentioned here are the same.)
When you set such a custom term field as the data source in a facet, you will notice that the facet does not display any choices. This is because custom fields attached to taxonomy terms cannot be indexed directly by FacetWP.
However, with a bit of custom code, using a custom term field as data source is possible.
First, create the facet and set its data source to the taxonomy itself. Then, add the following code to your (child) theme's function.php. Make sure to re-index afterwards.
How to use custom PHP code?
PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info
/** * This code works for term fields created in ACF and Pods. * 1. Replace "your_facet_name" with the actual facet name * 2. Replace "your_custom_field" with the actual custom field name * 3. Re-index afterwards */ add_filter( 'facetwp_index_row', function( $params, $class ) { if ( 'your_facet_name' == $params['facet_name'] ) { // Get the taxonomy term $term_id = (int) $params['term_id']; // Get the custom field value from the term $value = get_term_meta( $term_id, 'your_custom_field', true ); // Set the facet label and URL slug to the custom term field value $params['facet_display_value'] = $value; // the facet choice label // Optional, see explanation below. $params['facet_value'] = $value; // the facet URL slug, used for indexing/filtering } return $params; }, 10, 2 );
What this code does, is force FacetWP into indexing the custom term field value instead of the term name and the term slug. When using this, keep the following in mind:
Normally, when FacetWP indexes a taxonomy term, the following happens:
- Term names are stored in FacetWP’s index table as
facet_display_value
and used to generate the display of the facet’s choices. - Term slugs are stored in FacetWP’s index table as
facet_value
and are used for filtering, which also means they will show up as facet slug in the URL after interacting with the facet.
The above code sets both the facet_display_value
and the facet_value
to the custom field's value while indexing.
Setting facet_value
to the custom field value is optional. There are a few scenarios in which you may want to keep using the term slug as facet_value
:
- If your custom term field value has special characters or spaces. These will be replaced when the value is indexed, or - depending on the characters - the value will get entirely hashed into a string of random characters. If you want to keep using the term slug in the URL, do not set
facet_value
to the custom field value. - If multiple terms share the same value for the custom field, they will be indexed together as one facet choice. If you want to keep them indexed as separate choices, do not set
facet_value
to the custom field value.
Using ACF Block fields as facet data source
If you are using ACF Pro, you can use ACF Blocks to create custom WordPress blocks and add them to your pages.
ACF blocks can have custom fields in them. To create these, first create a Field Group and add your fields. Then, in the Field Group's Settings > Location Rules, let the Field Group show in one or more of the custom ACF blocks you created:
These custom fields will now appear in your block's settings, on pages/templates where you add this block, as shown in the image on the right.
By default, WordPress stores a block’s field data inside the block’s HTML comment in post_content
, which is stored in the posts
database table. However, FacetWP cannot use custom field data in this table to index facets. It can only index data stored in the postmeta
table. So, if you create a facet and set its Data Source setting to a custom field that is connected to an ACF block, you'll see no rows indexed for this facet (in the facet overview screen), and the facet will have no choices.
To solve this, you can force ACF to store its block field data in the post-meta database table instead. This can be done by adding a "usePostMeta": "true"
argument to the block's block.json
file. Below is an example, with the extra argument added in line 17:
{ "name": "acf/testimonial", "title": "Testimonial", "description": "A custom testimonial block that uses ACF fields.", "style": [ "file:./testimonial.css" ], "category": "formatting", "icon": "admin-comments", "keywords": [ "testimonial", "quote" ], "acf": { "mode": "preview", "renderTemplate": "testimonial.php", "usePostMeta": "true" }, "supports": { "anchor": true } }
After adding this argument, make sure to save the posts that use this ACF block, and/or do a full re-index.