Location: PHPKode > scripts > DOMIT XML parser > domit-xml-parser/state_rotation.html
<html>
<head>
<title>OOP Techniques for Modifying an XML Document: Part 1</title>
</head>
<body>
<h2>OOP Techniques for Modifying an XML Document: Part 1</h2>
<p>The following tutorial is the first of a two-part series on using Object-Oriented Programming (OOP)
to improve your approach to manipulating xml structures.</p>


<br />
<h3>The Problem</h3>
<p>A DOMIT! user recently asked to see some examples of modifying an xml document . How should one read in an xml file, 
change one or more of its values, and write it back to the filesystem?</p>

<p>He sent me the following snippet of XML to work with:</p>

<p><font color="darkblue"><pre>

&lt;?xml version="1.0" encoding="iso-8859-1"?&gt;
&lt;!DOCTYPE glossary [
  &lt;!ELEMENT staterotation   (rotation+)&gt;
  &lt;!ELEMENT rotation        (state, banner)&gt;
  &lt;!ELEMENT state           (#PCDATA)&gt;
  &lt;!ELEMENT banner          (#PCDATA)&gt;
]&gt;
&lt;staterotation&gt;
	
	&lt;rotation&gt;
	  &lt;state&gt;ca&lt;/state&gt; 
	  &lt;banner&gt;8&lt;/banner&gt; 
	&lt;/rotation&gt; 
	
	&lt;rotation&gt;
	  &lt;state&gt;hi&lt;/state&gt; 
	  &lt;banner&gt;1&lt;/banner&gt; 
	&lt;/rotation&gt;
	
	&lt;rotation&gt;
	  &lt;state&gt;fl&lt;/state&gt; 
	  &lt;banner&gt;2&lt;/banner&gt; 
	&lt;/rotation&gt;
	
	&lt;rotation&gt;
	  &lt;state&gt;tx&lt;/state&gt; 
	  &lt;banner&gt;4&lt;/banner&gt; 
	&lt;/rotation&gt;
	
	&lt;rotation&gt;
	  &lt;state&gt;ny&lt;/state&gt; 
	  &lt;banner&gt;6&lt;/banner&gt; 
	&lt;/rotation&gt;

&lt;/staterotation&gt;

</pre></font></p>

<p>The requirements for working with the string were as follows:</p>

<p><i>Here is an xml file that contains different state nodes. With each state node there is a banner node. 
The banner node keeps track of which banner number to display when someone wants to view a banner from that state. 
After a banner has been viewed for that state the banner node value should be incremented by one.</i>
</p>

<br />
<h3>The Solution</h3>
<p>I was a bit unclear about the directions. I assumed that the banners were advertising of some sort, 
to be displayed on
a web site. I also assumed that there existed more than one banner per state and that these banners should 
be rotated 
such that the same one was not always on display.</p>

<p>But how many banners per state were there? And in what context would someone want to view a banner for one 
state as opposed to another? </p>

<p>I had many other questions, and my first thoughts were to e-mail the contributor and ask for more detail. 
Then I stopped and thought:</p>

<p>Instead of fretting over the minute details of the proposed implementation, why not tackle the problem from a 
more abstract, reusable perspective?
The xml file, after all, describes a set of data that is stable enough to warrant a Document Type Definition. 
It was not out of the question 
that the xml data might be used in a variety of different contexts. 

<p>With this in mind, I felt it would be wasteful to merely crank out a bunch of linear code that 
could only be used in a narrow set of circumstances. 
I should instead write a  "wrapper" class that handled the data access and modification in a context-agnostic way. 
The user could be provided with a few simple but versatile methods that automated all the messy, redundant work of
looping through nodes to find particular values. The xml data could thus be accessed in a safe and consistent manner, 
with methods that had already been tested and were known to work properly. 
It's a bit more up-front work, but the benefits are many.</p>

<br />
<h3>Building the Class</h3>
<p>So, I decided to build a class called <b>StateRotation</b>.</p>

<br />
<h4><i>a) Class and Constructor</i></h4>
<p>The first step is to create a class statement, and a class constructor whose role is to
build an empty DOMIT_Document that can be used by the class methods. 
The document is contained in the class variable <b>$xmlDoc</b>. The contructor invokes an instance of the <b>DOMIT!</b> parser:</p>

<p><font color="darkblue"><pre>

class StateRotation {
	var $xmlDoc;
	
	function StateRotation() {
		require_once("xml_domit_parser.php");
		$this-&gt;xmlDoc =&amp; new DOMIT_Document();
	} //StateRotation

</pre></font></p>

<br />
<h4><i>b) XML Input and Output</i></h4>

<p>Two methods are added for populating the document: 

<ul>
<li><p>The method <b>fromFile</b> uses the DOMIT_Document method <b>loadXML</b> to read from a file. 
You pass in a path to the xml file (<b>$filename</b>) and it will construct a new DOMIT_Document.</p></li>
<li><p>The method <b>fromString</b> uses the DOMIT_Document method <b>parseXML</b> to read from an xml string.
You pass in a raw xml string (<b>$string</b>) and it will construct a new DOMIT_Document.</p></li>
</ul>
</p>

<p><font color="darkblue"><pre>

	function fromFile($filename) {
		return $this-&gt;xmlDoc-&gt;loadXML($filename);
	} //fromFile
	
	function fromString($string) {
		return $this-&gt;xmlDoc-&gt;parseXML($string);
	} //fromString

</pre></font></p>

<p>Both methods return "true " if the parsing is successful.</p>

<p>A method for saving the modified xml file is also necessary. The <b>toFile</b> method uses the <b>saveXML</b>
method of DOMIT_Document, which returns "true" of the document is successfully saved:</p>

<p><font color="darkblue"><pre>

	function toFile($filename) {
		return $this->xmlDoc->saveXML($filename);
	} //toFIle

</pre></font></p>

<br />
<h4><i>c) Accessors and Mutators</i></h4>
<p>Now that the class is able to input and output xml, we must write the accessor and mutator methods - 
methods which get and set the class data. The <b>count</b> function will tell us how many rotation nodes exist. We'll
need this number to loop through each node and search for values.</p>

<p><font color="darkblue"><pre>

	function count() {
		return count($this-&gt;xmlDoc-&gt;documentElement-&gt;childNodes);
	} //count

</pre></font></p>

<p><b>getStatesList</b> is a convenient method for obtaining a list of existing states in the list. 
It returns this list as an array reference - thus the ampersand in the method signature. </p>

<p><font color="darkblue"><pre>

	function &amp;getStateList() {
		$total = $this-&gt;count();
		$states = array();
		
		for ($i = 0; $i &lt; $total; $i++) {
			$currRotation =&amp; $this-&gt;xmlDoc-&gt;documentElement-&gt;childNodes[$i];
			$states[] = $currRotation-&gt;childNodes[0]-&gt;firstChild-&gt;nodeValue;
		}
		
		return $states;
	} //getStateList

</pre></font></p>

<p>Note how <b>getStatesList</b> uses our <b>count()</b> function to loop through each child node of the documentElement.
At each node, it will burrow a bit deeper and grab the nodeValue of the state node.</p>

<p>The same basic loop can be used for the remaining methods. The <b>getBanner</b> method iterates
through each rotation node, performs a test to determine if the current state name equals the state name passed in by
the programmer, and if the test is successful, the banner value is returned. Otherwise a blank string is returned.
</p>

<p><font color="darkblue"><pre>

	function getBanner($state) {
		$total = $this-&gt;count();
		
		for ($i = 0; $i &lt; $total; $i++) {
			$currRotation =&amp; $this-&gt;xmlDoc-&gt;documentElement-&gt;childNodes[$i];
			
			if ($currRotation-&gt;childNodes[0]-&gt;firstChild-&gt;nodeValue == $state) {
				return $currRotation-&gt;childNodes[1]-&gt;firstChild-&gt;nodeValue;
			}
		}
		
		return "";
	} //getBanner
	
</pre></font></p>

<p>The <b>setBanner</b> method is also searches for a particular state node. When it finds that node, 
however, it updates the node with the nodeValue supplied by the programmer. A break statement pops you 
out of the loop once the update has been made.</p>

<p><font color="darkblue"><pre>

	function setBanner($state, $num) {
		$total = $this-&gt;count();
		
		for ($i = 0; $i &lt; $total; $i++) {
			$currRotation =&amp; $this-&gt;xmlDoc-&gt;documentElement-&gt;childNodes[$i];
			
			if ($currRotation-&gt;childNodes[0]-&gt;firstChild-&gt;nodeValue == $state) {
				$currRotation-&gt;childNodes[1]-&gt;firstChild-&gt;nodeValue = $num;
				break;
			}
		}
	} //setBanner
	
</pre></font></p>




<p>One of the requirements of the class is that the value of the banner node needs to be incremented each time it is viewed. 
We therefore add an <b>incrementBanner</b> method, which, like the <b>setBanner</b> method, loops through the rotation nodes
until it finds the specified state. It then bumps up the value of the banner by one.</p>

<p>One caveat when using xml is that numerical data is prersented in string format. When incrementing the banner value, we must
therefore:</p>

<p>
<ul>
<li><p>convert the number from a string to an integer using the PHP <b>intval</b> function</p></li>
<li><p>perform the increment</p></li>
<li><p>convert back from integer to string by concatenating the integer with a "".</p></li>
</ul>
</p>


<p><font color="darkblue"><pre>

	function incrementBanner($state) {
		$total = $this-&gt;count();
		
		for ($i = 0; $i &lt; $total; $i++) {
			$currRotation =&amp; $this-&gt;xmlDoc-&gt;documentElement-&gt;childNodes[$i];
			
			if ($currRotation-&gt;childNodes[0]-&gt;firstChild-&gt;nodeValue == $state) {
				$currNum = intval($currRotation-&gt;childNodes[1]-&gt;firstChild-&gt;nodeValue);
				$currNum++;
				$currRotation-&gt;childNodes[1]-&gt;firstChild-&gt;nodeValue = ("" . $currNum);
				break;
			}
		}
	} //incrementBanner
	
</pre></font></p>

<p>Once the increment is performed, we can exit the loop using the break statement.</p>

<br />
<h3>Implementing the Class</h3>
<p>Now that we have encapsulated the functionality that we need within the <b>StateRotation</b> class, 
we should be able to safely query and update the xml structure, never again having to worry about the gory details
of looping through nodes and testing each for certain conditions, while the inevitable typos creep in. 
If a method works properly once, it should work every time.</p>

<p>This makes for far cleaner, more readable, and more manageable 
code, that is insulated to a great extent from human error. It is in fact the whole point of an object oriented approach.</p>

<p>Let's try a simple test of our new class. We will:</p>

<p>
<ul>
<li><p>open an xml file</p></li>
<li><p>echo out the banner value of the "ca" state</p></li>
<li><p>increment the banner value</p></li>
<li><p>echo out the new data</p></li>
<li><p>write the updated xml string to the filesystem</p></li>
</ul>
</p>

<p>We instantiate the <b>StateRotation</b> class in the usual way:</p>

<p><font color="darkblue"><pre>

	require_once("state_rotation.php");
	$sr =&amp; new StateRotation();
	
</pre></font></p>

<p>The xml file <b>statecount.xml</b> is then loaded into the class with the <b>fromFile</b> method. 
Since this method returns true if the parsing is successful, it is prudent to trap for errors and print out 
an error in the event that something goes wrong:</p>

<p><font color="darkblue"><pre>

	if ($sr-&gt;fromFile("statecount.xml")) {
		//code goes here!!!!
	}	
	else {
		echo ("Invalid xml file");
	}
	
</pre></font></p>

<p>With out new class, echoing out the value of the "ca" banner is a one-line affair.</p>

<p><font color="darkblue"><pre>

echo("The current banner value for 'ca' is: " . $sr-&gt;getBanner("ca"));

</pre></font></p>

<p>Incrementing its value is equally as simple:</p>

<p><font color="darkblue"><pre>

$sr-&gt;incrementBanner("ca");

</pre></font></p>

<p>The new value can now be echoed:</p>

<p><font color="darkblue"><pre>

echo("&lt;br /&gt;&lt;br /&gt;The new banner value for 'ca' is: "  . $sr-&gt;getBanner("ca"));

</pre></font></p>

<p>The new xml string is then saved:</p>

<p><font color="darkblue"><pre>

$sr-&gt;toFile("statecount.xml");

</pre></font></p>

<p>Executing the code returns the hoped for results:</p>

<p><font color="darkblue"><pre>

The current banner value for 'ca' is: 8

The new banner value for 'ca' is: 9

</pre></font></p>


<p>We have thus achieved our objective, and have in our possession a completely reusable class that allows us to 
safely and concisely manipulate any xml string that conforms to the DTD specified!</p>

<p>You can download the source files in zip format <a href="state_rotation.zip">here</a></p>

<br />
<h3>Future Considerations</h3>

<p>In the second part of this tutorial, we will build upon the functionality of our <b>StateRotation</b> class by adding methods
to add, insert, and replace rotation nodes.</p>

<p>If you have any questions about this tutorial, please email the <a href="mailto:hide@address.com">author</a> or post to the 
<a href="http://www.engageinteractive.com/phpBB2/" target="_child">DOMIT! bulletin board</a>.</p>

</body>
</html>
Return current item: DOMIT XML parser