$product->get_id(), 'title' => $product->get_name(), 'description' => wp_strip_all_tags( $product->get_description() ), 'short_description' => wp_strip_all_tags( $product->get_short_description() ), 'url' => get_permalink( $product->get_id() ), 'sku' => $product->get_sku(), 'price' => $product->get_price(), 'regular_price' => $product->get_regular_price(), 'sale_price' => $product->get_sale_price(), 'currency' => get_woocommerce_currency(), 'availability' => $this->map_availability( $product->get_stock_status() ), 'stock_quantity' => $product->get_stock_quantity(), 'condition' => 'new', 'brand' => $this->get_brand( $product ), 'gtin' => $this->get_gtin( $product ), 'mpn' => $this->get_mpn( $product ), 'images' => $this->get_images( $product ), 'categories' => $this->get_categories( $product ), 'weight' => $product->get_weight(), 'weight_unit' => get_option( 'woocommerce_weight_unit', 'kg' ), 'dimensions' => $this->get_dimensions( $product ), 'attributes' => $this->get_attributes( $product ), 'variants' => array(), 'is_variable' => $product->is_type( 'variable' ), 'parent_id' => $product->get_parent_id(), 'date_created' => $product->get_date_created() ? $product->get_date_created()->format( 'c' ) : null, 'date_modified' => $product->get_date_modified() ? $product->get_date_modified()->format( 'c' ) : null, ); // Group variants for variable products. if ( $product->is_type( 'variable' ) && $product instanceof \WC_Product_Variable ) { $data['variants'] = $this->map_variants( $product ); } /** * Filters the normalized product data for feed generation. * * @param array $data Normalized product data. * @param \WC_Product $product The WooCommerce product. */ return apply_filters( 'aicommerce_feed_product_data', $data, $product ); } /** * Map multiple products to normalized feed data. * * @param array $products Array of WC_Product objects. * * @return array Array of normalized product data. */ public function map_batch( array $products ): array { $mapped = array(); foreach ( $products as $product ) { if ( $product instanceof \WC_Product ) { $mapped[] = $this->map( $product ); } } return $mapped; } /** * Map WooCommerce stock status to feed availability values. * * @param string $stock_status WC stock status. * * @return string Feed-compatible availability value. */ private function map_availability( string $stock_status ): string { $map = array( 'instock' => 'in_stock', 'outofstock' => 'out_of_stock', 'onbackorder' => 'backorder', ); return $map[ $stock_status ] ?? 'out_of_stock'; } /** * Get brand with fallback chain. * * @param \WC_Product $product The product. * * @return string Brand name or empty string. */ private function get_brand( \WC_Product $product ): string { $brand = $product->get_meta( '_aicommerce_brand' ); if ( empty( $brand ) ) { $brand = $product->get_meta( '_wc_brand' ); } if ( empty( $brand ) && taxonomy_exists( 'product_brand' ) ) { $brand_terms = get_the_terms( $product->get_id(), 'product_brand' ); if ( is_array( $brand_terms ) && ! empty( $brand_terms ) ) { $brand = $brand_terms[0]->name; } } if ( empty( $brand ) ) { $brand = get_option( 'aicommerce_default_brand', '' ); } return (string) $brand; } /** * Get GTIN with fallback chain. * * @param \WC_Product $product The product. * * @return string GTIN or empty string. */ private function get_gtin( \WC_Product $product ): string { $gtin = $product->get_meta( '_aicommerce_gtin' ); if ( empty( $gtin ) && method_exists( $product, 'get_global_unique_id' ) ) { $gtin = $product->get_global_unique_id(); } if ( empty( $gtin ) ) { $gtin = $product->get_meta( '_global_unique_id' ); } return (string) $gtin; } /** * Get MPN with fallback chain. * * @param \WC_Product $product The product. * * @return string MPN or empty string. */ private function get_mpn( \WC_Product $product ): string { $mpn = $product->get_meta( '_aicommerce_mpn' ); if ( empty( $mpn ) ) { $mpn = $product->get_meta( '_mpn' ); } return (string) $mpn; } /** * Get all product images (main + gallery). * * @param \WC_Product $product The product. * * @return array Array of image URLs. */ private function get_images( \WC_Product $product ): array { $images = array(); $main_id = $product->get_image_id(); if ( $main_id ) { $url = wp_get_attachment_url( $main_id ); if ( $url ) { $images[] = $url; } } foreach ( $product->get_gallery_image_ids() as $gallery_id ) { $url = wp_get_attachment_url( $gallery_id ); if ( $url ) { $images[] = $url; } } return $images; } /** * Get product categories. * * @param \WC_Product $product The product. * * @return array Array of category data. */ private function get_categories( \WC_Product $product ): array { $terms = get_the_terms( $product->get_id(), 'product_cat' ); if ( ! is_array( $terms ) ) { return array(); } $categories = array(); foreach ( $terms as $term ) { $categories[] = array( 'id' => $term->term_id, 'name' => $term->name, 'slug' => $term->slug, ); } return $categories; } /** * Get product dimensions. * * @param \WC_Product $product The product. * * @return array Dimension data. */ private function get_dimensions( \WC_Product $product ): array { return array( 'length' => $product->get_length(), 'width' => $product->get_width(), 'height' => $product->get_height(), 'unit' => get_option( 'woocommerce_dimension_unit', 'cm' ), ); } /** * Get product attributes. * * @param \WC_Product $product The product. * * @return array Array of attribute name/value pairs. */ private function get_attributes( \WC_Product $product ): array { $attributes = array(); foreach ( $product->get_attributes() as $key => $attribute ) { if ( is_object( $attribute ) && method_exists( $attribute, 'get_options' ) ) { $attributes[ $key ] = array( 'name' => wc_attribute_label( $key, $product ), 'options' => $attribute->get_options(), 'visible' => $attribute->get_visible(), ); } } return $attributes; } /** * Map variants for a variable product. * * Groups color/size variants under the parent product entry. * * @param \WC_Product_Variable $product The variable product. * * @return array Array of variant data. */ private function map_variants( \WC_Product_Variable $product ): array { $variants = array(); $variation_ids = $product->get_children(); foreach ( $variation_ids as $variation_id ) { $variation = wc_get_product( $variation_id ); if ( ! $variation instanceof \WC_Product_Variation ) { continue; } $variants[] = array( 'id' => $variation->get_id(), 'sku' => $variation->get_sku(), 'price' => $variation->get_price(), 'regular_price' => $variation->get_regular_price(), 'sale_price' => $variation->get_sale_price(), 'availability' => $this->map_availability( $variation->get_stock_status() ), 'stock_quantity' => $variation->get_stock_quantity(), 'gtin' => $this->get_gtin( $variation ), 'mpn' => $this->get_mpn( $variation ), 'image' => $variation->get_image_id() ? wp_get_attachment_url( $variation->get_image_id() ) : '', 'attributes' => $variation->get_variation_attributes(), 'weight' => $variation->get_weight(), 'dimensions' => array( 'length' => $variation->get_length(), 'width' => $variation->get_width(), 'height' => $variation->get_height(), ), ); } return $variants; } }