Sometimes, WooCommerce websites owners need to hide products for non-authorized users. For example, you want to make some of your products visible for your registered members only. Or you don’t want your competitors to see some specific products.
Unfortunately, there’s no in-built feature like this in WooCommerce. I’ve searched for the ready-to-use plugins in the WordPress plugin directory implementing this feature, but with no results. Well, it seems that this topic is a good choice for a new post!
Today, I’m going to show you how to code a solution that will allow you to choose which WooCommerce products will be hidden for anonymous users.
How to mark certain products as hidden for non-authorized users?
First of all, we need a way to mark the products as hidden for anonymous users. There’re at least three possible options:
- Create a special products category
- Create a special product tag
- Create a custom meta field
The first two options are better if you think that the meta fields are too complicated. I’ve got to note that you can always use any custom fields plugin — ACF, Carbon Fields, etc. Also, you need to keep in mind that new categories or tags may be indexed by search engines if you don’t hide them.
That’s why I think that the meta fields are the best solution in such cases. This time, I won’t use any custom fields plugins and will show you how to create a custom meta field programmatically. Let’s get to work!
Let’s create a custom product meta field
As you may know, WooCommerce products by default come with a meta box called “Product data”. You can set and change a product’s type, price, and other information in this meta box. The good news is that this meta box is extensible, which means you can add your own fields and tabs there.
Since we need to add only one custom field, which can be attributed to the general product options. I think this is a good place for our meta field.
How to add custom field in the “Advanced” tab
At the end of the template that renders the “Advanced” tab, there’s a hook called woocommerce_product_options_advanced. Using this hook, we can add our own fields or event custom elements in this tab.
Since our field will have two possible values — true or false, we’ll add a new checkbox here. Luckily, the WooCommerce developers have created a few handful functions that render basic form fields:
- woocommerce_wp_text_input
- woocommerce_wp_hidden_input
- woocommerce_wp_textarea_input
- woocommerce_wp_checkbox
- woocommerce_wp_select
- woocommerce_wp_radio
If you want to learn more about those functions and their parameters, take a look at their source code here in the official WooCommerce GitHub repository.
You can guess what kind of fields render those functions by their names. We’re going to use the woocommerce_wp_checkbox() function this time.
add_action( 'woocommerce_product_options_advanced', 'add_is_hidden_for_anonymous_field' );
function add_is_hidden_for_anonymous_field() {
$args = array(
'id' => 'is_hidden_for_anonymous',
'label' => 'Hide product for non-authorize users',
);
woocommerce_wp_checkbox( $args );
}
As you see, we don’t even need to write any custom code here. We just use the in-built WooCommerce features. Let’s see what we’ve got in the “Advanced” tab after we saved our changes.
Works just as expected: our custom checkbox field appeared as the last field in the “Advanced” tab. Now we need to store this checkbox’s value as a product’s meta field. The hook we need to use this time is called woocommerce_process_product_meta.
How to save the custom field’s value
add_action( 'woocommerce_process_product_meta', 'save_is_hidden_for_anonymous_field' );
function save_is_hidden_for_anonymous_field( $post_id ) {
$product = wc_get_product( $post_id );
$value = $_POST['is_hidden_for_anonymous'];
$product->update_meta_data( 'is_hidden_for_anonymous', $value );
$product->save();
}
As you see, this time I don’t use the update_post_meta() function. Instead, I use the update_meta_data() method of the WC_Product class. This function is a part of the CRUD features that were introduced in WooCommerce 3.0. You can learn more about what is CRUD and its benefits here: Developing using WooCommerce CRUD objects.
Now, if you check the checkbox and press the “Update” button, you’ll see that the checkbox is checked. This means that our saving function works as expected.
How to hide products for non-authorized users
Now, we have a way to mark certain products as hidden for anonymous users. But we still need to use our field’s value to hide these products from the archive page.
At the moment, our products are displayed by default regardless of what value they have in the is_hidden_for_anonymous meta field. How can we change this behavior?
Well, if you’ve read my previous posts, you probably already know about a very handful action hook in WordPress: pre_get_posts. Using this hook, you can modify a WP_Query instance based on your conditions. If you want to learn more about this hook, feel free to check this post: How to add custom filters to the WooCommerce orders table?
add_action( 'pre_get_posts', 'maybe_hide_products_for_anonymous_users', 10, 1 );
function maybe_hide_products_for_anonymous_users( $query ) {
// We don't need to hide anything if a user is logged in
if ( is_user_logged_in() ) {
return;
}
// We don't want to modify queries that were called somewhere in the admin area
// We're intested in modifying search queries and product queries
if ( ! is_admin() && ( is_search() || in_array ( $query->get('post_type'), array( 'product' ) ) ) ) {
$meta_query = $query->get( 'meta_query' );
if ( ! is_array( $meta_query ) ) {
$meta_query = array();
}
$meta_query[] = array(
'key' => 'is_hidden_for_anonymous',
'compare' => 'NOT EXISTS'
);
$query->set( 'meta_query', $meta_query );
}
}
This time I wrote my code a bit differently. Pay your attention to these lines of code:
$meta_query = $query->get( 'meta_query' );
if ( ! is_array( $meta_query ) ) {
$meta_query = array();
}
What does this code? It’s checking if the meta_query property of the current query is not empty. If it’s not empty, we don’t want to rewrite the meta_query conditions set up before. It might have broken the logic of other plugins or WooCommerce itself. Instead, we’re taking the already existing meta_query array and just push there our new condition.
Does it really work?
Let’s check if our code really allowed us to hide products for non-authorized users. Behind the scenes, I’ve marked the first product from the previous screenshot (Album) as hidden for anonymous users. So, if our code really works, we won’t see it on the Shop page again if we’re not authorized. We won’t also see it in the search results or if we try to access it directly.
Yup, I see Beanie and Beanie with Logo products that we’ve seen on the previous screenshot, but I don’t see Album here. Just like expected. What about the search results?
Being unauthorized, I don’t even know that Album exists. But what if another user would send me a direct link to this product?
WordPress just shows me a 404 page instead of the product page. “Artemy, aren’t you trying to trick us? What if you just removed this product completely?”. Well, just try to authorize again, and you’ll see that your product still exists. It’s just hidden for anonymous visitors of your website. This is exactly what you need if your goal is to hide products for non-authorized users.
Final code
add_action( 'woocommerce_product_options_advanced', 'add_is_hidden_for_anonymous_field' );
function add_is_hidden_for_anonymous_field() {
$args = array(
'id' => 'is_hidden_for_anonymous',
'label' => 'Hide products for non-authorize users',
);
woocommerce_wp_checkbox( $args );
}
add_action( 'woocommerce_process_product_meta', 'save_is_hidden_for_anonymous_field' );
function save_is_hidden_for_anonymous_field( $post_id ) {
$product = wc_get_product( $post_id );
$value = $_POST['is_hidden_for_anonymous'];
$product->update_meta_data( 'is_hidden_for_anonymous', $value );
$product->save();
}
add_action( 'pre_get_posts', 'maybe_hide_products_for_anonymous_users', 10, 1 );
function maybe_hide_products_for_anonymous_users( $query ) {
// We don't need to hide anything if a user is logged in
if ( is_user_logged_in() ) {
return;
}
// We don't want to modify queries that were called somewhere in the admin area
// We're intested in modifying search queries and product queries
if ( ! is_admin() && ( is_search() || in_array ( $query->get('post_type'), array( 'product' ) ) ) ) {
$meta_query = $query->get( 'meta_query' );
if ( ! is_array( $meta_query ) ) {
$meta_query = array();
}
$meta_query[] = array(
'key' => 'is_hidden_for_anonymous',
'compare' => 'NOT EXISTS'
);
$query->set( 'meta_query', $meta_query );
}
}
You can use this code inside of your theme’s functions.php file, or you can create a plugin and use this code there.
Let me know in the comments if this post was helpful for you or if you’ve got any questions about its content. Thank you for your attention; see you in the next one!
I have an “onboarding (yes:no) field in my profile. How to use this snippet to only display the product if the Onboarding” field is yes?
Hi Scott. This post is pretty universal for all types of meta fields. You just need to change the meta field’s name and hide/show your products depending on the meta field’s value.