class-studiou-wcmp-frontend.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <?php
  2. /**
  3. * StudioU WC Mandatory Products Frontend
  4. *
  5. * @package StudioU_WC_Mandatory_Products
  6. */
  7. // Exit if accessed directly
  8. if (!defined('ABSPATH')) {
  9. exit;
  10. }
  11. class StudioU_WCMP_Frontend {
  12. /**
  13. * Constructor
  14. */
  15. public function __construct() {
  16. // Nothing to do at constructor
  17. }
  18. /**
  19. * Initialize hooks
  20. */
  21. public function init() {
  22. // Add CSS for frontend
  23. add_action('wp_enqueue_scripts', array($this, 'enqueue_styles'));
  24. // Add custom item data to cart item
  25. add_filter('woocommerce_get_item_data', array($this, 'add_cart_item_data'), 10, 2);
  26. // Prevent quantity changes for mandatory products
  27. add_filter('woocommerce_cart_item_quantity', array($this, 'adjust_cart_item_quantity'), 10, 3);
  28. // Make mandatory products non-removable if needed
  29. add_filter('woocommerce_cart_item_remove_link', array($this, 'adjust_cart_item_remove_link'), 10, 2);
  30. // Validate cart to ensure mandatory products are present
  31. add_action('woocommerce_check_cart_items', array($this, 'validate_cart_has_mandatory_products'));
  32. }
  33. /**
  34. * Enqueue frontend styles
  35. */
  36. public function enqueue_styles() {
  37. if (is_product() || is_cart()) {
  38. wp_enqueue_style(
  39. 'studiou-wcmp-styles',
  40. STUDIOU_WCMP_PLUGIN_URL . 'assets/css/frontend.css',
  41. array(),
  42. STUDIOU_WCMP_VERSION
  43. );
  44. }
  45. }
  46. /**
  47. * Add custom item data to cart item
  48. *
  49. * @param array $item_data Cart item data
  50. * @param array $cart_item Cart item
  51. * @return array Modified item data
  52. */
  53. public function add_cart_item_data($item_data, $cart_item) {
  54. if (isset($cart_item['mandatory_product']) && $cart_item['mandatory_product']) {
  55. $item_data[] = array(
  56. 'key' => __('Mandatory Item', 'studiou-wc-product-cat-manage'),
  57. 'value' => __('This item was automatically added', 'studiou-wc-product-cat-manage')
  58. );
  59. }
  60. return $item_data;
  61. }
  62. /**
  63. * Adjust cart item quantity field for mandatory products
  64. *
  65. * @param string $product_quantity HTML of quantity field
  66. * @param string $cart_item_key Cart item key
  67. * @param array $cart_item Cart item
  68. * @return string Modified quantity field HTML
  69. */
  70. public function adjust_cart_item_quantity($product_quantity, $cart_item_key, $cart_item) {
  71. // If this is a mandatory product, make quantity field readonly
  72. if (isset($cart_item['mandatory_product']) && $cart_item['mandatory_product']) {
  73. // Get current quantity
  74. $quantity = $cart_item['quantity'];
  75. // Return readonly quantity field
  76. return sprintf(
  77. '<div class="quantity"><input type="text" value="%s" class="input-text qty text" readonly="readonly" /></div>',
  78. esc_attr($quantity)
  79. );
  80. }
  81. return $product_quantity;
  82. }
  83. /**
  84. * Adjust remove link for mandatory products
  85. *
  86. * @param string $link The cart item remove link
  87. * @param string $cart_item_key The cart item key
  88. * @return string Modified remove link
  89. */
  90. public function adjust_cart_item_remove_link($link, $cart_item_key) {
  91. if (!isset(WC()->cart) || !method_exists(WC()->cart, 'get_cart') || !method_exists(WC()->cart, 'get_cart_item')) {
  92. return $link;
  93. }
  94. // Use get_cart_item method if available (WC 3.7+)
  95. if (method_exists(WC()->cart, 'get_cart_item')) {
  96. $cart_item = WC()->cart->get_cart_item($cart_item_key);
  97. } else {
  98. $cart = WC()->cart->get_cart();
  99. $cart_item = isset($cart[$cart_item_key]) ? $cart[$cart_item_key] : null;
  100. }
  101. // Return original link if cart item not found
  102. if (!$cart_item) {
  103. return $link;
  104. }
  105. // If this is a mandatory product, remove the ability to remove it
  106. if (isset($cart_item['mandatory_product']) && $cart_item['mandatory_product']) {
  107. return '<span class="mandatory-product-info" title="' .
  108. esc_attr__('This is a mandatory product and cannot be removed directly', 'studiou-wc-product-cat-manage') .
  109. '"><i class="dashicons dashicons-lock"></i></span>';
  110. }
  111. return $link;
  112. }
  113. /**
  114. * Validate cart to ensure mandatory products are present
  115. */
  116. public function validate_cart_has_mandatory_products() {
  117. // Skip if in admin
  118. if (is_admin()) {
  119. return;
  120. }
  121. // Skip if cart is not initialized
  122. if (!function_exists('WC') || !WC()->cart || WC()->cart->is_empty()) {
  123. return;
  124. }
  125. $cart_contents = WC()->cart->get_cart();
  126. $plugin = new StudioU_WC_Mandatory_Products();
  127. $required_mandatory_products = array();
  128. // First, get all mandatory products that should be in the cart
  129. foreach ($cart_contents as $cart_item) {
  130. // Skip if this is already a mandatory product
  131. if (isset($cart_item['mandatory_product']) && $cart_item['mandatory_product']) {
  132. continue;
  133. }
  134. $product_id = $cart_item['product_id'];
  135. $mandatory_products = $plugin->get_mandatory_products($product_id);
  136. foreach ($mandatory_products as $mandatory_product) {
  137. $mandatory_product_id = $mandatory_product['product_id'];
  138. $mandatory_product_type = $mandatory_product['type'];
  139. $mandatory_product_quantity = $mandatory_product['quantity'];
  140. // For OnePerCart, add once to requirements
  141. if ($mandatory_product_type === 'OnePerCart') {
  142. $key = 'cart_' . $mandatory_product_id;
  143. if (!isset($required_mandatory_products[$key])) {
  144. $required_mandatory_products[$key] = array(
  145. 'product_id' => $mandatory_product_id,
  146. 'quantity' => $mandatory_product_quantity,
  147. 'type' => $mandatory_product_type
  148. );
  149. }
  150. }
  151. // For OnePerProduct, add for each product
  152. else if ($mandatory_product_type === 'OnePerProduct') {
  153. $key = 'product_' . $product_id . '_' . $mandatory_product_id;
  154. if (!isset($required_mandatory_products[$key])) {
  155. $required_mandatory_products[$key] = array(
  156. 'product_id' => $mandatory_product_id,
  157. 'quantity' => $mandatory_product_quantity,
  158. 'type' => $mandatory_product_type,
  159. 'for_product_id' => $product_id
  160. );
  161. }
  162. }
  163. }
  164. }
  165. // Now check if all required mandatory products are in the cart
  166. $missing_products = array();
  167. foreach ($required_mandatory_products as $key => $required_product) {
  168. $found = false;
  169. foreach ($cart_contents as $cart_item) {
  170. if (isset($cart_item['product_id']) && $cart_item['product_id'] == $required_product['product_id']) {
  171. // For OnePerCart, just check it exists
  172. if ($required_product['type'] === 'OnePerCart') {
  173. $found = true;
  174. break;
  175. }
  176. // For OnePerProduct, check it exists for the specific product
  177. else if ($required_product['type'] === 'OnePerProduct' &&
  178. isset($cart_item['mandatory_for']) &&
  179. $cart_item['mandatory_for'] == $required_product['for_product_id']) {
  180. $found = true;
  181. break;
  182. }
  183. }
  184. }
  185. if (!$found) {
  186. $missing_products[] = $required_product;
  187. }
  188. }
  189. // If we have missing products, add them and show notice
  190. if (!empty($missing_products)) {
  191. foreach ($missing_products as $missing_product) {
  192. $product_id = $missing_product['product_id'];
  193. $quantity = $missing_product['quantity'];
  194. $type = $missing_product['type'];
  195. $extra_data = array(
  196. 'mandatory_product' => true
  197. );
  198. // For OnePerProduct, add the for_product_id
  199. if ($type === 'OnePerProduct' && isset($missing_product['for_product_id'])) {
  200. $extra_data['mandatory_for'] = $missing_product['for_product_id'];
  201. }
  202. // Add the product to cart
  203. WC()->cart->add_to_cart(
  204. $product_id,
  205. $quantity,
  206. 0, // No variation
  207. array(), // No variation data
  208. $extra_data
  209. );
  210. // Get product name
  211. $product = wc_get_product($product_id);
  212. $product_name = $product ? $product->get_name() : __('A product', 'studiou-wc-product-cat-manage');
  213. // Add notice
  214. wc_add_notice(
  215. sprintf(
  216. __('%s has been automatically added to your cart as it is required.', 'studiou-wc-product-cat-manage'),
  217. $product_name
  218. ),
  219. 'notice'
  220. );
  221. }
  222. }
  223. }
  224. }