When developing not trivial application it often happens that we need to make some data transformations. Let’s assume we we have user and address and we create API and want to data come in the following format:
1 2 3 4 5 6 7 8 9 |
[ 'name' => 'John', 'surname' => 'Doe', 'address' => [ 'street' => 'Street', 'zipcode' => '23123', 'city' => 'Some city', ] ] |
but in database we don’t want to create separate table and put address fields in user table as address_street, address_zipcode, address_city.
Obviously in such simple case we can transform data manually, but what in case we want to do it automatically? Well, we can use Laravel to help us to achieve that – collections and Laravel helper functions will help us to solve this easily.
Data transformation
To not handle just simple case, let’s assume we might have multidimensional array and for the following array:
1 2 3 4 5 6 7 8 9 10 11 12 |
$input = [ 'a' => 1, 'b' => 2, 'c' => [ 'd' => 3, 'e' => 4, 'f' => [ 'g' => 5, 'h' => 6, ], ], ]; |
we want to get the following result:
1 2 3 4 5 6 7 8 |
$output = [ 'a' => 1, 'b' => 2, 'c_d' => 3, 'c_e' => 4, 'c_f_g' => 5, 'c_f_h' => 6, ] |
We could now think a while and create some recursive functions or use array_walk
PHP function but in fact there’s no need to do that if we use Laravel.
In fact if we look at Laravel helpers documentation we will find method that is close enough for start.
If we use
1 |
$output = array_dot($input); |
we will get as result
1 2 3 4 5 6 7 8 |
[ 'a' => 1, 'b' => 2, 'c.d' => 3, 'c.e' => 4, 'c.f.g' => 5, 'c.f.h' => 6, ] |
what is really close enough to what we want to achieve. So now we can either use foreach loop to change dots into underscores or we can use some magic of Laravel collections to get the desired output. The method we need here is keyBy of Laravel collections that will help use to modify collection keys.
If we use:
1 2 3 |
$output = collect(array_dot($input))->keyBy(function ($value, $key) { return str_replace('.', '_', $key); })->all(); |
as result we will get now:
1 2 3 4 5 6 7 8 |
[ 'a' => 1, 'b' => 2, 'c_d' => 3, 'c_e' => 4, 'c_f_g' => 5, 'c_f_h' => 6, ] |
what is exactly we need. All that we achieved in 3 lines of code using Laravel magic.
Opposite data transformation
Let’s now assume, we also need opposite transformation. Usually when we create one transformation we need to create also opposite transformation. How can we handle this? Well, there’s no array_dot
opposite function in Laravel, but there is array_set function that will again help us to achieve that.
At the moment our input is
1 2 3 4 5 6 7 8 |
$input = [ 'a' => 1, 'b' => 2, 'c_d' => 3, 'c_e' => 4, 'c_f_g' => 5, 'c_f_h' => 6, ]; |
If we want to use array_set
method, we will need to transform keys again to dot notation. We can again to do it using foreach, but I prefer collection method so we can use:
1 2 3 |
collect($input)->keyBy(function ($value, $key) { return str_replace('_', '.', $key); }) |
Now we have collection of items but we need to use array_set for each element of this collection so we can write it like this:
1 2 3 4 5 6 7 |
$output = []; collect($input)->keyBy(function ($value, $key) { return str_replace('_', '.', $key); })->each(function ($value, $key) use (&$output) { array_set($output, $key, $value); }); |
Not as short as first transformation, but it’s still only 6 lines of code.
Sum up
Now think about those transformations and code you need to write manually to handle those 2 transformations. Using built-in Laravel function we used 9 lines of codes for both transformations. Probably doing it manually would take 18 lines or more of code that is not as legible as this shown above. So it’s obvious it’s very useful to know what Laravel can offer because instead of writing very complex code you can focus on your real application code and not duplicating code that already exists.
Obviously, in above case if you really used those methods to transform data into database, you should consider what would happen if address had field for example zip_code instead of zipcode. It would obviously cause problems, because zip_code in address would be transformed into address_zip_code (what’s fine) but when using opposite transformation it would be transformed into:
1 2 3 4 5 |
'address' => [ 'zip' => [ 'code' => 123 ] ] |
so probably in this case using dot notation would be much safer because underscores are often used in fields names (and there won’t be need to use keyBy method so code would be shorter)