| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- <?php
- /**
- * Plugin Name: QDR - Studiou WC Mandatory Products
- * Plugin URI: https://www.quadarax.com/plugins/studiou-wc-mandatory-products
- * Description: Extends WooCommerce to add mandatory products to cart based on product categories.
- * Version: 1.1.1
- * Requires at least: 6.7.2
- * Requires PHP: 8.2
- * Author: Dalibor Votruba
- * Author URI: https://www.quadarax.com
- * License: GPL v2 or later
- * License URI: https://www.gnu.org/licenses/gpl-2.0.html
- * Text Domain: studiou-wc-mandatory-products
- * Domain Path: /languages
- * WC requires at least: 3.0
- * WC tested up to: 8.0
- *
- * Woo: 12345:342928dfsfhsf8429842374wdf4234sfd
- * WC requires at least: 3.0
- * WC tested up to: 8.0
- *
- * @package StudioU_WC_Mandatory_Products
- */
- // Exit if accessed directly
- if (!defined('ABSPATH')) {
- exit;
- }
- // Declare HPOS compatibility
- add_action('before_woocommerce_init', function() {
- if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) {
- \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true);
- }
- });
- // Check if WooCommerce is active
- if (!in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
- return;
- }
- if (!class_exists('StudioU_WC_Mandatory_Products')) {
- class StudioU_WC_Mandatory_Products {
- /**
- * Constructor
- */
- public function __construct() {
- // Define constants
- $this->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 '<div class="mandatory-products-info">';
- echo '<h4>' . __('This product includes the following mandatory items:', 'studiou-wc-mandatory-products') . '</h4>';
- echo '<ul>';
-
- foreach ($mandatory_products as $mandatory_product) {
- $product_id = absint($mandatory_product['product_id']);
- $quantity = absint($mandatory_product['quantity']);
- $type = sanitize_text_field($mandatory_product['type']);
-
- $mandatory_product_obj = wc_get_product($product_id);
-
- if ($mandatory_product_obj) {
- echo '<li>';
- echo sprintf(
- '<a href="%s">%s</a> x %d (%s)',
- get_permalink($product_id),
- $mandatory_product_obj->get_name(),
- $quantity,
- $type === 'OnePerCart' ? __('One per cart', 'studiou-wc-mandatory-products') : __('One per product', 'studiou-wc-mandatory-products')
- );
- echo '</li>';
- }
- }
-
- echo '</ul>';
- echo '</div>';
- }
- }
-
- // Initialize the plugin
- new StudioU_WC_Mandatory_Products();
- }
|