Advanced Custom Fields (ACF) is an indispensable time-saving tool for many WordPress developers. But, when data sets become large, ACF queries can take a long time if not done efficiently.
Here are some effective ways to massively speed up your ACF queries!
1. Use Local JSON (or Export to PHP)
Local JSON is a new feature in version 5 of ACF. It couldn’t be easier to use. All you have to do is create a folder in your theme called “acf-json” (with proper read and write permissions of course). Then, every time you save a field group, ACF will save your settings in JSON form there, and read the file instead of reading the database.
In the AIO Collective Blog, Eliza Witkowska did a test using ACF JSON in which the number of queries was reduced from 164 to 107!
If you want to go even further, you can export your ACF settings to PHP. In this example, the number of queries started out at 1197 using ACF, then went to 851 using local JSON, and finally down to 704 using an ACF-exported PHP file. But then of course, you’re no longer able to edit your settings in ACF.
Either way, you get a good reduction in queries without much effort! As an added benefit, Local JSON can make it easier to sync changes across dev and production sites.
2. Use get_post_meta() instead of get_field()
UPDATE: I’ve read online that this problem might have been fixed, so that get_field() is no longer so slow. I haven’t seen test data to prove it yet though. Please comment if you know of any.
Depending on what kind of queries you are doing, using WordPress functions like get_post_meta() to display information instead of ACF functions like get_field() can also give you a big performance boost.
In his article, Craig Simpson shows an example where using ACF’s helper functions caused the page render time to increase by 173% and increased the number of queries by 14%, vs. using WordPress native functions.
The reason why native is faster (in this case) is that when the WP Query is run, it caches all of the metadata, so you can access to it using WordPress native functions without any database calls.
A side benefit of doing this is that the front-end of the site will still work even if someone deactivates the Advanced Custom Fields plugin.
3. Create Bi-Directional Relationships Using ACF Post-2-Post
Let’s say you have thousands of books and authors. ACF is set up so that the Book custom post type is related to the Author custom post type through an ACF Relationship or Post Object. When you query a Book, it’s quick and easy to get the Author.
But, let’s say you want an Author page to show all of the books by that author. You have to do a meta query that internally involves looping through all Books, and then doing a LIKE search of the serialized metadata for those books, to find ones that match the author. That can take a long time.
While searching for a solution, I discovered the amazingly-useful ACF Post-Post plugin which automatically populates a relationship field (or Post Object field) in the target post back to the original post. In our example, it means that when an Author was assigned to a Book, the plugin would also assign the Book to the Author. So, querying which Books an Author has written will be one ridiculously fast query compared to the slow meta query we’d need otherwise.
As an added bonus, you get to see which Books an Author has written right in the WordPress edit page for that Author.
Note that the reciprocal relationship is saved when the original relationship is saved. So, if you have an existing site, you’ll have to go through and re-save (and possibly re-pick) all of the relationships that you want to be “bidirectional”. So, it’s best to use this solution at the start of your project, before entering a lot of data.
Having said that, I could see a way to cut this solution in gradually. New posts (created after the plugin was installed) would have the new bi-directional relationships. The target template (say, the Author page) would have a check to see if the bidirectional field was populated. If not, it would fall back on using the old slow meta query.
4. Avoid Repeaters
In his article, John Huebner argues that Repeaters are terrible for query performance and that they’re overused. If possible, consider using a custom post type or taxonomy instead.
5. Pre-load Objects in Repeaters
If you must use Repeaters, use this trick to pre-load the data. This technique is demonstrated in Eliza Witkowska’s article and one by Indrek Kõnnussaar. Both talk about using get_posts() on post type “attachment” to pre-load images before a repeater loop, so that there’s just one database query at the beginning instead of one per loop iteration. This can massively reduce queries if you are looping through a large repeater!
6. Return IDs Instead of Objects
I couldn’t find any hard data to prove that returning IDs instead of Objects is faster in general. But, some commenters online affirm it is quicker to get the ID, then do WordPress queries to get the information for that post, rather than to get the entire Image or Post Object. I’ll leave this as a “maybe”, and an exercise for the reader.
If anyone has a big database, try it both ways and let me know about this one!
I hope you found these ideas helpful! Let me know what kind of performance gains you got! Do you have any other tips? Please let me know in the comments below! – Brian