Support

Account

Home Forums General Issues How can I exclude extra relationship posts from Woocommerce archive loop?

Solved

How can I exclude extra relationship posts from Woocommerce archive loop?

  • I’m working on a Woocommerce site that is integrated with a Store system. This system does not support regular Woocommerce variations so we used an ACF relationship field to create a custom variation selection on the frontend product page.

    The client has requested that if products are variations only 1 of those should be displayed in the archive/category/search loop, so we need to exclude these extra products from the loop, but keep 1 of them.

    What is the best way for me to do this?

    My initial thought is looping through all products, create an array of product_ids that has values in the relationship field and 1 for the relationship values. array_diff those to only be left with extra varaitions. Then adding that array to post__not_in in pre_get_posts action.

    Am I on the right track, I’m worried this might be too slow and wondering if there’s another speedy/optimal way to do this?

  • It seems like that method would be slow. But I’d need more information to know if there is a better solution.

    Where is the relationship set and how do they work? The main product? Each of the “variations”? Both? Are you using a relationship field or a post object field?

  • The relationships are set for each product. They are all “Simple” products. I’m using a ACF Relationship field to create custom variations.

    Screenshot of the Relationship field

    The Relationship field is also set up to be bidirectional:

    // Creating Bidirectional relationship
    // for products to create variations
    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=product_variations', 'bidirectional_acf_update_value', 10, 3);
  • What you have planned is a solution, but it could impact the performance of your site significantly if you have a lot of products to deal with and could eventually cause the page to time out.

    The easiest fix for this is to add another ACF field, I would use a true/false that I would use to either include of exclude the product from archives. The client would need to set this field when they are adding the relationships. With the addition of this field you could then add a simple meta_query to the main WP query for WC to select the products that get shown. This would mean a little extra work for the client.

    Another more complicated option would be to store your “post__not_in” array in a transient so that the value does not need to be regenerated on every page load.

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

You must be logged in to reply to this topic.