Support

Account

Home Forums Backend Issues (wp-admin) Bidirectional term-to-term relationship

Solving

Bidirectional term-to-term relationship

  • Ive read about ACF Bidirectional Relationships (https://www.advancedcustomfields.com/resources/bidirectional-relationships/) but this is between posts (Relationship).

    What if I want, on a term edit page, to have a field allowing the selection of a term from a different Taxonomy, and to have that relationship be reciprocated in the other side?

    Not strictly a “Relationship”and I don’t believe WordPress even supports term-to-term relationships… (?).

  • +1 for this.

    I don’t know if you have checked out https://wordpress.org/plugins/acf-extended/ – it seems to add bidirectional relationships as a feature, but I’m not sure if this is between taxonomy terms or just between posts.

  • I don’t know if that plugin will do what you want or not, first I’ve heard of it.

    Ad far as doing this with code it would work in a similar way. The biggest difference would be that anywhere that you’re getting or setting a value, instead of $post_id you would use 'term_'.$post_id for the post you want to get or set the value from. The value you’re setting would be the same $post_id as this would be the term ID for a taxonomy field.

  • Thanks for the pointer here, John – I think I’m really close to getting this to work.

    I am using an ACF field named surname_linked to create a relationship between one term in the surname custom taxonomy and another.

    The code below successfully adds a two way relationship between the taxonomy terms, but fails to remove taxonomy terms from the linked term when the term is removed.

    Maybe you can spot where I’m going wrong?

    /* Bi-directional Surname taxonomy term updating - to keep Surname terms in sync */
    function gwsfhs_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, 'surname_'.$post_id2, false);
    			
    			// allow for selected posts to not contain a value
    			if( empty($value2) ) {
    				$value2 = array();
    			}
          
          $prefix = 'term_';
          $post_id = preg_replace('/^' . preg_quote($prefix, '/') . '/', '', $post_id);
    			
    			// 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, 'surname_'.$post_id2);
    			
    		}
    	
    	}
    	
    	// find posts which have been removed
    	$old_value = get_field($field_name, 'surname_'.$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, 'surname_'.$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, 'surname_'.$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=surname_linked', 'gwsfhs_bidirectional_acf_update_value', 10, 3);
  • I don’t know for sure, I think it may have something to do with this part of the code

    
    // find the position of $post_id within $value2 so we can remove it
    $pos = array_search($post_id, $value2);
    
    // remove
    unset( $value2[$pos] );
    

    Maybe the value you’re getting for $pos is somehow incorrect or somehow does not translate to the correct index, but I can’t say for sure.

    Generally, when I’m removing things from an array I build a new array rather than manipulate the existing array and it saves me a lot of headaches.

    
    $new_array = array();
    foreach ($old_array as $value) {
      if (/*condition to keep value*/) {
        $new_array[] = $value;
      }
    }
    $old_array = $new_array;
    
  • Much appreciated, John. I’ll take another look.

  • Looping back on this, I’m surprised to find I can natively set a term-to-term relationship across terms in two different taxonomies (person and company) using wp_set_object_terms()

    $term_taxonomy_ids = wp_set_object_terms( $bill_term_id, $microsoft_term_id, 'company' );

    They are just objects.

    And I can retrieve the company connected to a person with wp_get_object_terms like…

    $bills_company_terms = wp_get_object_terms($bill_term_id, 'company');

    However, putting a backend UI on that is another matter. Don’t know how to accomplish that. Any ideas?

  • @iamrobertandrews

    You would need to create an acf/update_value filter for the taxonomy fields. In this filter you would need to:
    1) Get the current value of the field before it will be updated
    2) Compare the old value with the new value to see what relationships are being added or removed and update the other end of the relationship accordingly.
    3) When updating the other end you will need to get the value of the other end, add or remove the new relationship and then update the other end.

  • Hi marcusjwilson, have you achieved a working solution for this please?

  • Sorry @martinko – Not had the time to implement all of John’s solution here. Would be interested if anyone else has come up with code for this.

Viewing 10 posts - 1 through 10 (of 10 total)

The topic ‘Bidirectional term-to-term relationship’ is closed to new replies.