I have a trouble figuring out how to properly convert a list of product data from XML into CSV format.
My source is a XML file containing a list of products with attributes like color, size, material etc. with the following structure:
<?xml version="1.0" encoding="utf-8" ?>
<store>
<products>
<product>
<name>T-Shirt</name>
<price>19.00</price>
<attributes>
<attribute>
<name>Color</name>
<options>
<option>
<name>White</name>
<price>0.00</price>
</option>
<option>
<name>Black</name>
<price>0.00</price>
</option>
<option>
<name>Blue</name>
<price>0.00</price>
</option>
</options>
</attribute>
<attribute>
<name>Size</name>
<options>
<option>
<name>XS</name>
<price>-5.00</price>
</option>
<option>
<name>S</name>
<price>-5.00</price>
</option>
<option>
<name>M</name>
<price>0.00</price>
</option>
<option>
<name>L</name>
<price>0.00</price>
</option>
<option>
<name>XL</name>
<price>5.00</price>
</option>
</options>
</attribute>
</attributes>
</product>
<product>
<name>Sweatshirt</name>
<price>49.00</price>
<attributes>
<attribute>
<name>Color</name>
<options>
<option>
<name>White</name>
<price>0.00</price>
</option>
<option>
<name>Black</name>
<price>0.00</price>
</option>
</options>
</attribute>
<attribute>
<name>Size</name>
<options>
<option>
<name>XS</name>
<price>-10.00</price>
</option>
<option>
<name>M</name>
<price>0.00</price>
</option>
<option>
<name>XL</name>
<price>10.00</price>
</option>
</options>
</attribute>
<attribute>
<name>Material</name>
<options>
<option>
<name>Cotton</name>
<price>10.00</price>
</option>
<option>
<name>Polyester</name>
<price>0.00</price>
</option>
</options>
</attribute>
</attributes>
</product>
<product>
<name>Earrings</name>
<price>29.00</price>
</product>
</products>
</store>
Each product has a number of elements like name, price etc. but also a random number of attributes (like color, size, material etc.) that also have a random number of options. Each option can affect the price of the product, so ordering a XS sized t-shirt can be cheaper than ordering a XL sized t-shirt.
I would like to end up with a CSV representing one attribute combination on each line.
In my example that would result in 3 colors x 5 sizes = 15 lines for the T-Shirt , 2 colors x 3 sizes x 2 materials = 12 lines for the Sweatshirt and 1 line for the Earrings without any attributes:
name,price,color,size,material
T-Shirt,14.00,White,XS,
T-Shirt,14.00,Black,XS,
T-Shirt,14.00,Blue,XS,
T-Shirt,14.00,White,S,
T-Shirt,14.00,Black,S,
T-Shirt,14.00,Blue,S,
T-Shirt,19.00,White,M,
T-Shirt,19.00,Black,M,
T-Shirt,19.00,Blue,M,
T-Shirt,19.00,White,L,
T-Shirt,19.00,Black,L,
T-Shirt,19.00,Blue,L,
T-Shirt,24.00,White,XL,
T-Shirt,24.00,Black,XL,
T-Shirt,24.00,Blue,XL,
Sweatshirt,49.00,White,XS,Cotton
Sweatshirt,49.00,Black,XS,Cotton
Sweatshirt,59.00,White,M,Cotton
Sweatshirt,69.00,Black,M,Cotton
Sweatshirt,69.00,White,XL,Cotton
Sweatshirt,69.00,Black,XL,Cotton
Sweatshirt,39.00,White,XS,Polyester
Sweatshirt,39.00,Black,XS,Polyester
Sweatshirt,49.00,White,M,Polyester
Sweatshirt,49.00,Black,M,Polyester
Sweatshirt,59.00,White,XL,Polyester
Sweatshirt,59.00,Black,XL,Polyester
Earrings,29.00,,,
I already managed to generate the CSV Output for simple products like the Earrings and products with just one attribute, but am struggling to come up with a way to generate all possible product attribute combinations for products with more than one attribute.
My miserable attempts at this so far have produced following code:
<?php
mb_internal_encoding("UTF-8");
header('Content-Type: text/html; charset=utf-8');
$source = "example.xml";
$handle = fopen($source, "r");
$fp = fopen('export.csv', 'w');
$xml = simplexml_load_file($source);
// Generate list of attributes (for csv header etc.)
$header_attributes = array();
foreach ($xml->products->product as $product) {
if(isset($product->attributes)) {
foreach($product->attributes->attribute as $attribute) {
array_push($header_attributes, $attribute->name);
}
}
}
$header_attributes = array_unique($header_attributes);
$csvheader = array(
'name','price' // these exist for all products, could also include weight, image, description, special price etc...
);
$static_csvheadercount = count($csvheader);
foreach($header_attributes as $attribute) {
array_push($csvheader, $attribute); // add variable number of attribute fields to csv header
}
fputcsv($fp, $csvheader);
foreach ($xml->products->product as $product) { // loop through each product
if(isset($product->attributes)) $simple = 0;
else $simple = 1;
if($simple == 1) { // if product is a simple product with no attributes
$output=array();
array_push($output,(string)$product->name);
array_push($output,(string)$product->price);
for($i = $static_csvheadercount + $attribute_position; $i < count($csvheader); $i++) {
array_push($output, '');
}
fputcsv($fp, $output);
}
else { // is a configurable product with attributes
$json = json_encode($product->attributes);
$attributes = json_decode($json, TRUE);
$attributes_number = count($product->attributes->attribute);
if($attributes_number > 1) { // if product has more than 1 attributes so we have to generate each attribute combination
//
// I'm trying to figure out what should happen here
//
}
else { // if product has only one attribute
$attributename = (string)$product->attributes->attribute->name;
$attribute_position = array_search($attributename, $header_attributes);
$options_number = count($product->attributes->options->option);
$pos = 1;
foreach($attributes['attribute']['options']['option'] as $option) {
$output=array();
array_push($output,(string)$product->name);
array_push($output,(string)$product->price);
for($i = $static_csvheadercount - 1; $i < ($static_csvheadercount + $attribute_position); $i++) {
array_push($output, '');
}
$output[$static_csvheadercount + $attribute_position] = $option['name'];
for($i = $static_csvheadercount + $attribute_position; $i < count($csvheader) - 1 ; $i++) {
array_push($output, '');
}
fputcsv($fp, $output);
$pos++;
}
$output=array();
array_push($output,(string)$product->name);
array_push($output,(string)$product->price);
for($i = $static_csvheadercount; $i < count($csvheader); $i++) {
array_push($output, '');
}
fputcsv($fp, $output);
}
}
}
?>
I've been stuck at this problem for hours unable to figure out a solution.
Can someone give a few tips or pointer how to achieve the output for products with multiple attributes?