Gustavo
Gustavo

Reputation: 1683

How to build a XML from an Array in php?

This seams obvious, but what I found the most was how to manipulate existing XML and now I wish to build from ground zero. The source is a database converted into an Array. The root is a single "menu" and all child elements are called "item". The structure is defined by the value of "parent" property and "code" property.

item[0] ("code"=>"first" "somevar"=>"somevalue")
item[1] ("code"=>"second", "parent"=>"first" "somevar"=>"othervalue")

Means item[1] is a child of item[0].

<menu>
  <item code="first" somevar="somevalue">
    <item code="second" somevar="othervalue" />
  </item>
</menu>

There will be only two levels of items this time, maybe later I'll expand the capabilities to "n" levels...

I tried with SimpleXML, but it seams is too simple. So I tried with DOMDocument, but I'm stuck creating new elements...

$domMenu = new DOMDocument();
$domMenu->createElement("menu");
... creating the $domItem as a DOMElement with attributes ...
$domMenu->menu->appendChild($domItem);

This generates an error, it seams "menu" is not seen as an DOMElement. Should I use getElements methods or there is a better way of build this XML?

Upvotes: 0

Views: 140

Answers (2)

ThW
ThW

Reputation: 19482

You did not append the menu element to the DOM. And DOM does not map element names to object properties like SimpleXML. The root element is accessible using the DOMDocument::$documentElement property.

$domMenu = new DOMDocument();
$domMenu->appendChild(
  $menuNode = $domMenu->createElement("menu")
);

... creating the $domItem as a DOMElement with attributes ...

$menuNode->appendChild($domItem);

In you case I would suggest using xpath to find the parent node for the itemNode and if not found let the function call itself (recursion) to append the parent element first. If here is not parent item, append the node to the document element.

$data = [
   ["code"=>"second", "parent"=>"first", "somevar"=>"othervalue"],
   ["code"=>"first", "somevar"=>"somevalue"]
];

function appendItem($xpath, $items, $item) {
  // create the new item node
  $itemNode = $xpath->document->createElement('item');
  $itemNode->setAttribute('code', $item['code']);
  $itemNode->setAttribute('somevar', $item['somevar']);

  $parentCode = isset($item['parent']) ? $item['parent'] : NULL;
  // does it have a parent and exists this parent in the $items array
  if (isset($parentCode) && isset($items[$parentCode])) {
    // fetch the existing parent
    $nodes = $xpath->evaluate('//item[@code = "'.$parentCode.'"]');
    if ($nodes->length > 0) {
      $parentNode = $nodes->item(0);
    } else  {
      // parent node not found create it
      $parentNode = appendItem($xpath, $items, $items[$parentCode]);
    }
  } else {
    $parentNode = $xpath->document->documentElement; 
  }
  $parentNode->appendChild($itemNode);
  return $itemNode;
}

$dom = new DOMDocument();
$xpath = new DOMXpath($dom);
$dom->appendChild(
  $dom->createElement("menu")
);

// build an indexed list using the "code" values
$items = [];
foreach ($data as $item) {
  $items[$item['code']] = $item;
}
foreach ($items as $item) {
  // check if the item has already been added
  if ($xpath->evaluate('count(//item[@code = "'.$item['code'].'"])') == 0) {
    // add it
    appendItem($xpath, $items, $item);
  }
}

$dom->formatOutput = TRUE;
echo $dom->saveXml();

Output:

<?xml version="1.0"?>
<menu>
  <item code="first" somevar="somevalue">
    <item code="second" somevar="othervalue"/>
  </item>
</menu>

Upvotes: 1

Joran Den Houting
Joran Den Houting

Reputation: 3163

$xml = new SimpleXMLElement('<menu/>');
array_walk_recursive($array, array ($xml, 'addChild'));

print $xml->asXML();

Upvotes: 0

Related Questions