The client has a simple request. The site has a sale category containing all products that are on sale. Instead of manually adding products to the category after giving them a sale price, and manually removing them from the category when they are no longer on sale (when the sale price is removed) he would like this to be automated.
First, we have to determine whether or not the product is on sale. WooCommerce provides us with a method we can use to set up a conditional:
if ( $product->is_on_sale() ) {
// do something
}
This method checks to see if the product is on sale, and returns a boolean. In order to be on sale, the sale price must exist and must be lower than the regular price.
WooCommerce has a number of different product types. We only use two on this store: simple products and variable products. A variable product is a product that has variations, like color, size, capsule count, etc. Because each of those variants is a separate product with its own SKU, price, attributes, etc., it is conceivably possible that only one of the variants will be placed on sale. By convention, if at least one variant has a sale price, the variable product will be considered on sale. We are not going to need to do anything to accommodate this; I am just pointing out the logic so that it won’t surprise or confuse us.
Let’s set up a few variables. We are going to need to establish the sale category, and the product. The both of these will use ids. Product id is determined by the product we are editing, and category id is determined by the name of the category, which for our purposes will be hardcoded. (We could also just hardcode the category id):
$category_name = 'Sale'; // replace with your sale category name.
$category_term = get_term_by( 'name', $category_name, 'product_cat' );
$category_id = $category_term->term_id;
If the condition above is met – if the product is on sale (if the boolean returns ‘true’) – we execute the following, to assign the product to the sale category:
wp_set_object_terms( $product->get_id(), $category_id, 'product_cat', true );
wp_set_object_terms( ) creates term and taxonomy relationships. It relates the object (post, product, etc) to a taxonomy (in this case, the sale category).
The entire function and hook is this:
add_action( 'woocommerce_before_product_object_save', 'plk_add_product_to_sale_category' );
/**
* Add products to sale category when they have a sale price.
*/
function plk_add_product_to_sale_category( $product ) {
$category_name = 'Sale'; // replace with your sale category name.
$category_term = get_term_by( 'name', $category_name, 'product_cat' );
$category_id = $category_term->term_id;
if ( $product->is_on_sale() ) {
wp_set_object_terms( $product->get_id(), $category_id, 'product_cat', true );
}
}
The product is defined by the product page you are on in the admin. The category is defined in the variable assignments. The plk_add_product_to_sale_category( ) function uses the is_on_sale() method to check to see if the product has a sale price that is lower than the main price, and if it does it uses the wp_set_object_terms( ) function to add the product to the sale category.
This is half of what we want to accomplish. We also want to remove the product from the sale category if it does not have a sale price.
First, we check to see if the is_on_sale( ) returns false:
if ( ! $product->is_on_sale() ) {
// do something
}
Then we use the following function: wp_remove_object_terms( ), which, as you can imagine, is the exact opposite of wp_set_object_terms( ).
We end up with this:
if ( ! $product->is_on_sale() ) {
wp_remove_object_terms( $product->get_id(), $category_id, 'product_cat' );
}
We could now have two entirely separate functions, also hooked into woocommerce_before_product_object_save, one that checks to see if the product is on sale and if it is, adds it to the sale category, and another that checks to see if the product is on sale and if it isn’t, removed it from the sale category, but since the is_on_sale( ) method returns a boolean, and there are no other possibilities but true or false, we can do this as a single function using if /else:
if ( $product->is_on_sale() ) {
wp_set_object_terms( $product->get_id(), $category_id, 'product_cat', true );
} else {
wp_remove_object_terms( $product->get_id(), $category_id, 'product_cat' );
}
}
The entire function is below. Paste it into your functions.php, or perhaps you have a file, like I do, that contains all your woocommerce customizations: inc/woocommerce.php:
/**
* Add/remove products to/from sale category when sale price is set/deleted.
*/
add_action( 'woocommerce_before_product_object_save', 'plk_product_sale_category', 10, 1 );
/**
* Add products to sale category when they have a sale price.
* Remove products from sale category on save when they don't have a sale price.
*/
function plk_product_sale_category( $product ) {
$category_name = 'Sale'; // replace with your sale category name.
$category_term = get_term_by( 'name', $category_name, 'product_cat' );
$category_id = $category_term->term_id;
if ( $product->is_on_sale() ) {
wp_set_object_terms( $product->get_id(), $category_id, 'product_cat', true );
} else {
wp_remove_object_terms( $product->get_id(), $category_id, 'product_cat' );
}
}