Pragmatism in the real world

Zend_Config: Iteration and Arrays

An question about iteration, arrays and Zend_Config was posted to the Zend Framework‘s mailing list by Troy Marker. I thought I’d repeat it here as the solution shows how flexible Zend_Config can be.

The post was:


First off I am using an XML file for the data:

< ?xml version="1.0"?>
<config>
    <lists>
        <root>
            <line1>
                <link>/index/show/gallery/cars/</link>
                <text>Cars</text>
            </line1>
        </root>
    </lists>
</config>

I am trying to iterate the file above to create a list in an array to pass to Smarty. I am using the function below to try this:
public static function GetList1($rlist)
{
$retval = array();
$count = 0;
$lists = Zend::registry('lists')->config->lists;
foreach($lists[$rlist] as $key => $list) {
$retval[count]['link'] = $list['link'];
$count++;
}
return $retval;
}

the $rlist parameter would pass the section of the xml file to put into the array.

The responce I am looking for is:

$retval[0][‘link’] = /index/show/gallery/cars/
$retval[0][‘text’] = Cars

At a guess, Troy is building a menu type system, but that’s by-the-by for the problem.

First of all, a recap on Zend_Config’s load system; you must always load one of the top level element’s from the file. In the case of this particular XML file, you would have to load <lists>. This is accomplished using the php code:
$config = new Zend_Config(Zend_Config_Xml::load('test.xml', 'lists'));
Having loaded a top level element, it follows that the $config now “points” to elements inside this element. Thus we can access the information at using:
$link = $config->root->line1->link
and $link will contain the string “/index/show/gallery/cars/”.

All nice and easy :)

Iteration is also supported by Zend_Config, so if we do:
foreach($config->root->line1 as $key=>$data)
{
echo "$key = $datan";
}

Then the output is:

link = /index/show/gallery/cars/
text = Cars

At this point when you realise that you need an array of arrays as Troy does, you’d be tempted to use a nested foreach loop to iterate over all the elements to create the array:
$allLines = array();
foreach($config->root as $line)
{
$thisElement = array();
foreach($line as $key=>$data)
{
$thisElement[$key] = $data;
}
$allLines[] = $thisElement;
}

A zend::dump($allLines) looks like this:

$allLines array(2) {
  [0] => array(2) {
    ["link"] => string(25) "/index/show/gallery/cars/"
    ["text"] => string(4) "Cars"
  }
  [1] => array(2) {
    ["link"] => string(27) "/index/show/gallery/family/"
    ["text"] => string(6) "Family"
  }
}

This solves Troy’s problem completely! However there is another way! (obviously, otherwise, this entry would be pointless!) Zend_Config provides a helper function for exactly this type of problem: asArray().

asArray() does exactly what it says on the tin: it gives you an array from your config key. This lets us lose one of the foreach loops:
$allLines = array();
foreach($config->root as $line)
{
$allLines[] = $line->asArray();
}

and a dump() of $allLines is exactly the same as above.

As this array is destined for Smarty, it’s likely to be processed using Smarty’s {foreach} construct. One useful thing about {foreach} is that it doesn’t need a zero indexed array, any array will do. Thus we can probably get away with this code:
$allLines = $config->root->asArray();
a dump() of $allLines this time gives:

$allLines array(2) {
  ["line1"] => array(2) {
    ["link"] => string(25) "/index/show/gallery/cars/"
    ["text"] => string(4) "Cars"
  }
  ["line2"] => array(2) {
    ["link"] => string(27) "/index/show/gallery/family/"
    ["text"] => string(6) "Family"
  }
}

As you can see, it’s not quite the same as before. This time each element’s key in $allLines is now the name of the enclosing element of the XML file. As I said, this probably doesn’t matter in 99.9% of cases. Going back to Troy’s GetList1() function, we can replace it with:

public static function GetList1($rlist)
{
return Zend::registry('lists')->$rlist->asArray();
}

Which, I think is pretty cool :)