To thoroughly understand how this code works, we’ll check both the admin-facing and public-facing code in detail.
Admin Facing Code
The admin-facing code provides a user interface for setting up the quantity limits in the WordPress admin dashboard. It allows administrators to enable/disable the functionality, define error messages, and set quantity limits for product categories based on user roles.
Initial Setup and Hooks
class LPC_Admin
{
public $lpc_product_categories;
function __construct()
{
add_action('init', array($this, 'get_product_categories'));
add_action('admin_menu', array($this, 'add_menu_page'), 99);
add_action('admin_enqueue_scripts', array($this, 'add_admin_style'));
add_action('admin_enqueue_scripts', array($this, 'add_admin_script'));
}
__construct: Initializes the class and sets up hooks.get_product_categories: Retrieves product categories.add_menu_page: Adds the plugin settings page to the admin menu.add_admin_styleandadd_admin_script: Enqueues custom styles and scripts for the admin page.
Retrieve Product Categories
public function get_product_categories(){
global $lpc_product_categories;
$args = array(
'taxonomy' => "product_cat",
);
$lpc_product_categories = get_terms($args);
}
get_product_categories: Fetches all product categories and stores them in a global variable$lpc_product_categories.
Add Menu Page
public function add_menu_page()
{
add_menu_page(
esc_html__('Limit per Category', 'wp-paceah'),
esc_html__('Limit per Category', 'wp-paceah'),
'manage_options',
'lpc-settings',
array($this, 'settings_callback'),
'dashicons-businessman',
55.5
);
}
add_menu_page: Adds a new page to the WordPress admin menu for the plugin settings.esc_html__: Used for localization.manage_options: Capability required to access this menu.lpc-settings: The slug for the menu.settings_callback: The callback function to display the settings page.dashicons-businessman: The icon for the menu.55.5: The position of the menu.
Settings Page Callback
public function settings_callback() {
include_once LPC_PLUGIN_PATH."/admin/settings.php";
}
settings_callback: Includes the settings page template.
Enqueue Styles and Scripts
public function add_admin_style(){
if (is_admin()) {
wp_enqueue_style('lpc-admin', LPC_PLUGIN_URL.'admin/assets/css/admin.css', false, rand());
}
}
public function add_admin_script() {
if (is_admin()) {
global $lpc_product_categories;
wp_enqueue_script('lpc-repeater', LPC_PLUGIN_URL.'admin/assets/js/jquery.repeater.js', array('jquery'), '1.0', true);
wp_enqueue_script('lpc-admin', LPC_PLUGIN_URL.'admin/assets/js/admin.js', array('jquery'), '1.0', true);
wp_localize_script('lpc-admin', 'lpc_admin_object', array(
'ajaxurl' => admin_url('admin-ajax.php'),
'category_group' => $lpc_product_categories[1]->term_id,
));
}
}
}
new LPC_Admin();
add_admin_style: Enqueues the admin CSS file.add_admin_script: Enqueues the admin JS files and localizes the script to pass PHP data to JavaScript.
Settings Form Handling
global $lpc_product_categories, $wp_roles;
$confirmation_message = '';
if (isset($_POST['save_lpc_option']) && isset($_POST['lpc_settings'])){
update_option('lpc_settings', $_POST['lpc_settings']);
$confirmation_message = '<div class="notice notice-success"><p>Options saved successfully!</p></div>';
}
$lpc_settings = get_option('lpc_settings', array());
$all_roles = $wp_roles->roles;
$editable_roles = apply_filters('editable_roles', $all_roles);
- Global Variables:
$lpc_product_categoriesand$wp_rolesare used to fetch product categories and user roles. - Form Handling: When the form is submitted, the settings are saved to the WordPress options table using
update_option. - Fetching Settings: Retrieves existing settings using
get_option. - User Roles: Fetches all editable user roles.
Settings Form
<form method="POST">
<?php echo $confirmation_message; ?>
<table class="form-table">
<tbody>
<tr valign="top">
<th colspan="2">
<h3>LPC Settings</h3>
</th>
</tr>
<tr>
<th>
<label for="lpc_settings[enable]">Enable</label>
</th>
<td>
<input type="checkbox" class="regular-text" id="lpc_settings[enable]" name="lpc_settings[enable]" value="1" <?php echo esc_attr(isset($lpc_settings['enable']) ? 'checked' : ""); ?>><label>Enable/Disable</label>
<p class="description">Check the checkbox to enable the functionality.</p>
</td>
</tr>
<tr>
<th>
<label for="lpc_settings[max_qty_error_message]">Max Quantity Error Message</label>
</th>
<td>
<input type="text" class="regular-text" id="lpc_settings[max_qty_error_message]" name="lpc_settings[max_qty_error_message]" value="<?php echo esc_attr(isset($lpc_settings['max_qty_error_message']) ? $lpc_settings['max_qty_error_message'] : ""); ?>">
<p class="description">Enter error message that will display when max quantity limit is reached. You can use dynamic variables in the message. <br><i>{{cat_name}}</i>, <i>{{qty_limit}}</i></p>
</td>
</tr>
<tr>
<th>
<label for="lpc_settings[min_qty_error_message]">Min Quantity Error Message</label>
</th>
<td>
<input type="text" class="regular-text" id="lpc_settings[min_qty_error_message]" name="lpc_settings[min_qty_error_message]" value="<?php echo esc_attr(isset($lpc_settings['min_qty_error_message']) ? $lpc_settings['min_qty_error_message'] : ""); ?>">
<p class="description">Enter error message that will display when min quantity limit is reached. You can use dynamic variables in the message. <br><i>{{cat_name}}</i>, <i>{{qty_limit}}</i></p>
</td>
</tr>
<tr>
<th>
<label for="lpc_settings[quantity_threshold]">Quantity Threshold</label>
</th>
<td>
<div class="repeater">
<div data-repeater-list="lpc_settings">
<div class="lpc_heading">
<p class="column">Customer Group</p>
<p class="column">Category</p>
<p class="column">Min Qty</p>
<p class="column">Max Qty</p>
<p class="small-column">Action</p>
</div>
<?php
$counter = false;
foreach($lpc_settings as $key => $values){
if (is_array($values)){
$counter = true;
?>
<div data-repeater-item class="lpc_container">
<select class="form-control column" name="customer_group">
<?php
echo "<option value='no'>No</option>";
if($editable_roles){
foreach ($editable_roles as $key => $role) {
echo "<option value='".$key."' ".selected($values['customer_group'], $key).">".$role['name']."</option>";
}
}
?>
</select>
<select class="form-control column" name="category_group">
<?php
if($lpc_product_categories){
foreach ($lpc_product_categories as $key => $product_category) {
echo "<option value='".$product_category->term_id."' ".selected($values['category_group'], $product_category->term_id).">".$product_category->name."</option>";
}
}
?>
</select>
<input class="column" name="min_qty_value" type="number" value="<?php echo $values['min_qty_value']; ?>" />
<input class="column" name="max_qty_value" type="number" value="<?php echo $values['max_qty_value']; ?>" />
<input class="small-column button-secondary" data-repeater-delete type="button" value="Delete"/>
</div>
<?php
}
}
if (!$counter){
?>
<div data-repeater-item class="lpc_container">
<select class="form-control column" name="customer_group">
<?php
echo "<option value='no'>
No</option>";
if($editable_roles){
foreach ($editable_roles as $key => $role) {
echo "<option value='".$key."'>".$role['name']."</option>";
}
}
?>
</select>
<select class="form-control column" name="category_group">
<?php
if($lpc_product_categories){
foreach ($lpc_product_categories as $key => $product_category) {
echo "<option value='".$product_category->term_id."'>".$product_category->name."</option>";
}
}
?>
</select>
<input class="column" name="min_qty_value" type="number" value="1" />
<input class="column" name="max_qty_value" type="number" value="10" />
<input class="small-column button-secondary" data-repeater-delete type="button" value="Delete"/>
</div>
<?php
}
?>
</div>
<input data-repeater-create type="button" class="button-primary" value="Add New Row"/>
</div>
</td>
</tr>
</tbody>
</table>
<p class="submit">
<input type="submit" class="button-primary" name="save_lpc_option" value="Save">
</p>
</form>
- Settings Form: This form contains the settings for enabling the functionality, defining error messages, and setting quantity limits.
- Enable Checkbox: Toggles the functionality on or off.
- Error Messages: Fields to enter custom error messages for minimum and maximum quantity limits.
- Quantity Thresholds: A repeater field to set quantity limits for different customer groups and product categories.
Public Facing Code
The public-facing code enforces the quantity limits set by the admin on the WooCommerce cart page. It checks the quantities before proceeding to checkout and displays error messages if the limits are not met.
Initial Setup and Hooks
class LPC_Public
{
public $lpc_settings;
function __construct()
{
$this->lpc_settings = get_option('lpc_settings', array());
add_action('woocommerce_before_cart', array($this, 'custom_check_cart_qty_before_checkout'));
}
__construct: Initializes the class and retrieves the plugin settings.woocommerce_before_cart: Hook that calls the quantity check function before displaying the cart.
Helper Function to Get Product Category by ID
public function get_product_category_by_id($category_id) {
$term = get_term_by('id', $category_id, 'product_cat', 'ARRAY_A');
return $term['name'];
}
get_product_category_by_id: Fetches the name of a product category by its ID.
Check Cart Quantity Limits
public function custom_check_cart_qty_before_checkout() {
$category_qty_limits = array();
if (!empty($this->lpc_settings)){
foreach ($this->lpc_settings as $key => $values){
if (is_array($values)){
$category_qty_limits[$values['category_group']] = $values;
}
}
}
$category_quantities = array();
foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
$product_id = $cart_item['product_id'];
$product = wc_get_product($product_id);
$category_ids = $product->get_category_ids();
foreach ($category_ids as $category_id) {
if (!isset($category_quantities[$category_id])) {
$category_quantities[$category_id] = 0;
}
$category_quantities[$category_id] += $cart_item['quantity'];
}
}
if (isset($this->lpc_settings['enable']) && $this->lpc_settings['enable'] == 1){
foreach ($category_quantities as $category_id => $quantity) {
$category_data = array();
if (isset($category_qty_limits[$category_id])){
$category_data = $category_qty_limits[$category_id];
if (current_user_can($category_data['customer_group']) || $category_data['customer_group'] == "no"){
if ($quantity > $category_data['max_qty_value']) {
remove_action('woocommerce_proceed_to_checkout', 'woocommerce_button_proceed_to_checkout', 20);
$max_qty_error_message = $this->lpc_settings['max_qty_error_message'];
$max_qty_error_message = str_replace('{{qty_limit}}', "<b>".$category_data['max_qty_value']."</b>" , $max_qty_error_message);
$category_name = $this->get_product_category_by_id($category_id);
$max_qty_error_message = str_replace('{{cat_name}}', "<b>$category_name</b>", $max_qty_error_message);
wc_add_notice(__($max_qty_error_message, 'woocommerce'), 'error');
}
if ($quantity < $category_data['min_qty_value']) {
remove_action('woocommerce_proceed_to_checkout', 'woocommerce_button_proceed_to_checkout', 20);
$min_qty_error_message = $this->lpc_settings['min_qty_error_message'];
$min_qty_error_message = str_replace('{{qty_limit}}', "<b>".$category_data['min_qty_value']."</b>" , $min_qty_error_message);
$category_name = $this->get_product_category_by_id($category_id);
$min_qty_error_message = str_replace('{{cat_name}}', "<b>$category_name</b>", $min_qty_error_message);
wc_add_notice(__($min_qty_error_message, 'woocommerce'), 'error');
}
}
}
}
}
}
}
new LPC_Public();
custom_check_cart_qty_before_checkout: The main function to check cart quantity limits.- Retrieve Settings: Fetches the quantity limits from the settings.
- Calculate Category Quantities: Loops through the cart items and calculates the total quantities for each product category.
- Check Limits: Compares the quantities against the set limits and displays error messages if the limits are exceeded or not met. It also removes the “Proceed to Checkout” button if the limits are violated.