$product_id, 'title' => $product_name, 'description' => $description, 'price' => $base_price, 'link' => $product_url, 'image_link' => $main_image, ], $config); } else { // Product with variants $variants = extractVariants($product, $base_price); foreach ($variants as $variant) { // Combine meta description with variant suffix $variant_description = $description . ' ' . $variant['description_suffix']; $meta_feed[] = buildMetaProduct([ 'id' => $variant['id'], 'item_group_id' => $product_id, 'title' => $variant['title'], 'description' => $variant_description, 'price' => $variant['price'], 'link' => $product_url, 'image_link' => $variant['image'], 'additional_image_link' => $variant['additional_image'] ?? '', ], $config); } } } return $meta_feed; } //------------------------------------------ // Extract variants from configurations //------------------------------------------ function extractVariants($product, $base_price) { $variants = []; $product_id = $product['productcode']; $product_name = $product['productname']; $image_base_url = $GLOBALS['meta_config']['image_base_url']; $configurations = $product['configurations'] ?? []; foreach ($configurations as $config) { if ($config['type'] === 'group') { $group_name = $config['assignment_name'] ?? 'Option'; $attributes = $config['attributes'] ?? []; foreach ($attributes as $attr) { // Calculate price $attr_price = floatval($attr['price'] ?? 0); $attr_modifier = intval($attr['price_modifier'] ?? 1); $final_price = calculateVariantPrice($base_price, $attr_price, $attr_modifier); // Get images (using image_base_url for images) $variant_image = buildImageUrl($attr['alternative_media_full_path'] ?? '', $image_base_url); $additional_image = buildImageUrl($attr['full_path'] ?? '', $image_base_url); // Fallback to main product image if no variant image if (empty($variant_image)) { $variant_image = buildImageUrl($product['full_path'] ?? '', $image_base_url); } // Extract option name from item_name $item_name = $attr['item_name'] ?? ''; $option_name = formatAttributeName($item_name); // Build variant $variants[] = [ 'id' => $product_id . '_' . ($attr['attribute_id'] ?? ''), 'title' => $product_name . ' - ' . $option_name, 'description_suffix' => 'Available with ' . $option_name . ' ' . strtolower($group_name) . '.', 'price' => $final_price, 'image' => $variant_image, 'additional_image' => $additional_image, ]; } } } return $variants; } //------------------------------------------ // Build Meta product row //------------------------------------------ function buildMetaProduct($data, $config) { $product = [ 'id' => $data['id'], 'title' => $data['title'], 'description' => $data['description'], 'availability' => $config['availability'], 'condition' => $config['condition'], 'price' => formatPrice($data['price'], $config['currency']), 'link' => $data['link'], 'image_link' => $data['image_link'], 'brand' => $config['brand'], 'google_product_category' => $config['google_product_category'], ]; // Add optional fields if present if (!empty($data['item_group_id'])) { $product['item_group_id'] = $data['item_group_id']; } if (!empty($data['additional_image_link'])) { $product['additional_image_link'] = $data['additional_image_link']; } return $product; } //------------------------------------------ // Helper Functions //------------------------------------------ function getMetaDescription($product_code, $fallback_description = '') { global $meta_descriptions; // First check for exact match if (isset($meta_descriptions[$product_code])) { return $meta_descriptions[$product_code]; } // Then check for pattern match (starts with) foreach ($meta_descriptions as $pattern => $description) { if (strpos($product_code, $pattern) === 0) { return $description; } } // Return fallback if no match return $fallback_description; } function calculateVariantPrice($base_price, $modifier_price, $modifier_type) { if ($modifier_price <= 0) { return $base_price; } switch ($modifier_type) { case 1: // Addition return $base_price + $modifier_price; case 2: // Multiplication return $base_price * $modifier_price; default: return $base_price; } } function buildImageUrl($path, $base_url) { if (empty($path)) return ''; return rtrim($base_url, '/') . '/' . ltrim($path, '/'); } function buildProductUrl($slug, $base_url) { if (empty($slug)) return ''; return rtrim($base_url, '/') . '/product/' . $slug; } function formatPrice($price, $currency) { return number_format($price, 2, '.', '') . ' ' . $currency; } function cleanText($text) { $text = strip_tags($text); $text = preg_replace('/\s+/', ' ', $text); return trim($text); } function formatAttributeName($name) { // Remove prefix like "bracelet_" $name = preg_replace('/^bracelet_/', '', $name); // Replace underscores with spaces $name = str_replace('_', ' ', $name); // Capitalize words return ucwords($name); } //------------------------------------------ // Output Functions //------------------------------------------ function outputMetaFeedCSV($meta_feed) { header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename="meta_product_feed.csv"'); $output = fopen('php://output', 'w'); // Write headers if (!empty($meta_feed)) { fputcsv($output, array_keys($meta_feed[0])); // Write data foreach ($meta_feed as $row) { fputcsv($output, $row); } } fclose($output); } function outputMetaFeedXML($meta_feed) { header('Content-Type: application/xml; charset=utf-8'); $xml = new SimpleXMLElement(''); $channel = $xml->addChild('channel'); $channel->addChild('title', 'Morval Product Feed'); $channel->addChild('link', $GLOBALS['meta_config']['base_url']); $channel->addChild('description', 'Product feed for Meta catalog'); foreach ($meta_feed as $product) { $item = $channel->addChild('item'); foreach ($product as $key => $value) { $xml_key = 'g:' . $key; $item->addChild($xml_key, htmlspecialchars($value)); } } echo $xml->asXML(); } function outputMetaFeedJSON($meta_feed) { //header('Content-Type: application/json; charset=utf-8'); echo json_encode($meta_feed, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); } ?>