Lesson 7 - Finishing ArrayUtils library for PHP
In the previous lesson, ArrayUtils library for working with arrays in PHP, we started working on the ArrayUtils library which works with arrays in PHP. In today's tutorial, we'll add some more useful methods into the library and finish up with it.
Adding and removing key prefixes
We've already mentioned prefixes before. Imagine adding an address from a form into a database. As you already know, form-field names correspond with database table column names.
The HTML part
... Street: <input type="text" name="street" /><br /> Registry number: <input type="text" name="registry_number" /><br /> House number: <input type="text" name="house_number" /><br /> City: <input type="text" name="city" /><br /> ZIP: <input type="text" name="zip" /><br /> ...
The PHP part
$addressKeys = array('street', 'registry_number', 'house_number', 'city', 'zip'); $address = ArrayUtils::filterKeys($_POST, $addressKeys); Db::insert('address', $address);
However, sometimes data arrives in a more complex form, which contains multiple addresses - billing and shipping. To avoid key collisions and confusion, we'll prefix the shipping address with the "shipping_" prefix. Here's an example form:
The HTML part
... <h2>Billing address</h2> Street: <input type="text" name="street" /><br /> Registry number: <input type="text" name="registry_number" /><br /> House number: <input type="text" name="house_number" /><br /> City: <input type="text" name="city" /><br /> ZIP: <input type="text" name="zip" /><br /> ... <h2>Shipping address</h2> Street: <input type="text" name="shipping_street" /><br /> Registry number: <input type="text" name="shipping_registry_number" /><br /> House number: <input type="text" name="shipping_house_number" /><br /> City: <input type="text" name="shipping_city" /><br /> ZIP: <input type="text" name="shipping_zip" /><br /> ...
The PHP part
Here, we'll have to retrieve that part of $_POST without prefixes first. Then, we'll get the part with prefixes, remove the prefixes, and save both of the addresses into the "address" table. The code will be as follows:
$addressKeys = array('street', 'registry_number', 'house_number', 'city', 'zip'); $billingAddress = ArrayUtils::filterKeys($_POST, $addressKeys); // Retrieving the prefixed fields $shippingAddress = ArrayUtils::filterKeysPrefix('shipping_', $_POST); // Removing the prefix $shippingAddress = ArrayUtils::removePrefix('shipping_', $shippingAddress); // Filtering $shippingAddress = ArrayUtils::filterKeys($shippingAddress, $addressKeys); // Adding addresses into the database Db::insert('adresa', $billingAddress); Db::insert('adresa', $shippingAddress);
Here's the code for the addPrefix() and removePrefix() methods. Notice how they're implemented to work recursively.
public static function addPrefix($prefix, array $input) { $output = array(); foreach ($input as $key => $value) { $key = $prefix . $key; if (is_array($value)) $value = self::addPrefix($prefix, $value); $output[$key] = $value; } return $output; } public static function removePrefix($prefix, array $input) { $output = array(); foreach ($input as $key => $value) { if (strpos($key, $prefix) === 0) $key = substr($key, mb_strlen($prefix)); if (is_array($value)) $value = self::removePrefix($prefix, $value); $output[$key] = $value; } return $output; }
Converting between CamelCase and under_score notations
We usually name tables using the under_score notation in MySQL databases. In doing so, we avoid problems with uppercase/lowercase on Linux servers and PHPMyAdmin groups tables which use two underscores, e.g. user__address, user__bank_account, and so on. However, we use CamelCase notation in PHP. Occasionally, we'll need to convert an array using one notation to another, which is exactly what these two methods below do. We use the StringUtils library, which we've made in the previous lesson, for the conversions.
public static function camelToSnake($inputArray) { $outputArray = array(); foreach ($inputArray as $key => $value) { $key = StringUtils::camelToSnake($key); if (is_array($value)) $value = self::camelToSnake($value); $outputArray[$key] = $value; } return $outputArray; } public static function snakeToCamel($inputArray) { $outputArray = array(); foreach ($inputArray as $key => $value) { $key = StringUtils::snakeToCamel($key); if (is_array($value)) $value = self::snakeToCamel($value); $outputArray[$key] = $value; } return $outputArray; }
Exporting to XML
When communicating with external systems, we'll often work with JSON and XML formats. It'd be nice to be able to generate both these formats from an array. PHP provides really nice functions for generating arrays to and from JSON. Unfortunately, generating arrays for XML is not currently a part of PHP, which is why we'll add a method for it as well as conversion back to an array. Here's the code for the xmlEncode() and xmlEncodeElement() methods:
public static function xmlEncode(array $input, $root) { $doc = new DOMDocument('1.0', 'UTF-8'); $doc->formatOutput = true; $rootElement = $doc->createElement($root); $doc->appendChild($rootElement); self::xmlEncodeElement($input, $rootElement); return $doc->saveXML(); } private static function xmlEncodeElement(array $input, DOMElement $parent) { foreach ($input as $key => $value) { $element = $parent->ownerDocument->createElement($key); $parent->appendChild($element); if (is_array($value)) self::xmlEncodeElement($value, $element); else { $text = $parent->ownerDocument->createTextNode($value); $element->appendChild($text); } } }
The method receives an input array and the name of the root XML element. Then, it creates a new document and inserts the root element into it. Next, it calls the xmlEncodeElement() method, which receives an array and an element into which the array items should be inserted. We created the second method to facilitate recursion, since we may need to enable array nesting. For every one of the array's keys, we create an element and either insert it next to it or call the method recursively in case the value is an array.
Go ahead and try the method out:
$users = array( 'administrator' => array( 'user_id' => 1, 'name' => 'John Smith', 'email' => '[email protected]', ), 'editor' => array( 'user_id' => 2, 'name' => 'Jane Smith', 'email' => '[email protected]', ), ); echo ArrayUtils::xmlEncode($users, 'users');
Here's what the resulting XML should be:
<?xml version="1.0" encoding="UTF-8"?> <users> <administrator> <user_id>1</user_id> <name>John Smith</name> <email>[email protected]</email> </administrator> <editor> <user_id>2</user_id> <name>Jane Smith</name> <email>[email protected]</email> </editor> </users>
Note: When creating XML from an array, we're limited by the fact that each array key has to be unique. We can't create XML using an array, however, these methods can spare us a lot of work.
For the sake of completeness, let's also add the backward conversion. We'll use a small hack this time since the json_encode() function can accept a SimpleXMLElement instance. We'll convert the resulting JSON to an array using the json_decode() function:
public static function xmlDecode($xml) { $simpleXMLElement = simplexml_load_string($xml); $json = json_encode($simpleXMLElement); return json_decode($json, TRUE); }
That's it, we're done The finished, documented class is available for download in the attachment below, as always. In the next lesson, Building form framework for PHP - Motivation, we'll start programming a form framework.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 21x (6.4 kB)
Application includes source codes in language PHP