define_constants(); // Include required files $this->includes(); // Initialize hooks $this->init_hooks(); // Load text domain for translations add_action('plugins_loaded', array($this, 'load_textdomain')); } /** * Define plugin constants */ private function define_constants() { if (!defined('STUDIOU_WCMP_VERSION')) define('STUDIOU_WCMP_VERSION', '1.1.1'); if (!defined('STUDIOU_WCMP_PLUGIN_DIR')) define('STUDIOU_WCMP_PLUGIN_DIR', plugin_dir_path(__FILE__)); if (!defined('STUDIOU_WCMP_PLUGIN_URL')) define('STUDIOU_WCMP_PLUGIN_URL', plugin_dir_url(__FILE__)); } /** * Include required files */ private function includes() { // Admin classes require_once STUDIOU_WCMP_PLUGIN_DIR . 'includes/class-studiou-wcmp-admin.php'; // Frontend classes require_once STUDIOU_WCMP_PLUGIN_DIR . 'includes/class-studiou-wcmp-frontend.php'; } /** * Initialize hooks */ private function init_hooks() { // Activation hook register_activation_hook(__FILE__, array($this, 'activate')); // Admin hooks if (is_admin()) { add_action('init', array($this, 'init_admin'), 10); } // Frontend hooks add_action('init', array($this, 'init_frontend'), 10); // Add mandatory products to cart (with priority to ensure it runs after WooCommerce's own handlers) add_action('woocommerce_add_to_cart', array($this, 'add_mandatory_products_to_cart'), 20, 6); // Display mandatory products info on product page add_action('woocommerce_before_add_to_cart_button', array($this, 'display_mandatory_products_info'), 10); } /** * Load plugin text domain for translations */ public function load_textdomain() { load_plugin_textdomain( 'studiou-wc-mandatory-products', false, dirname(plugin_basename(__FILE__)) . '/languages' ); } /** * Plugin activation */ public function activate() { // Nothing to do at activation for now } /** * Initialize admin */ public function init_admin() { $admin = new StudioU_WCMP_Admin(); $admin->init(); } /** * Initialize frontend */ public function init_frontend() { $frontend = new StudioU_WCMP_Frontend(); $frontend->init(); } /** * Get all mandatory products for a product * * @param int $product_id The product ID * @return array Array of mandatory products */ public function get_mandatory_products($product_id) { $product_categories = get_the_terms($product_id, 'product_cat'); if (!$product_categories || is_wp_error($product_categories)) { return array(); } $mandatory_products = array(); $processed_categories = array(); // Get category lineage including parent categories foreach ($product_categories as $category) { $this->get_mandatory_products_from_category_lineage($category, $mandatory_products, $processed_categories); } return $mandatory_products; } /** * Recursive function to get mandatory products from a category and its parents * * @param object $category The category object * @param array &$mandatory_products Array to store mandatory products * @param array &$processed_categories Array to track processed categories to avoid duplicates */ private function get_mandatory_products_from_category_lineage($category, &$mandatory_products, &$processed_categories) { // Skip if we've already processed this category if (in_array($category->term_id, $processed_categories)) { return; } // Mark as processed $processed_categories[] = $category->term_id; // Get mandatory products for this category $category_mandatory_products = get_term_meta($category->term_id, 'mandatory_products', true); if (!empty($category_mandatory_products) && is_array($category_mandatory_products)) { foreach ($category_mandatory_products as $mandatory_product) { $mandatory_products[] = $mandatory_product; } } // Process parent category if exists if ($category->parent > 0) { $parent_category = get_term($category->parent, 'product_cat'); if ($parent_category && !is_wp_error($parent_category)) { $this->get_mandatory_products_from_category_lineage($parent_category, $mandatory_products, $processed_categories); } } } /** * Add mandatory products to cart * * @param string $cart_item_key Cart item key * @param int $product_id Product ID * @param int $quantity Quantity * @param int $variation_id Variation ID * @param array $variation Variation data * @param array $cart_item_data Cart item data */ public function add_mandatory_products_to_cart($cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data) { // Don't execute for mandatory products themselves to avoid infinite loops if (isset($cart_item_data['mandatory_product'])) { return; } // Get mandatory products for this product $mandatory_products = $this->get_mandatory_products($product_id); if (empty($mandatory_products)) { return; } // Get cart contents $cart_contents = WC()->cart->get_cart(); foreach ($mandatory_products as $mandatory_product) { $mandatory_product_id = absint($mandatory_product['product_id']); $mandatory_product_quantity = absint($mandatory_product['quantity']); $mandatory_product_type = sanitize_text_field($mandatory_product['type']); // Skip if product doesn't exist $product = wc_get_product($mandatory_product_id); if (!$product || !$product->is_purchasable() || !$product->is_in_stock()) { continue; } $add_to_cart = true; // Check if we need to add based on type if ($mandatory_product_type === 'OnePerCart') { // Check if product already exists in cart foreach ($cart_contents as $cart_item) { if ($cart_item['product_id'] == $mandatory_product_id) { $add_to_cart = false; break; } } } elseif ($mandatory_product_type === 'OnePerProduct') { // Check if this mandatory product has been added for this specific product already foreach ($cart_contents as $cart_item) { if (isset($cart_item['mandatory_for']) && $cart_item['mandatory_for'] == $product_id && $cart_item['product_id'] == $mandatory_product_id) { $add_to_cart = false; break; } } } // Add the mandatory product to cart if needed if ($add_to_cart) { // Add metadata to identify this as a mandatory product $extra_data = array( 'mandatory_product' => true, 'mandatory_for' => $product_id ); // Add to cart WC()->cart->add_to_cart( $mandatory_product_id, $mandatory_product_quantity, 0, // No variation array(), // No variation data $extra_data ); } } } /** * Display mandatory products info on product page */ public function display_mandatory_products_info() { global $product; if (!$product) { return; } $mandatory_products = $this->get_mandatory_products($product->get_id()); if (empty($mandatory_products)) { return; } echo '