A week ago, I was proverbially standing in a green field and curious how others might tackle the implementation of a core piece of the project I was starting. I had already spec’d out the meta data fields via Advanced Custom Fields; a plugin that not only makes typed field creation simple, but provides a consistent editing experience through both admin and front-end forms. What I wasn’t as clear on was how to filter options in a select field based on multiple other field selections.
So I put the question (at right) out to the WordPress ACF Users Facebook Group.
Filters like acf/fields/post_object/query are executed once on first form load. I’m wondering if anyone has existing gist / snippets to share on how they have solved filtering select fields dynamically as the user makes selections within the form. My initial thought is a mix of JS + Rest API (with so beautiful ACF to Rest API).
My use case involved 3 criteria against a multi-select field to determine which custom post types to show. Confused? A generic example of this functionality would be the search feature on used car sites where multiple select fields narrow down the entries to display. While I wouldn’t say that the implementation is particularly novel, I came away impressed by how powerful it is to combine ACF and the Rest API.
Step 2 – Setup the data
If I were building that used car example, I might setup the following:
- Taxonomy – Make
- Custom Post Type – Model
- ACF – Taxonomy Field associated to Make
- Custom Post Type – Vehicle
- ACF – Taxonomy Field associated to Make
- ACF – Post Object Field associated to Model
The idea being that each vehicle is defined by both make and model separately. Depending on which option(s) the user chooses, the list(s) are populated with appropriate selections. While the snippets included below are specific to a post type, the concepts can easily be re-purposed to filter against taxonomies or other data types you have associated through ACF field groups.
The individual files inside the Github Gist are explained in corresponding step numbers below.
Step 3 – PHP to enable parameters on the Rest API
The documentation on ACF to Rest API plugin gives a number of good examples on this, including this support thread that breaks out the two snippets of code you need to add.
- A) add_filter to rest_query_vars to tell the Rest API about params to be parsed out of querystring
- B) add_filter to rest_vehicle_query to modify the query for each param by post type
For a basic meta lookup that amounts to a scant ~10 lines of code which, as @shawnhooper put it is a, “net result [of] very little code and huge gains in terms of functionality.” At this point, you would be able to call the REST API for vehicle post type in your browser by any of the following:
Note: By default ACF stores taxonomy and post under their corresponding ID. If you wanted to have your Rest API parameters to be human-read-able, before passing the parameter to the meta_query you would first perform a get_posts / get_taxonomy loop to identify the ID. Having done so, your Rest API might then look like
Step 4 – JS to enable ACF to populate field using Rest API
The gist shows enqueuing the same JS to fire on both admin and front-end scenarios with different conditions. When active, it overrides the ajax arguments by field key (ID) to do the following:
- type – Change from POST to GET
- beforeSend – Add the nonce to secure the server-based JS request.
The Rest API guide on authentication is essential reading!
- url – Change to the new Rest API instead of default admin-ajax URL
- data – Use a custom function to populate API parameters based on select values retrieved at time of request. Setting field class or IDs in ACF makes this a LOT easier, but in some cases with repeaters and flexible content you may still need to do an “up, over and down” selector akin to $select.closest(“.indicator”).siblings(“.object”).find(“input”).val();
- results – Parse what the Rest API returns into suitable id/text format to populate select2 options
Step 5 – PHP to validate on the server
The above pieces are all in service of an efficient user interface with as much client-side logic as possible. Without it, you’d need to save the post with every field to get properly populated fields back from the server. But all that client-side logic does nothing to address unscrupulous users. So you will still want to use a filter like acf/validate_value/ to ensure that any submit entry matches actual data points. In my case, I was able to verify the count of user selected entries matched a count returned from get_posts loop with matching meta_query and post__in arguments.
Wash. Rinse. Repeat.
Pulling this all together the first time took me a little longer than I expected, but that’s usually par for the course when ramping up multiple new puzzle pieces and lacking a good night’s sleep. Re-executing steps 2 thru 5 with a different custom post type, meta fields and combination to filter / validate took less than an hour including testing.
Meagan suggested this might make a #perfectwordcamptalk although I’m slightly suspect. It could be the imposter syndrome talking me into thinking this was simply a case of finding the right documentation or terms to search for to find said documentation it than anything exceptional I hand-crafted. I hope some will find this blog post valuable even if I feel it’s more of a travel brochure for the stellar views that come from standing on the shoulders of giants.
What do you think? Would this make for a good wordcamp talk?