From e732c913621dc308a3250f9c080b84487cf949a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CVeLiTi=E2=80=9D?= <“info@veliti.nl”> Date: Wed, 10 Dec 2025 14:03:16 +0100 Subject: [PATCH] Implement catalog to meta feed transformation with variant extraction and output functions --- assets/functions_meta.php | 256 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 assets/functions_meta.php diff --git a/assets/functions_meta.php b/assets/functions_meta.php new file mode 100644 index 0000000..b8c11a1 --- /dev/null +++ b/assets/functions_meta.php @@ -0,0 +1,256 @@ + $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); +} + +?> \ No newline at end of file