Recursive Code to Parse Recursive Form Data

Spread the news

Recursive code can be very confusing and frustrating at first, but is a necessity if you are trying to accomplish certain tasks in an expandable, decoupled way.  I recently needed to make a function that could build a multi-dimensional array from a form.  The problem is that the depth of the array is completely fluid as the form is made by nested “rules” which play off each other, allowing the user to build a very complex rule set.  Without going into the details of of the proprietary code for my company, let’s take a look at the technique I chose and how you can use passing variables by reference to build a recursive loop that can go as deep as the array needs (limited only by server resources). Hopefully this technique will bring you a little clarity!

Background

The form data that gets passed into the function is used as data to implement an abstract class that can nest.  As the nesting occurs, the object within the data needs to be able to to take in the sub data, causing it to get deeper and deeper.  I decided to pass this “depth” in the form by using a special character (an underscore in this case) in the form field’s name.  The first portion of the form field’s name can be considered a “namespace” of the top level name of the variable.  Field names are long strings that are basic subcomponents separated by underscores. For example, “namespace_buttons_0_name=Test 1” should translate to this:

$namespace = [
	'buttons'=>[
		0=>[
			'name'=>'Test 1'
		]
	]
];

If you put a couple of these types of fields together, you can end up making a pretty large array:

“namespace_buttons_0_name=Test 1&namespace_buttons_0_color=blue&namespace_buttons_0_name=Test 2&namespace_buttons_0_color=red”

becomes:

$namespace = [
	'buttons'=>[
		0=>[
			'name'=>'Test 1',
			'color': 'blue'
		],
		1=>[
			'name'=>'Test 2',
			'color': 'red'
		]
	]
];

The deeper you go with this object, the more convoluted the field names become. However, there are some good ways to parse these, so let’s break it down!

The PHP

The first thing we will want to do is instantiate our base variable. Then, we can start iterating over the POST array to find any fields that are part of this “namespace.” We can use explode to break up the key into a simple array of components, checking the first one for the namespace to make sure we aren’t processing a form field that is not part of our array.

$namespace = [];
foreach ($_POST as $key=>$value){
	$keyComponents = explode('_', $key);
	if ($keyComponents[0]==='namespace'){
		//Do something with it - 'cause this is one of the "namespace" fields!
	}
}

Now we need a plan to iterate over the sublevels, creating unset subcomponents as arrays, and setting the final component as the value. As we drop down into these sublevels, we will need a way to keep track of where in the variable we should be. If we are going to allow this array to have unlimited nesting, then we are going to have to do this with a second variable, which is a pointer to the current spot in the keyComponents we are processing. Let’s call this pointer “mySpot” for now.

$namespace = [];
foreach ($_POST as $key => $value){
	$keyComponents = explode('_', $key);
	if ($keyComponents[0] === 'namespace') {
		$keyComponentCount = count($keyComponents) - 1;
		$mySpot = &$namespace;
		foreach ($keyComponents as $keyComponentDepth=>$keyComponent){
			if ($keyComponentDepth > 0){ //skip the namespace
				//ToDo
			}
		}
	}
}

As we iterate over the keyComponents, we need to skip the first one as it is the namespace. For everything else we can just check to see if we are at the end of the keyComponents (by comparing the depth to the count) and either set the subarray (at mySpot) to an array or the value.

$namespace = [];
foreach ($_POST as $key => $value){
	$keyComponents = explode('_', $key);
	if ($keyComponents[0] === 'namespace') {
		$keyComponentCount = count($keyComponents) - 1;
		$mySpot = &$namespace;
		foreach ($keyComponents as $keyComponentDepth=>$keyComponent){
			if ($keyComponentDepth > 0){ //skip the namespace
				if (!isset($mySpot[$keyComponent])){
					if ($keyComponentCount == $keyComponentDepth) { //the last one!
						$mySpot[$keyComponent] = $value;
					} else {
						$mySpot[$keyComponent] = [];
					}
				}
				$mySpot = &$mySpot[$keyComponent];
			}
		}
	}
}

Each time we go a level deeper, we set mySpot to the new current level so the next iteration tacks on to the existing array. Technically this is not a recursive function as it is not a function at all and doesn’t call itself for each level down. I find this style of recursive programming to be slightly more readable than true recursive functions, but I’m sure that is just a preference! Hopefully this technique will help you to find your own way to dig into recursive processing! Let me know your thoughts in the comments below!


Spread the news

Leave a Reply

Your email address will not be published. Required fields are marked *