Pages

2016/02/06

PHP Arrays - The php array functions

Source: Xavier33300 on Flickr.com
In my previous article about arrays (the basicssimple operations and associative arrays or hash maps) I have shown what arrays are and what you can do with it. Now I want to dive into real fun and explain PHP's array functions with real-world examples and how you can apply them in your day-to-day work.

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:

  1. 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
  2. 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,
    ];
}

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]
    );
});

$phoneSelect = [];
array_walk($countryList, function ($row$key) use (&$phoneSelect) {
    
$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]
        );
    }); 

    
$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'),
];

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;
}

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);

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.