In this tutorial, you will learn how to control the quantity of the specific set of products based on a logical condition
Here in this tutorial, we considered the condition if the discounted product added more than our defined quantity it will notify/alert the end-user but once you understand the concept the same can be applied in different scenarios with little modifications
Through this process, you will learn the following from the technical/development side
woocommerce_add_to_cart_validation filter hook
woocommerce_update_cart_validation filter hook
wc_add_notice WooCommerce notice function
wc_get_product function to get product instance
get_regular_price product instance method
get_sale_price product instance method
generate_cart_id cart instance method
find_product_in_cart cart instance method
get_cart_item cart instance method
Sample notice
Add to cart validation
Line 2: we defined a named constant MAX_QUANTITY where it helps how much discounted product a user can add to a cart
Line 4: we called the filter hook function add_filter with the tag woocommerce_add_to_cart_validation for the function validate_discounted_prdct_qty_limitation
The filter woocommerce_add_to_cart_validation is responsible for doing validation while adding the product to the cart
Line 19: Lets we see the inner working of the hooked function validate_discounted_prdct_qty_limitation
The hooked function has three arguments
$passed Boolean value to pass or fail the validation
$product_id an integer value which is the product id that is the primary key of that product
$quantity an integer which has the intended quantity of the product
Line 20: we call the function wc_get_product where we passed our product id which returns the right product object by making using of a factory pattern class WC_Product_Factory which return any one of the product object types WC_Product_Simple, WC_Product_Grouped, WC_Product_External, WC_Product_Variable, etc.,
Line 22: we get the products parent id if the product doesn’t have a parent which return 0 else it returns the parent product id
Line 23: if the product id isn’t empty (possible for variable products child product or for any custom product type) then assign the variable $product_id with the product’s parent id
Line 27: we call our validator function validate_product_qty and return it
On the shop page, when the threshold limit is crossed instead of showing the notice on shop page WooCommerce redirects to a specific product page and alerts the notice which is the default flow of the ajax add to cart functionality
Update cart validation
Line 3: we hooked our custom function on_update_cart_limit_qty to the hook tag woocommerce_update_cart_validation which is responsible for update cart validation
Therefore this custom function solely control the WooCommerce cart page validation and previous section we explored the hook woocommerce_add_to_cart_validation which can be triggered on the shop page or product page
Line 19: the function on_update_cart_limit_qty is a simple one-line function which calls the validation function validate_product_qty
Lets we see the arguments of this function
$passed Boolean value to pass or fail the validation
$cart_item_key string value, to be clear WooCommerce generated MD5 which is used as a key for every cart item in the cart
$values array value, currently updated product cart item properties
$quantity int value, which has the product’s updated quantity
Show validate message
Line 12: you may be surprised by seeing this function as earlier we saw that this function as the validation function but here it just the message notification function
The reason for this is we abstracted our validation logic in other business logic function is_discounted_prdct_qty_exceeded so you can reuse all the function up to we discussed (including this) without modifying anything and interpolate your desired validation business logic function into the if condition or even if you’re using these functions as object-oriented methods then you can inject the validation business logic as dependency injection (DI)
This function accepts three arguments they are
$product_id int value, which holds the product’s id (Primary Key)
$quantity int value, validating product quantity which can be verified against our MAX_QUANTITY constant
$action_type string value, a switcher key which accepts either add or update as value depends on this value we slightly modify the quantity calculation logic in the function is_discounted_prdct_qty_exceeded
Line 13: here whatever argument values we received are directly passed to the validation business logic function is_discounted_prdct_qty_exceeded
Line 15: the variable $output_string holds the string returned by the WordPress function wp_sprintf
Line 21: the WooCommerce function wc_add_notice is used to display the validation message
If product quantity exceeded the threshold limit then we return false else true
Validation business logic
Lets we see about the helper function is_discounted_prdct_qty_exceeded
Line 13: We passed the argument $product_id to the WooCommerce function wc_get_product which return the WC_Product object, if the product exists for the given product id else, return null if not found and if anything wrong return false
Line 14: we call the method get_regular_price of the WC_Product object variable $product_obj this method is responsible to return the product’s regular price i.e. actual price
Line 15: the method get_sale_price is similar to get_regular_price but it returns the discounted price
Line 17: we called the cart instance method generate_cart_id which return the unique cart id of the passed product id
Line 18: the cart instance method find_product_in_cart return the cart item key if the product is in the cart else return an empty string, this method is used to find whether our product is already in the cart or not
We will use this variable in if condition while adding a product to the cart we will see the purpose of this variable later
Line 20: this conditional logic is used to check whether our product is the discounted one or not
! empty( $sale_price ) here we check whether our sale price is not empty
(float) $sale_price < (float) $regular_price here we check if the sale price is lesser than the regular price to decide whether the product is discounted or not
Line 23: this conditional block is executed only when we are adding the product to the cart and the same product already present in the cart
'add' === $action_type here we checking is our action type is add if true means the next expression will be executed
! empty( $find_product_in_cart ) here we check if the product already in the cart
The reason for checking this condition is considering like this if the product which we are adding is already in the cart with a quantity 2 and the currently adding product quantity is 3 means we need to check the sum of both cart and added product quantity which will be 5 against our threshold value 3 instead of that if we check only added quantity with the threshold value it will pass but it’s wrong therefore we summed up both added product and cart quantity
Line 26: fetching a particular product’s data in the cart using the cart instance method get_cart_item using the cart item key which we get by passing the variable $cart_id
Line 27: the variable $cart_item holds an array which has all the details of a particular product in the cart, we fetching the product’s cart quantity $cart_item['quantity'] and sum with the add to cart quantity finally assign the total sum to the variable $quantity
Line 30: conditional check to check whether we crossed the threshold quantity if so then return true
Line 36: if the conditions are fails then return false to indicate threshold is either not reached or not applicable for this product
Conclusion
In this tutorial, you learned how to control the quantity of the products in the cart
This tutorial will teach you how to add a quantity input text box with the number type for each product in the shop page
Something like this
When a user clicks the add to cart ajax button instead of adding one quantity of the product it will add the quantity present in the input box to the cart part page
Rendering Input Quantity Box
To render the quantity input box we are using the filter woocommerce_loop_add_to_cart_link
You can find the official WooCommerce source code in GitHub where the filter hook is created
Here is the code snippet for rendering the quantity box just above the add to cart button
Lets we see line by line what this little function doing
Line 1: hooking our quantity input rendering function to the filter hook with default priority 10 and the last argument passed for the function add_filter is about how many arguments the filter expects
Line 3: here we declared our filter hook function with woocommerce_loop_add_to_cart_link expected two arguments $html and $product
The $html argument holds the HTML of the ajax add to cart button
The $product argument holds the product’s WooCommerce PHP object of any one of the following WC_Product_Simple or WC_Product_Variable or WC_Product_Grouped or WC_Product_External
Line 6: we do a short circuit conditional check and add input quantity to only satisfying products
Let’s explore each condition
The first condition is $product which just check the variable’s value if it’s null or false or zero or empty string then discard the inner block of the if condition
The second condition is $product->is_type('simple') which checks the current iterated product is of a type simple
The third condition is $product->is_purchasable() which checks whether the product is a purchasable one
The fourth condition is $product->is_in_stock() which checks whether the product is available in the stock
The fifth condition is !$product->is_sold_individually() which checks negation of whether the product is sold individually without any quantities
If all the above-mentioned condition is true then quantity input is rendered as in Line 7 using the WooCommerce template function woocommerce_quantity_input(array(), $product)
This template function woocommerce_quantity_input accepts three optional parameters
The first parameter is arguments for the HTML input number we pass an empty array so use the default values
The second parameter accepts either WC_Product object or null, the default value is null
If the second parameter is null then it fetches the product object from the $GLOBALS array but here we passed looped product object variable $product even though it’s not necessary to pass because the filter woocommerce_loop_add_to_cart_link assigns the current product to the $GLOBALS array
The third boolean parameter by default accepts true, if true it echoes the HTML else return the HTML
Line 7: declared a JS variable simpleProductInput and assigned a DOM node list which we fetched using the JS method querySelectorAll of the document object
Let us explore the string parameter '.product-type-simple .quantity > input' passed to the method querySelectorAll
The method querySelectorAll behaves just like CSS selector so whatever DOM matched to the argument passed to the method is fetched as JS node list
.product-type-simple is the class selector which fetches the DOM element which has the class product-type-simple
If we further extend our selector like .product-type-simple .quantity which fetches the element matches with class value quantity and present inside the element with the class value product-type-simple
The angle bracket > denotes child combinator selector which means it selects only direct children element
input selects the dom element with HTML tag input
So what our selector .product-type-simple .quantity > input is doing?
It selects the input DOM present in each simple product
Line 9: loop and fetch every targeted dom node from the dom node list
Line 10: the method dom.addEventListener accepts two parameters
The first one is to bind/add an event input it’s just like the events click, change, etc. to each input tag and the second parameter is listener anonymous function
Let’s see the inner working of the anonymous function which accepts a single argument Event object e
Inside the anonymous function, we chained a sequence of methods which is called as method chaining
e.target property points to DOM object which triggers the current event here it is the quantity input box
e.target.parentElement returns the parent element of the target which is HTML div rendered over the quantity input tag
e.target.parentElement.nextElementSibling returns the next element which sibling to the targeted DOM
Closely note here we used nextElementSibling instead of nextSibling because the nextSibling fetches immediate next node it may be an element node or text node or comment node but nextElementSibling fetches the element node like P, Div, Span, etc. here we fetch anchor tag of the add to cart button
Finally, we set the quantity what present in the input tag to the anchor tag add to cart button data attribute using the method setAttribute('data-quantity', e.target.value)
Which accepts two parameters, the first one is the name of the attribute data-quantity and the second one is the value assigned to that attribute
Here the value is quantity input’s value because it is the target of the current event
That’s all finally we enclose the script and function
Conclusion
In this tutorial, you learned how to intercept the shop page’s product loop and insert our desired logic or HTML
Learned about types of product object such as WC_Product_Simple, WC_Product_Variable, WC_Product_Grouped and WC_Product_External
Learned about product object methods such as is_type, is_purchasable, is_in_stock and is_sold_individually
Learned about woocommerce_quantity_input which return or echo HTML of the quantity input
Learned about Javascript event, method chaining, CSS selector and update or override the HTML attributes
We implemented the functionality to add desired quantity instead of adding a single quantity in the cart