PHP Arrays - The php array functions
Source: Xavier33300 on Flickr.com |
Collection to select form key-values
Many of the website registration forms uses a country select element or an autocomplete input field using data from a database.If this was a single PHP with HTML combined page, there was no problem as you could easily reuse the query made to collect the data. But when using an MVC approach where your controller needs to push the data it fetches from the backend to the form before the form can be passed to the view you're in for a treat. We're addressing the issues for an MVC approach here.
Let's look at our country table first to get an impression how data is being stored in the database.
+-----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| iso | char(2) | NO | | NULL | |
| name | varchar(80) | NO | | NULL | |
| nicename | varchar(80) | NO | | NULL | |
| iso3 | char(3) | YES | | NULL | |
| numcode | smallint(6) | YES | | NULL | |
| phonecode | int(5) | NO | | NULL | |
+-----------+-------------+------+-----+---------+----------------+
In order to use this data in our forms, the two fields "iso" and "printable_name" are most likely the fields that would make the most sense.
Two approaches we can take here:
- we query our database for only those fields we need, with the requirement that we need to make a second database call when we need to populate our select field for country phone prefixes
- we query all of our required fields "iso", "printable_name" and "phonecode" in one query and use PHP array functions to populate our select fields
We choose to take option 2.
Let's have a look on how most people do things nowadays. The first thing they do is retrieve the data from their backend in the controller. In the controller they prepare their form and pass the form back to the view.
public function registerAction()
{
$form = $this->getForm('member');
$countryMapper = new CountryMapper();
$countryList = $countryMapper->fetchAll();
$countrySelect = [];
$phoneSelect = [];
foreach ($countryList as $country) {
$countrySelect[] = [
'iso' => $country['iso'],
'nicename' => $country['nicename'],
];
$phoneSelect[] = [
'phonecode' => $country['phonecode'],
'nicename' => $country['nicename'],
];
}
$form->getElement('country')->addMultiOptions($countrySelect);
$form->getElement('phoneprefix')->addMultiOptions($phoneSelect);
if (isset ($this->session->memberForm)) {
$form = unserialize($this->session->memberForm);
unset ($this->session->memberForm);
}
return [
'memberForm' => $form,
];
}
{
$form = $this->getForm('member');
$countryMapper = new CountryMapper();
$countryList = $countryMapper->fetchAll();
$countrySelect = [];
$phoneSelect = [];
foreach ($countryList as $country) {
$countrySelect[] = [
'iso' => $country['iso'],
'nicename' => $country['nicename'],
];
$phoneSelect[] = [
'phonecode' => $country['phonecode'],
'nicename' => $country['nicename'],
];
}
$form->getElement('country')->addMultiOptions($countrySelect);
$form->getElement('phoneprefix')->addMultiOptions($phoneSelect);
if (isset ($this->session->memberForm)) {
$form = unserialize($this->session->memberForm);
unset ($this->session->memberForm);
}
return [
'memberForm' => $form,
];
}
So, within the controller a loop is created to populate two field elements "country" and "phoneprefix". Even though this is an easy approach and works in most cases, the moment you have a lot of data to process, this kind of approach is slowing down your application. Say hello to two power functions: "array_walk" and "array_intersect_key"!
PHP function "array_walk" allows you to apply user functionality to each element of that array. This means we can apply our second functionality "array_intersect_key" on each row.
PHP function "array_intersect_key" gives us the ability to take the common elements from our country list row and the keys we provide as second argument.
$countrySelect = [];
array_walk($countryList, function ($row, $key) use (&$countrySelect) {
$countrySelect[] = array_intersect_key(
$row,
['iso' => null, 'nicename' => null]
);
});
$countrySelect[] = array_intersect_key(
$row,
['iso' => null, 'nicename' => null]
);
});
$phoneSelect = [];
array_walk($countryList, function ($row, $key) use (&$phoneSelect) {
$phoneSelect[] = array_intersect_key(
$row,
['iso' => null, 'phonecode' => null]
);
});
$phoneSelect[] = array_intersect_key(
$row,
['iso' => null, 'phonecode' => null]
);
});
Now we have two arrays that contain only the elements we can just pass on to the form select elements. And the whole controller action now looks like this:
public function registerAction()
{
$form = $this->getForm('member');
$countryMapper = new CountryMapper();
$countryList = $countryMapper->fetchAll();
$countrySelect = [];
array_walk($countryList, function ($row, $key) use (&$countrySelect) {
$countrySelect[] = array_intersect_key(
$row,
['iso' => null, 'nicename' => null]
);
});
{
$form = $this->getForm('member');
$countryMapper = new CountryMapper();
$countryList = $countryMapper->fetchAll();
$countrySelect = [];
array_walk($countryList, function ($row, $key) use (&$countrySelect) {
$countrySelect[] = array_intersect_key(
$row,
['iso' => null, 'nicename' => null]
);
});
$phoneSelect = [];
array_walk($countryList, function ($row, $key) use (&$phoneSelect) {
$phoneSelect[] = array_intersect_key(
$row,
['iso' => null, 'phonecode' => null]
);
});
$form->getElement('country')->addMultiOptions($countrySelect);
$form->getElement('phoneprefix')->addMultiOptions($phoneSelect);
if (isset ($this->session->memberForm)) {
$form = unserialize($this->session->memberForm);
unset ($this->session->memberForm);
}
return [
'memberForm' => $form,
];
}
Finding primary keys from a collection
Another common pattern I see is when developers need to pull ID's from a collection of elements to pass on to a next request. Again, foreach is the common used loop to iterate over each element, adding the ID property to another array which then gets passed on to the query.
Let's first look our code that we're going to process.
class PhpFunction {
public $id;
public $label;
public function __construct($id, $label)
{
$this->id = $id;
$this->label = $label;
}
} $functions = [
new PhpFunction(123, 'array_map'),
new PhpFunction(124, 'array_merge_recursive'),
new PhpFunction(125, 'array_merge'),
];
public $id;
public $label;
public function __construct($id, $label)
{
$this->id = $id;
$this->label = $label;
}
} $functions = [
new PhpFunction(123, 'array_map'),
new PhpFunction(124, 'array_merge_recursive'),
new PhpFunction(125, 'array_merge'),
];
Now this example is only containing 3 elements, but when you look at the php.net website there are thousands of functions, so consider this is a huge collection of php functions.
In many cases I see something like the following code to retrieve the ID's from this collection:
$functionIds = [];
foreach ($functions as $function) {
$functionIds[] = $function->id;
}
foreach ($functions as $function) {
$functionIds[] = $function->id;
}
Even though this is a common pattern, I would prefer to use a native PHP function for processing this. Say hello to "array_map", the function that allows you to use a custom functionality on each element of the given array.
$functionIds = array_map(function ($element) {
return $element->id;
}, $functions);
return $element->id;
}, $functions);
The result is exactly the same, but now using PHP's native C power processing huge arrays goes a bit faster than the "foreach"-loop.
Next article we're looking at more common patterns for processing arrays. Love to see you again.
Comments
Post a Comment