WordPress Shop and restricting products and categories for some users/groups

Essentially, you need:

  • A global role-category relationship list, and/or…
  • A per-user category list

For per-user categories, hook onto the relevant profile actions:

  • show_user_profile & edit_user_profile for displaying the field (use wp_terms_checklist())
  • personal_options_update & edit_user_profile_update for saving the data

For the role-category data, use an options page. Loop over all roles ($wp_roles->roles) with a term checklist for each. Store the settings as an associative array:

Array
(
    [role_1] => Array
        (
            [0] => 4 // term ID
            [1] => 5
            [2] => 6
        )

    [role_2] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
        )

)

Now, once a user is logged in, you can grab their allowed categories like so:

if ( $user = wp_get_current_user() ) {
    $terms = array();

    if ( is_array( $role_terms = get_option( 'product_cat_roles' ) ) ) {
        foreach ( $user->roles as $role ) {
            if ( isset( $role_terms[ $role ] ) )
                $terms = array_merge( $terms, $role_terms[ $role ] ); 
        }   
    }

    if ( $user_terms = get_user_meta( $user->ID, '_product_cats', true ) )
        $terms = array_merge( $terms, $user_terms );

    $terms = array_unique( $terms );
}

Now for the fun part. We need to handle:

  • Product archives (all products)
  • Product category archive
  • Single product
  • Search

This is the core idea behind the “blocking”, but you’ll probably need to implement a more finely tuned solution. For example, blocked product category archive pages will load fine, just with an empty loop.

function wpse_148948_block_product_cats( $wp_query ) {
    if ( ! is_admin() && (
        $wp_query->is_tax( 'product_cat' ) ||
        $wp_query->is_singular( 'product' ) ||
        $wp_query->is_post_type_archive( 'product' ) ||
        $wp_query->is_search()
    ) ) {
        $user_terms = wpse_148948_get_user_product_cats();

        if ( ! is_array( $tax_query = $wp_query->get( 'tax_query' ) ) )
            $tax_query = array();

        $tax_query[] = array(
            'taxonomy' => 'product_category',
            'terms'    => $user_terms,
        );

        $wp_query->set( 'tax_query', $tax_query );
    }
}

add_action( 'pre_get_posts', 'wpse_148948_block_product_cats' );

You’ll also need to hide blocked categories from any front-end lists. If you’re using wp_list_categories, just apply the include argument. If you’re using widgets, you’ll have to find the appropriate hooks to filter out excluded product categories.