'GET', 'callback' => [__CLASS__, 'get_products'], 'permission_callback' => [Permissions::class, 'check_admin_permission'], ]); // Get single product register_rest_route('woonoow/v1', '/products/(?P\d+)', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'get_product'], 'permission_callback' => [Permissions::class, 'check_admin_permission'], ]); // Create product register_rest_route('woonoow/v1', '/products', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'create_product'], 'permission_callback' => [Permissions::class, 'check_admin_permission'], ]); // Update product register_rest_route('woonoow/v1', '/products/(?P\d+)', [ 'methods' => 'PUT', 'callback' => [__CLASS__, 'update_product'], 'permission_callback' => [Permissions::class, 'check_admin_permission'], ]); // Delete product register_rest_route('woonoow/v1', '/products/(?P\d+)', [ 'methods' => 'DELETE', 'callback' => [__CLASS__, 'delete_product'], 'permission_callback' => [Permissions::class, 'check_admin_permission'], ]); // Get product categories register_rest_route('woonoow/v1', '/products/categories', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'get_categories'], 'permission_callback' => [Permissions::class, 'check_admin_permission'], ]); // Get product tags register_rest_route('woonoow/v1', '/products/tags', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'get_tags'], 'permission_callback' => [Permissions::class, 'check_admin_permission'], ]); // Get product attributes register_rest_route('woonoow/v1', '/products/attributes', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'get_attributes'], 'permission_callback' => [Permissions::class, 'check_admin_permission'], ]); } /** * Get products list with filters */ public static function get_products(WP_REST_Request $request) { $page = max(1, (int) $request->get_param('page')); $per_page = min(100, max(1, (int) ($request->get_param('per_page') ?: 20))); $search = $request->get_param('search'); $status = $request->get_param('status'); $category = $request->get_param('category'); $type = $request->get_param('type'); $stock_status = $request->get_param('stock_status'); $orderby = $request->get_param('orderby') ?: 'date'; $order = $request->get_param('order') ?: 'DESC'; $args = [ 'post_type' => 'product', 'posts_per_page' => $per_page, 'paged' => $page, 'orderby' => $orderby, 'order' => $order, ]; // Search if ($search) { $args['s'] = sanitize_text_field($search); } // Status filter if ($status) { $args['post_status'] = $status; } else { $args['post_status'] = ['publish', 'draft', 'pending', 'private']; } // Category filter if ($category) { $args['tax_query'] = [ [ 'taxonomy' => 'product_cat', 'field' => 'term_id', 'terms' => (int) $category, ], ]; } // Type filter if ($type) { $args['tax_query'] = $args['tax_query'] ?? []; $args['tax_query'][] = [ 'taxonomy' => 'product_type', 'field' => 'slug', 'terms' => $type, ]; } // Stock status filter if ($stock_status) { $args['meta_query'] = [ [ 'key' => '_stock_status', 'value' => $stock_status, ], ]; } $query = new \WP_Query($args); $products = []; foreach ($query->posts as $post) { $product = wc_get_product($post->ID); if ($product) { $formatted = self::format_product_list_item($product); // Debug: Log first product to verify structure if (empty($products)) { error_log('WooNooW Debug - First product data: ' . print_r($formatted, true)); } $products[] = $formatted; } } $response = new WP_REST_Response([ 'rows' => $products, 'total' => $query->found_posts, 'page' => $page, 'per_page' => $per_page, 'pages' => $query->max_num_pages, '_debug' => 'ProductsController-v2-' . time(), // Verify this code is running ], 200); // Prevent caching $response->header('Cache-Control', 'no-cache, no-store, must-revalidate'); $response->header('Pragma', 'no-cache'); $response->header('Expires', '0'); $response->header('X-WooNooW-Version', '2.0'); // Debug header return $response; } /** * Get single product */ public static function get_product(WP_REST_Request $request) { $id = (int) $request->get_param('id'); $product = wc_get_product($id); if (!$product) { return new WP_Error('product_not_found', __('Product not found', 'woonoow'), ['status' => 404]); } return new WP_REST_Response(self::format_product_full($product), 200); } /** * Create new product */ public static function create_product(WP_REST_Request $request) { try { $data = $request->get_json_params(); // Validate required fields if (empty($data['name'])) { return new WP_Error('missing_name', __('Product name is required', 'woonoow'), ['status' => 400]); } // Determine product type $type = $data['type'] ?? 'simple'; if ($type === 'variable') { $product = new WC_Product_Variable(); } else { $product = new WC_Product_Simple(); } // Set basic data $product->set_name($data['name']); if (!empty($data['slug'])) { $product->set_slug($data['slug']); } $product->set_status($data['status'] ?? 'publish'); $product->set_description($data['description'] ?? ''); $product->set_short_description($data['short_description'] ?? ''); if (!empty($data['sku'])) { $product->set_sku($data['sku']); } if (!empty($data['regular_price'])) { $product->set_regular_price($data['regular_price']); } if (!empty($data['sale_price'])) { $product->set_sale_price($data['sale_price']); } $product->set_manage_stock($data['manage_stock'] ?? false); if (!empty($data['manage_stock'])) { $product->set_stock_quantity($data['stock_quantity'] ?? 0); } $product->set_stock_status($data['stock_status'] ?? 'instock'); // Optional fields if (!empty($data['weight'])) { $product->set_weight($data['weight']); } if (!empty($data['length'])) { $product->set_length($data['length']); } if (!empty($data['width'])) { $product->set_width($data['width']); } if (!empty($data['height'])) { $product->set_height($data['height']); } // Virtual and downloadable if (!empty($data['virtual'])) { $product->set_virtual(true); } if (!empty($data['downloadable'])) { $product->set_downloadable(true); } if (!empty($data['featured'])) { $product->set_featured(true); } // Categories if (!empty($data['categories']) && is_array($data['categories'])) { $product->set_category_ids($data['categories']); } // Tags if (!empty($data['tags']) && is_array($data['tags'])) { $product->set_tag_ids($data['tags']); } // Images if (!empty($data['image_id'])) { $product->set_image_id($data['image_id']); } if (!empty($data['gallery_image_ids']) && is_array($data['gallery_image_ids'])) { $product->set_gallery_image_ids($data['gallery_image_ids']); } $product->save(); // Handle variations for variable products if ($type === 'variable' && !empty($data['attributes']) && is_array($data['attributes'])) { self::save_product_attributes($product, $data['attributes']); if (!empty($data['variations']) && is_array($data['variations'])) { self::save_product_variations($product, $data['variations']); } } return new WP_REST_Response(self::format_product_full($product), 201); } catch (Exception $e) { return new WP_Error('create_failed', $e->getMessage(), ['status' => 500]); } } /** * Update product */ public static function update_product(WP_REST_Request $request) { $id = (int) $request->get_param('id'); $data = $request->get_json_params(); $product = wc_get_product($id); if (!$product) { return new WP_Error('product_not_found', __('Product not found', 'woonoow'), ['status' => 404]); } // Update basic data if (isset($data['name'])) $product->set_name($data['name']); if (isset($data['slug'])) $product->set_slug($data['slug']); if (isset($data['status'])) $product->set_status($data['status']); if (isset($data['description'])) $product->set_description($data['description']); if (isset($data['short_description'])) $product->set_short_description($data['short_description']); if (isset($data['sku'])) $product->set_sku($data['sku']); if (isset($data['regular_price'])) $product->set_regular_price($data['regular_price']); if (isset($data['sale_price'])) $product->set_sale_price($data['sale_price']); if (isset($data['manage_stock'])) { $product->set_manage_stock($data['manage_stock']); if ($data['manage_stock']) { if (isset($data['stock_quantity'])) $product->set_stock_quantity($data['stock_quantity']); } } if (isset($data['stock_status'])) $product->set_stock_status($data['stock_status']); if (isset($data['weight'])) $product->set_weight($data['weight']); if (isset($data['length'])) $product->set_length($data['length']); if (isset($data['width'])) $product->set_width($data['width']); if (isset($data['height'])) $product->set_height($data['height']); // Categories if (isset($data['categories'])) { $product->set_category_ids($data['categories']); } // Tags if (isset($data['tags'])) { $product->set_tag_ids($data['tags']); } // Images if (isset($data['image_id'])) { $product->set_image_id($data['image_id']); } if (isset($data['gallery_image_ids'])) { $product->set_gallery_image_ids($data['gallery_image_ids']); } $product->save(); // Handle variations for variable products if ($product->is_type('variable')) { if (isset($data['attributes'])) { self::save_product_attributes($product, $data['attributes']); } if (isset($data['variations'])) { self::save_product_variations($product, $data['variations']); } } return new WP_REST_Response(self::format_product_full($product), 200); } /** * Delete product */ public static function delete_product(WP_REST_Request $request) { $id = (int) $request->get_param('id'); $force = $request->get_param('force') === 'true'; $product = wc_get_product($id); if (!$product) { return new WP_Error('product_not_found', __('Product not found', 'woonoow'), ['status' => 404]); } $result = $product->delete($force); if (!$result) { return new WP_Error('delete_failed', __('Failed to delete product', 'woonoow'), ['status' => 500]); } return new WP_REST_Response(['success' => true, 'id' => $id], 200); } /** * Get product categories */ public static function get_categories(WP_REST_Request $request) { $terms = get_terms([ 'taxonomy' => 'product_cat', 'hide_empty' => false, ]); if (is_wp_error($terms)) { return new WP_REST_Response([], 200); // Return empty array on error } $categories = []; foreach ($terms as $term) { $categories[] = [ 'id' => $term->term_id, 'name' => $term->name, 'slug' => $term->slug, 'parent' => $term->parent, 'count' => $term->count, ]; } return new WP_REST_Response($categories, 200); } /** * Get product tags */ public static function get_tags(WP_REST_Request $request) { $terms = get_terms([ 'taxonomy' => 'product_tag', 'hide_empty' => false, ]); if (is_wp_error($terms)) { return new WP_REST_Response([], 200); // Return empty array on error } $tags = []; foreach ($terms as $term) { $tags[] = [ 'id' => $term->term_id, 'name' => $term->name, 'slug' => $term->slug, 'count' => $term->count, ]; } return new WP_REST_Response($tags, 200); } /** * Get product attributes */ public static function get_attributes(WP_REST_Request $request) { $attributes = wc_get_attribute_taxonomies(); $result = []; foreach ($attributes as $attribute) { $result[] = [ 'id' => $attribute->attribute_id, 'name' => $attribute->attribute_name, 'label' => $attribute->attribute_label, 'type' => $attribute->attribute_type, 'orderby' => $attribute->attribute_orderby, ]; } return new WP_REST_Response($result, 200); } /** * Format product for list view * Returns all essential product fields including type, status, prices, stock, etc. */ private static function format_product_list_item($product) { $image = wp_get_attachment_image_src($product->get_image_id(), 'thumbnail'); // Get price HTML - for variable products, show price range $price_html = $product->get_price_html(); if (empty($price_html) && $product->is_type('variable')) { $prices = $product->get_variation_prices(true); if (!empty($prices['price'])) { $min_price = min($prices['price']); $max_price = max($prices['price']); if ($min_price !== $max_price) { $price_html = wc_format_price_range($min_price, $max_price); } else { $price_html = wc_price($min_price); } } } return [ 'id' => $product->get_id(), 'name' => $product->get_name(), 'sku' => $product->get_sku(), 'type' => $product->get_type(), 'status' => $product->get_status(), 'price' => $product->get_price(), 'regular_price' => $product->get_regular_price(), 'sale_price' => $product->get_sale_price(), 'price_html' => $price_html, 'stock_status' => $product->get_stock_status(), 'stock_quantity' => $product->get_stock_quantity(), 'manage_stock' => $product->get_manage_stock(), 'image_url' => $image ? $image[0] : '', 'permalink' => get_permalink($product->get_id()), 'date_created' => $product->get_date_created() ? $product->get_date_created()->date('Y-m-d H:i:s') : '', 'date_modified' => $product->get_date_modified() ? $product->get_date_modified()->date('Y-m-d H:i:s') : '', ]; } /** * Format product with full details */ private static function format_product_full($product) { $data = self::format_product_list_item($product); // Add full details $data['description'] = $product->get_description(); $data['short_description'] = $product->get_short_description(); $data['slug'] = $product->get_slug(); $data['weight'] = $product->get_weight(); $data['length'] = $product->get_length(); $data['width'] = $product->get_width(); $data['height'] = $product->get_height(); $data['categories'] = $product->get_category_ids(); $data['tags'] = $product->get_tag_ids(); $data['image_id'] = $product->get_image_id(); $data['gallery_image_ids'] = $product->get_gallery_image_ids(); $data['virtual'] = $product->is_virtual(); $data['downloadable'] = $product->is_downloadable(); $data['featured'] = $product->is_featured(); // Gallery images $gallery = []; foreach ($product->get_gallery_image_ids() as $image_id) { $image = wp_get_attachment_image_src($image_id, 'full'); if ($image) { $gallery[] = [ 'id' => $image_id, 'url' => $image[0], 'width' => $image[1], 'height' => $image[2], ]; } } $data['gallery'] = $gallery; // Variable product specifics if ($product->is_type('variable')) { $data['attributes'] = self::get_product_attributes($product); $data['variations'] = self::get_product_variations($product); } return $data; } /** * Get product attributes */ private static function get_product_attributes($product) { $attributes = []; foreach ($product->get_attributes() as $attribute) { $attributes[] = [ 'id' => $attribute->get_id(), 'name' => $attribute->get_name(), 'options' => $attribute->get_options(), 'position' => $attribute->get_position(), 'visible' => $attribute->get_visible(), 'variation' => $attribute->get_variation(), ]; } return $attributes; } /** * Get product variations */ private static function get_product_variations($product) { $variations = []; foreach ($product->get_children() as $variation_id) { $variation = wc_get_product($variation_id); if ($variation) { $image = wp_get_attachment_image_src($variation->get_image_id(), 'thumbnail'); // Format attributes with human-readable names and values $formatted_attributes = []; // Get parent product attributes to know what to look for $parent_attributes = $product->get_attributes(); foreach ($parent_attributes as $parent_attr) { if (!$parent_attr->get_variation()) { continue; // Skip non-variation attributes } $attr_name = $parent_attr->get_name(); $clean_name = $parent_attr->get_name(); // Already clean for custom attributes // Get the variation's value for this attribute if (strpos($attr_name, 'pa_') === 0) { // Global/taxonomy attribute $clean_name = wc_attribute_label($attr_name); $value = $variation->get_attribute($attr_name); // Convert slug to term name if (!empty($value)) { $term = get_term_by('slug', $value, $attr_name); $value = $term ? $term->name : $value; } } else { // Custom attribute - get from meta $value = get_post_meta($variation_id, 'attribute_' . sanitize_title($attr_name), true); } $formatted_attributes[$clean_name] = $value; } $variations[] = [ 'id' => $variation->get_id(), 'sku' => $variation->get_sku(), 'price' => $variation->get_price(), 'regular_price' => $variation->get_regular_price(), 'sale_price' => $variation->get_sale_price(), 'stock_status' => $variation->get_stock_status(), 'stock_quantity' => $variation->get_stock_quantity(), 'manage_stock' => $variation->get_manage_stock(), 'attributes' => $formatted_attributes, 'image_id' => $variation->get_image_id(), 'image_url' => $image ? $image[0] : '', ]; } } return $variations; } /** * Save product attributes */ private static function save_product_attributes($product, $attributes_data) { $attributes = []; foreach ($attributes_data as $attr_data) { $attribute = new \WC_Product_Attribute(); $attribute->set_name($attr_data['name']); $attribute->set_options($attr_data['options']); $attribute->set_position($attr_data['position'] ?? 0); $attribute->set_visible($attr_data['visible'] ?? true); $attribute->set_variation($attr_data['variation'] ?? true); $attributes[] = $attribute; } $product->set_attributes($attributes); $product->save(); } /** * Save product variations */ private static function save_product_variations($product, $variations_data) { foreach ($variations_data as $var_data) { if (isset($var_data['id']) && $var_data['id']) { // Update existing variation $variation = wc_get_product($var_data['id']); } else { // Create new variation $variation = new WC_Product_Variation(); $variation->set_parent_id($product->get_id()); } if ($variation) { if (isset($var_data['sku'])) $variation->set_sku($var_data['sku']); if (isset($var_data['regular_price'])) $variation->set_regular_price($var_data['regular_price']); if (isset($var_data['sale_price'])) $variation->set_sale_price($var_data['sale_price']); if (isset($var_data['stock_status'])) $variation->set_stock_status($var_data['stock_status']); if (isset($var_data['manage_stock'])) $variation->set_manage_stock($var_data['manage_stock']); if (isset($var_data['stock_quantity'])) $variation->set_stock_quantity($var_data['stock_quantity']); if (isset($var_data['attributes'])) $variation->set_attributes($var_data['attributes']); if (isset($var_data['image_id'])) $variation->set_image_id($var_data['image_id']); $variation->save(); } } } }