I have this webshop that I've built in WordPress, no WooCommerce, no Wp-Commerce or anything like that, but from scratch.

The users visiting the site will have to be logged in to view products and categories otherwise they will be redirected to the login page.

Now here's my question, I have created a bunch of test users and user groups/roles. I have all these categories with the products inside, some products share the same category. Now I want to restrict some categories for certain users/groups. That being said I still want the user to be able to view the product IF the product shares another category which the user has access to. So basically restricting users/groups to what categories (including products in the category) they can see.

Does that make sense?

What would be the best approach do this? I'm not asking for you guys to code everything for me, I've just sat for like 3 or 4 days now and haven't made anything yet that worked the way I want it work, I even tried a couple of plugins such a Restrict cateogies, Members, Member Roles etc.



  • Welcome at WPSE! Can you show us what you have tried so far, what worked and what didn't?
    – kraftner
    Commented Jun 8, 2014 at 22:19

1 Answer 1


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:

    [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' ) ||
    ) ) {
        $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.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.