This article mostly mentions working with JSON
data, but the helpers we will create, are also very useful when you want to convert between stdClass
objects and Arrays
.
The syntax for handling JSON data in php can feel overwhelming. On the other hand, indexed php arrays aren't nice on your keyboard either.
In applications where you have to interact with a lot of JSON there are some really nice packages that will help, but sometimes you don't want to install packages, just to save some keystrokes.
Laravel helpers
Working with data objects doesn’t have to be painful. Laravel has some nifty helpers that does the heavy lifting for you.
Important note: The Laravel helpers are fantastic, they never return any error if the property doesn't exist, they should be your first choice!
Install a package
In a JSON heavy application you’ll want to install packages to help you out. If you choose this path you can ignore the rest of this article :)
My favourites:
Create your own helpers
So, you ended up with an edge case where you don't want to use data_get($array, 'foo.bar.baz')
or $array[0][1][3]
. You want to use the familiar foo->bar->baz
arrow accessor. Ok, here you go:
We are going to create a few helpers:
I like to "camelCase" method names because when I read the code I understand that it is a custom method.
-
arrToObj($array): mixed
-
objToArr(arr|object $object): mixed
-
toJsonObj(arr|object $object): string
-
toJsonNumCheck(arr|object $object): string
-
toJsonArr(arr|object $object): string
-
jsonToPhpArr(string $json): mixed
-
jsonToPhpObj(string $json): mixed
Why not use php type casting?
The methods we are creating are recursive, php (object)
/(array)
casting is not. They will not work if you have a multidimensional array.
Associative Array example:
$address = [
'street' => 'My street',
'zip' => 12345,
'phones' => [
'home' => '561-999-9999',
'cell' => '561-555-5555',
'office' => [
'number' => '561-777-7777',
'extension' => '100',
],
],
];
Php type casting
$object = (object)$address;
/*
As you can see, the nested arrays are not converted to objects.
{
"street": "My street",
"zip": 12345,
"phones": [
"home" => "561-999-9999",
"cell" => "561-555-5555",
"office" => [
"number" => "561-777-7777",
"extension" => "100",
],
],
}
*/
Access array
//default php
$extension = $address['phones']['office']['extension'];
//Laravel helper
$extension = data_get($address, 'phones.office.extension');
//Custom helper two lines
$obj = arrToObj($address);
$extension = $obj->phones->office->extension;
//Custom helper inline
$extension = arrToObj($address)->phones->office->extension;
Update array
//default php
$address['phones']['office']['number'] = '561-223';
//Laravel helper
data_set($address, 'phones.office.number', '561-222');
//Custom helper
$obj = arrToObj($address);
$obj->phones->office->number = '561-224';
Multidimensional Arrays to JSON conversion
$cars = array (
array("Volvo",22,18),
array("BMW",15,13),
array("Saab",5,2),
array("Land Rover",17,15)
);
Convert Array to encoded JSON array.
This one is a bit silly, it's the same as
json_encode($cars, true)
, but I keep forgetting if the $flag
should be true or false, so I use the helper instead.
$jsonArr = toJsonArr($cars);
[
[ "Volvo", 22, 18, ],
[ "BMW", 15, 13, ],
[ "Saab", 5, 2, ],
[ "Land Rover", 17, 15, ],
]
Convert Array to encoded JSON object
$cars = array (
array('Brand' => 'Volvo', 'foo' => 22, 'bar' => 18),
array('Brand' => 'BMW', 'foo' => 12, 'bar' => 8),
array('Brand' => 'Saab', 'foo' => 5, 'bar' => 2),
array('Brand' => 'Land Rover', 'foo' => 17, 'bar' => 15)
);
$jsonObj = toJsonObj($cars);
{
"0": {
"Brand": "Volvo",
"foo": 22,
"bar": 18,
},
"1": {
"Brand": "BMW",
"foo": 12,
"bar": 8,
},
"2": {
"Brand": "Saab",
"foo": 5,
"bar": 2,
},
"3": {
"Brand": "Land Rover",
"foo": 17,
"bar": 15,
},
}
Convert numeric strings to json numbers. Keep your eye on string-num
, it is converted to a "real" number.
$cars = array (
array('Brand' => 'Volvo', 'string-num' => "17", 'num' => 15)
);
$object = toJsonNumCheck($cars);
[
{
"Brand": "Volvo",
"string-num": 17,
"bar": 15,
},
]
Caveats, warning!
1. Keys must exist
Don't use this helpers to access or update data unless you're absolutely sure that the key=>value
pairs exist in the array/json
data. Otherwise use Laravel data_get()/data_set()
2. Warning, indexed arrays
If you're eager to use the ->
array syntax with indexed arrays. Be careful not to end up with a much harder syntax than you bargained for.
$obj = arrToObj(array("Volvo", "BMW", "Toyota"));
//returns
{
"0": "Volvo",
"1": "BMW",
"2": "Toyota",
}
//ouch, painful syntax ;)
$obj->{'0'} //"Volvo"
Working with Eloquent Models
You don't have much use of the helpers when working with Eloquent Models. Laravel allows you to use the arrow syntax directly.
The only thing to keep in mind is how to handle multidimensional arrays in json columns.
Problem: Setting nested values will overwrite any values not specified
return $user->phone_numbers;
/* [
"home" => "561-999-9999"
"cell" => "561-555-5555",
"office" => [
"number" => "561-777-7777",
"extension" => "100"
],
] */
$user->update([
'phone_numbers' => [
'cell' => '222-222-2222',
'international' => '+358 50 555 55 55',
],
]);
return $user->phone_numbers;
/* [
"cell" => "222-222-2222",
"international" => "+358 50 555 55 55"
] */
Solution 1: specify full paths in key-value pairs when updating the model.
$user->update([
'phone_numbers->cell' => '222-222-2222',
'phone_numbers->international' => '+358 50 555 55 55',
]);
return $user->phone_numbers;
/* [
"home" => "561-999-9999"
"cell" => "222-222-2222",
"office" => [
"number" => "561-777-7777",
"extension" => "100"
],
"international" => "+358 50 555 55 55"
] */
Solution 2: Use Laravel helpers.
$user = User::first();
data_set($user, 'phone_numbers.cell', '222-222-2222');
data_set($user, 'phone_numbers.international', '+358 50 555 55 55');
$user->save();
If you want to read more about how to handle Eloquent Model, json columns
The helper file
Create ArrayJsonHelper
in App/Helpers
, or a directory of your choice.
<?php
if (!function_exists('arrToObj')) {
/**
* Recursively convert php Array to stdClass Object
* @param array $array
* @return mixed
*/
function arrToObj(array $array = []): mixed
{
return json_decode(json_encode($array, JSON_FORCE_OBJECT));
}
}
if (!function_exists('objToArr')) {
/**
* Recursively convert stdClass Object or Array with nested Objects, to php array
* @param array|object $object
* @return mixed
*/
function objToArr(array|object $object): mixed
{
return json_decode(json_encode($object), true);
}
}
if (!function_exists('toJsonObj')) {
/**
* Recursively convert php stdClass Object or Array to JSON object
* @param array|object $object
* @return string
*/
function toJsonObj(array|object $object): string
{
return json_encode($object, JSON_FORCE_OBJECT);
}
}
if (!function_exists('toJsonNumCheck')) {
/**
* Recursively convert php stdClass Object or Array to JSON object
* <br>and encodes numeric strings as numbers
* @param array|object $object
* @return string
*/
function toJsonNumCheck(array|object $object): string
{
return json_encode($object, JSON_NUMERIC_CHECK);
}
}
if (!function_exists('toJsonArr')) {
/**
* Recursively convert php stdClass Object or Array to JSON array
* @param array|object $object
* @return string
*/
function toJsonArr(array|object $object): string
{
return json_encode($object, true);
}
}
if (!function_exists('jsonToPhpArr')) {
/**
* Recursively convert serialized JSON to php Array
* @param string $json
* @return mixed
*/
function jsonToPhpArr(string $json): mixed
{
return json_decode($json, true);
}
}
if (!function_exists('jsonToPhpObj')) {
/**
* Convert JSON to stdClass Object, deep
* @param string $json
* @return mixed
*/
function jsonToPhpObj(string $json): mixed
{
return json_decode($json, false);
}
}
Register the helpers in composer.json
"autoload":
"psr-4": {...},
//other stuff
"files": [
"app/Helpers/ArrayJsonHelper.php"
]
},
Create more helpers using available php json constants
Happy coding // Tina :)
Comments (0)