-
-
Save hakre/0f0a65234fa139b95ee1 to your computer and use it in GitHub Desktop.
Using DOMXml and Xpath, to update XML entries
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* http://stackoverflow.com/questions/9671273/using-domxml-and-xpath-to-update-xml-entries | |
*/ | |
$xml = <<<XML | |
<?xml version="1.0" encoding="UTF-8"?> | |
<storagehouse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:noNamespaceSchemaLocation="schema.xsd"> | |
<item id="c7278e33ef0f4aff88da10dfeeaaae7a"> | |
<name>HDMI Cable 3m</name> | |
<weight>0.5</weight> | |
<category>Cables</category> | |
<location>B3</location> | |
</item> | |
<item id="df799fb47bc1e13f3e1c8b04ebd16a96"> | |
<name>Dell U2410</name> | |
<weight>2.5</weight> | |
<category>Monitors</category> | |
<location>C2</location> | |
</item> | |
</storagehouse> | |
XML; | |
class ItemNode | |
{ | |
private $node; | |
private $createData; | |
public function __construct(DOMNode $node) | |
{ | |
$this->node = $node; | |
$this->snapshot(); | |
} | |
/** | |
* did the repository node changed since last snapshot? | |
* | |
* @return bool | |
*/ | |
public function hasChanged() { | |
return !$this->arraysAreEqual($this->getData(), $this->createData); | |
} | |
/** | |
* compare arrays for equality but w/o key order | |
* @param array $a | |
* @param array $b | |
* @return bool | |
*/ | |
private function arraysAreEqual(array $a, array $b) | |
{ | |
if (count($a) != count($b)) { | |
return FALSE; | |
} | |
foreach($a as $name => $value) | |
{ | |
if (!array_key_exists($name, $b)) return FALSE; | |
if ($b[$name] !== $a[$name]) return FALSE; | |
} | |
return TRUE; | |
} | |
public function itemHasChanges(Item $item) | |
{ | |
return !$this->arraysAreEqual(get_object_vars($item), $this->createData); | |
} | |
public function snapshot() | |
{ | |
$this->createData = $this->getData(); | |
} | |
public function overloadObject($object) | |
{ | |
foreach ($this->getData() as $name => $value) { | |
$object->$name = $value; | |
} | |
} | |
/** | |
* @return array | |
*/ | |
public function getData() | |
{ | |
$values = array(); | |
foreach ($this->node->childNodes as $child) { | |
/* @var $child DOMElement */ | |
if ($child instanceof DOMElement) { | |
$values[$child->localName] = $child->nodeValue; | |
} | |
} | |
return $values; | |
} | |
public function updateWith(Item $item) | |
{ | |
$data = get_object_vars($item); | |
$this->setData($data); | |
$this->snapshot(); | |
} | |
public function setData(array $data) | |
{ | |
foreach ($data as $name => $value) | |
{ | |
$this->setValue($name, $value); | |
} | |
} | |
private function setValue($name, $value) | |
{ | |
$xp = new DOMXPath($this->node->ownerDocument); | |
$result = $xp->query(sprintf('./%s', $name), $this->node); | |
if (!$result || $result->length != 1) { | |
throw new InvalidArgumentException(sprintf('Value "%s" does not exist.', $name)); | |
} | |
$result->item(0)->nodeValue = $value; | |
} | |
} | |
class Repository | |
{ | |
private $doc; | |
private $provided = array(); | |
public function __construct($xml) | |
{ | |
$doc = new DOMDocument(); | |
$doc->loadXML($xml); | |
$this->doc = $doc; | |
} | |
public function getItemByID($id) | |
{ | |
$xp = new DOMXPath($this->doc); | |
if (($result = $xp->query(sprintf('//item[@id="%s"]', $id))) && $result->length == 1) { | |
$node = new ItemNode($result->item(0)); | |
$item = new Item($id); | |
$node->overloadObject($item); | |
$this->register($item, $node); | |
return $item; | |
} else { | |
throw new InvalidArgumentException(sprintf('No Item with id "%s" exists.', $id)); | |
} | |
} | |
private function register($object, ItemNode $node) | |
{ | |
$this->provided[] = array($object, $node); | |
} | |
public function saveChanges() | |
{ | |
$updates = array(); | |
foreach($this->provided as $entity) | |
{ | |
/* @var $object Item */ | |
/* @var $node ItemNode */ | |
list($object, $node) = $entity; | |
if ($node->hasChanged()) { | |
throw new UnexpectedValueException('Transaction not safe (DOMElement Node has changed).'); | |
} | |
if ($node->itemHasChanges($object)) { | |
$changes[] = $entity; | |
} | |
} | |
if (!count($changes)) { | |
return; | |
} | |
foreach($changes as $entity) { | |
list($object, $node) = $entity; | |
$node->updateWith($object); | |
} | |
} | |
public function getXML() { | |
return $this->doc->saveXML(); | |
} | |
} | |
class Item | |
{ | |
private $id; | |
public function __construct($id) | |
{ | |
$this->id = $id; | |
} | |
public function getID() | |
{ | |
return $this->id; | |
} | |
} | |
$id = "df799fb47bc1e13f3e1c8b04ebd16a96"; | |
$simplexml = simplexml_load_string($xml); | |
$result = $simplexml->xpath(sprintf('/storagehouse/item[@id="%s"]', $id)); | |
if (!$result || count($result) !== 1) { | |
throw new Exception(sprintf('Item with id "%s" does not exists or is not unique.', $id)); | |
} | |
list($item) = $result; | |
$item->category = 'LCD Monitor'; | |
echo $simplexml->asXML(); | |
$doc = new DOMDocument(); | |
$doc->loadXML($xml); | |
$xpath = new DOMXPath($doc); | |
$result = $xpath->query(sprintf('/storagehouse/item[@id="%s"]', $id)); | |
if (!$result || $result->length !== 1) { | |
throw new Exception(sprintf('Item with id "%s" does not exists or is not unique.', $id)); | |
} | |
$item = $result->item(0); | |
$category = $xpath->query('./category', $item)->item(0); | |
$category->nodeValue = "LCD Monitor"; | |
echo $doc->saveXML($item); | |
$repository = new Repository($xml); | |
$item = $repository->getItemByID($id); | |
$item->category = 'LCD Monitor'; | |
$repository->saveChanges(); | |
echo $repository->getXML(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment