Overview
This tutorial will cover the concept of bidirectional relationships using ACF to link a Custom Post Type and a Post in WordPress. We will then display additional information under a post (or a CPT) if it has a link to a CPT.
Currently bidirectional relationships are not created by default in the ACF plugin (v 4.4.11). However, until this happens ACF has provided a code snippet that you need to put in your functions.php here: https://www.advancedcustomfields.com/resources/bidirectional-relationships
Their example clearly shows how you can create bidirectional links between Posts. Let’s extend this to our example where we want to show Posts that have Videos. If a Post is associated with a Video (or any CPT of choice) we want the video’s excerpt and information from custom taxonomies like genres, artistes etc. to appear below our post.
In Part 1 we cover the basic setup of the custom post types, custom taxonomies, the required advanced custom fields, and necessary additions to functions.php. It is more or less a recap of the same example given on the ACF post above.
Step 1: Setup the Custom Post Types and Custom Taxonomies
Create a Custom Post Type called Videos. I am using a plugin called Custom Post Type UI to do this. Using the same plugin I’ve created Custom Taxonomies – Genres, Artistes and attached them to Videos. This is how the setup looks:
Step 2: Setup the required fields using ACF
Field Group for Video CPT
My setup has the following fields but the most important for this tutorial is the field named linked_objects with a field type of Relationship. The field group will show up only on the video CPT using the location rules. The relationship field has been setup in the same way as shown in the ACF example.
Field Group for Posts
Here we are creating an additional field group to show up only on Posts or another Custom Post Type like Events. In the ACF example the same field group is used on all posts, however, with custom post types this may not be feasible. Also, I am using the same relationship field linked_objects in this field group too.
Step 3: Add the code snippet from ACF to make bidirectional relationships work
As already mentioned above in ACF v.4.4.11 bidirectional relationship is not part of the plugin’s core functionality but may make it’s way in the future. Until then you will need to add the following code snippet. If you’ve followed the example on their website, and already done this you can skip this step:
Source: https://www.advancedcustomfields.com/resources/bidirectional-relationships/
function bidirectional_acf_update_value( $value, $post_id, $field ) { // vars $field_name = $field['name']; $field_key = $field['key']; $global_name = 'is_updating_' . $field_name; // bail early if this filter was triggered from the update_field() function called within the loop below // - this prevents an inifinte loop if( !empty($GLOBALS[ $global_name ]) ) return $value; // set global variable to avoid inifite loop // - could also remove_filter() then add_filter() again, but this is simpler $GLOBALS[ $global_name ] = 1; // loop over selected posts and add this $post_id if( is_array($value) ) { foreach( $value as $post_id2 ) { // load existing related posts $value2 = get_field($field_name, $post_id2, false); // allow for selected posts to not contain a value if( empty($value2) ) { $value2 = array(); } // bail early if the current $post_id is already found in selected post's $value2 if( in_array($post_id, $value2) ) continue; // append the current $post_id to the selected post's 'related_posts' value $value2[] = $post_id; // update the selected post's value (use field's key for performance) update_field($field_key, $value2, $post_id2); } } // find posts which have been removed $old_value = get_field($field_name, $post_id, false); if( is_array($old_value) ) { foreach( $old_value as $post_id2 ) { // bail early if this value has not been removed if( is_array($value) && in_array($post_id2, $value) ) continue; // load existing related posts $value2 = get_field($field_name, $post_id2, false); // bail early if no value if( empty($value2) ) continue; // find the position of $post_id within $value2 so we can remove it $pos = array_search($post_id, $value2); // remove unset( $value2[ $pos] ); // update the un-selected post's value (use field's key for performance) update_field($field_key, $value2, $post_id2); } } // reset global varibale to allow this filter to function as per normal $GLOBALS[ $global_name ] = 0; // return return $value; } add_filter('acf/update_value/name=related_posts', 'bidirectional_acf_update_value', 10, 3);
Step 4: Creating a link between Posts and our Custom Post Type
This part is simple. Assuming you’ve created some posts and/or events and videos or your CPT items, you will see the ACF fields and the linked_objects section. So go ahead and link your cpt, in our example a video to a post and an event. I have linked a Video to a Post as well as another Custom Type – Events (created by the fantastic Event Manager plugin).
And the corresponding video is now auto-linked to the post and event.
In Part 2, we will extract the meta associated with our videos such as the ACF fields and custom taxonomies to show up below posts and events that have videos linked to them.
Giacomo says
This is fantastic. On code change the name “related_posts” (last row) with your relationship name defined on ACF and ….GOOO.
If you have several relationship, you can add filters adding the last row with relevant relationship names.
This method is correct but for the new link only.
Is there the possibility to update all the CPT already linket to other CTP?
Karan NA Gupta says
I believe this is what you’re referring to: https://www.advancedcustomfields.com/resources/acf-update_value/
I’m not sure if it’ll really work … but for existing posts, may be run a one-time For loop, and then wp_update_post() to re-save existing posts for the CPTs?