class-studiou-wc-product-cat-manage-manipulator.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <?php
  2. /**
  3. * Category Manipulator class
  4. *
  5. * Handles manipulation operations for product categories
  6. *
  7. * @since 1.0.0
  8. * @package Studiou_WC_Product_Cat_Manage
  9. * @subpackage Studiou_WC_Product_Cat_Manage/includes
  10. */
  11. // If this file is called directly, abort.
  12. if (!defined('WPINC')) {
  13. die;
  14. }
  15. class Studiou_WC_Product_Cat_Manage_Manipulator {
  16. /**
  17. * Database handler
  18. *
  19. * @var Studiou_WC_Product_Cat_Manage_DB
  20. */
  21. private $db;
  22. /**
  23. * Constructor
  24. *
  25. * @param Studiou_WC_Product_Cat_Manage_DB $db Database handler
  26. */
  27. public function __construct($db) {
  28. $this->db = $db;
  29. // Register AJAX handlers
  30. add_action('wp_ajax_studiou_wcpcm_move_products', array($this, 'handle_move_products_ajax'));
  31. }
  32. /**
  33. * Handle AJAX move products request
  34. */
  35. public function handle_move_products_ajax() {
  36. // Check nonce
  37. if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'studiou-wcpcm-nonce')) {
  38. wp_send_json_error(array(
  39. 'message' => __('Security check failed', 'studiou-wc-product-cat-manage')
  40. ));
  41. }
  42. // Check permissions
  43. if (!current_user_can('manage_woocommerce')) {
  44. wp_send_json_error(array(
  45. 'message' => __('You do not have sufficient permissions', 'studiou-wc-product-cat-manage')
  46. ));
  47. }
  48. // Validate input
  49. if (!isset($_POST['source_category']) || !isset($_POST['target_category'])) {
  50. wp_send_json_error(array(
  51. 'message' => __('Missing required parameters', 'studiou-wc-product-cat-manage')
  52. ));
  53. }
  54. $source_category_id = intval($_POST['source_category']);
  55. $target_category_id = intval($_POST['target_category']);
  56. // Validate categories
  57. if ($source_category_id === $target_category_id) {
  58. wp_send_json_error(array(
  59. 'message' => __('Source and target categories must be different', 'studiou-wc-product-cat-manage')
  60. ));
  61. }
  62. if ($source_category_id <= 0 || $target_category_id <= 0) {
  63. wp_send_json_error(array(
  64. 'message' => __('Invalid category selection', 'studiou-wc-product-cat-manage')
  65. ));
  66. }
  67. try {
  68. // Execute move operation
  69. $result = $this->move_products_between_categories($source_category_id, $target_category_id);
  70. // Return success response
  71. wp_send_json_success(array(
  72. 'message' => $result['message'],
  73. 'moved_count' => $result['moved_count'],
  74. 'source_count' => $result['source_count'],
  75. 'target_count' => $result['target_count']
  76. ));
  77. } catch (Exception $e) {
  78. wp_send_json_error(array(
  79. 'message' => sprintf(
  80. __('Error during move operation: %s', 'studiou-wc-product-cat-manage'),
  81. $e->getMessage()
  82. )
  83. ));
  84. }
  85. }
  86. /**
  87. * Move all products from source category to target category
  88. *
  89. * @param int $source_category_id Source category ID
  90. * @param int $target_category_id Target category ID
  91. * @return array Result with counts and message
  92. * @throws Exception On error
  93. */
  94. public function move_products_between_categories($source_category_id, $target_category_id) {
  95. // Get source and target category terms
  96. $source_category = get_term($source_category_id, 'product_cat');
  97. $target_category = get_term($target_category_id, 'product_cat');
  98. if (is_wp_error($source_category) || is_wp_error($target_category)) {
  99. throw new Exception(__('Invalid category specified', 'studiou-wc-product-cat-manage'));
  100. }
  101. // Get all products in source category
  102. $product_args = array(
  103. 'post_type' => 'product',
  104. 'post_status' => array('publish', 'private', 'draft'),
  105. 'posts_per_page' => -1,
  106. 'fields' => 'ids',
  107. 'tax_query' => array(
  108. array(
  109. 'taxonomy' => 'product_cat',
  110. 'field' => 'term_id',
  111. 'terms' => $source_category_id,
  112. )
  113. )
  114. );
  115. $products = get_posts($product_args);
  116. $moved_count = 0;
  117. // Move each product
  118. foreach ($products as $product_id) {
  119. // Get current categories for the product
  120. $current_categories = wp_get_post_terms($product_id, 'product_cat', array('fields' => 'ids'));
  121. if (is_wp_error($current_categories)) {
  122. continue;
  123. }
  124. // Remove source category and add target category
  125. $new_categories = array_diff($current_categories, array($source_category_id));
  126. $new_categories[] = $target_category_id;
  127. // Update product categories
  128. $result = wp_set_post_terms($product_id, $new_categories, 'product_cat');
  129. if (!is_wp_error($result)) {
  130. $moved_count++;
  131. }
  132. }
  133. // Get updated counts
  134. $source_count = $this->get_category_product_count($source_category_id);
  135. $target_count = $this->get_category_product_count($target_category_id);
  136. // Generate result message
  137. $message = sprintf(
  138. __('Moved %d products from "%s" to "%s". Source category now has %d products, target category now has %d products.', 'studiou-wc-product-cat-manage'),
  139. $moved_count,
  140. $source_category->name,
  141. $target_category->name,
  142. $source_count,
  143. $target_count
  144. );
  145. return array(
  146. 'moved_count' => $moved_count,
  147. 'source_count' => $source_count,
  148. 'target_count' => $target_count,
  149. 'message' => $message
  150. );
  151. }
  152. /**
  153. * Get product count for a category
  154. *
  155. * @param int $category_id Category ID
  156. * @return int Product count
  157. */
  158. private function get_category_product_count($category_id) {
  159. $args = array(
  160. 'post_type' => 'product',
  161. 'post_status' => array('publish', 'private', 'draft'),
  162. 'posts_per_page' => -1,
  163. 'fields' => 'ids',
  164. 'tax_query' => array(
  165. array(
  166. 'taxonomy' => 'product_cat',
  167. 'field' => 'term_id',
  168. 'terms' => $category_id,
  169. )
  170. )
  171. );
  172. $products = get_posts($args);
  173. return count($products);
  174. }
  175. /**
  176. * Get all categories with product counts for select lists
  177. *
  178. * @return array Array of categories with ID, name, and product count
  179. */
  180. public function get_categories_with_counts() {
  181. $args = array(
  182. 'taxonomy' => 'product_cat',
  183. 'orderby' => 'name',
  184. 'hide_empty' => false,
  185. );
  186. $categories = get_terms($args);
  187. $categories_with_counts = array();
  188. if (!is_wp_error($categories)) {
  189. foreach ($categories as $category) {
  190. $product_count = $this->get_category_product_count($category->term_id);
  191. $categories_with_counts[] = array(
  192. 'id' => $category->term_id,
  193. 'name' => $category->name,
  194. 'count' => $product_count,
  195. 'display_name' => sprintf('%s (%d products)', $category->name, $product_count)
  196. );
  197. }
  198. }
  199. return $categories_with_counts;
  200. }
  201. }