When in_same_term
parameter in get_next_post() / get_previous_post() function is set to TRUE, a post is selected from any category assigned to the current post.
As you wrote, you can not use the exclude
parameter in this case.
Using the get_{$adjacent}_post_where
filter, you can skip product
in the category list, which you are looking for posts. First you get IDs of the categories assigned to current post, next you delete unwanted IDs from the list, and finally you replace the relevant part of the “where” statement in the query string.
add_filter( 'get_next_post_where', 'se337397_adjacent_from_category', 15, 5 );
add_filter( 'get_previous_post_where', 'se337397_adjacent_from_category', 15, 5 );
function se337397_adjacent_from_category( $where, $in_same_term, $excluded_terms, $taxonomy, $post )
{
if ( $in_same_term == FALSE || 'post' != $post->post_type )
return $where;
//
// category IDs to ignore when choosing an adjacent post
$to_ignore = [ 5 ];
//
// or get ID by slug (term='product', taxonomy='category'):
// $term = get_term_by('slug', '{your_term_slug}', '{your_taxonomy}');
// if ( $term === false )
// return $where;
// $to_ignore = [ $term->term_id ];
// get terms assigned to current post
$term_array = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'ids' ) );
//
// Remove any exclusions and ignored from the term array to include.
$term_array = array_diff( $term_array, (array)$excluded_terms, $to_ignore );
$term_array = array_map( 'intval', $term_array );
if ( ! $term_array || is_wp_error( $term_array ) )
$term_array = [ 0 ];
//
// replace a part of the query string that limits results to given terms (categories)
$sql__in_terms=" AND tt.term_id IN (" . implode( ',', $term_array ) . ')';
$in_begin = strpos( $where, 'AND tt.term_id IN' );
$in_end = strpos( $where, ')', $in_begin );
$new_where = substr( $where, 0, $in_begin );
$new_where .= $sql__in_terms;
$new_where .= substr( $where, $in_end + 1 );
return $new_where;
}