I have a JSON file like that
{
"20":{
"0":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
},
"1":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
},
"2":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
},
"3":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
},
"4":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
},
"5":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
},
"6":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
}
},
"21":{
"0":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
},
"1":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
},
"2":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
},
"3":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
},
"4":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
},
"5":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
},
"6":{
"period":[
{
"open": 350,
"close": 600
},
{
"open": 660,
"close": 900
}
]
}
}
and I have an action
that is is decoding this JSON
to array, and pass with foreach
-es step by step, get that data and then store it in Database.
One guy said me that it is possible to refactor all that action, so I will not have absolutely (or approximative) any foreach or if. Also he said that it is called representative/functional
programming.
So, I found that concept and all that stuff, but also can't figure out how to do it. My imperial
code:
$processingFile = file_get_contents(storage_path('hours.txt'));
$decodedFile = json_decode($processingFile, true);
$data = [];
$i = 0;
$batch = 10000;
foreach ($decodedFile as $business => $days) {
foreach ($days as $dayOfWeek => $periods) {
if (count($periods)) {
foreach ($periods['period'] as $key => $value) {
$i++;
$tmp = [
'business_id' => $business,
'day_of_week' => $dayOfWeek,
'open_periods_mins' => $value['open'],
'close_periods_mins' => $value['close'],
];
array_push($data, $tmp);
if($i === $batch) {
BusinessHour::insert($data);
$data = [];
$i = 0;
}
}
}
}
}
if( count($data) ) {
BusinessHour::insert($data);
}
I don't know how to parse
step by step and cut it all in functions using Laravel Collections
or whatever... declarative paradigm
.
Can someone explain / rewrite that code for teaching purpose? Thanks!
I am not sure if there are any universal ways that are applicable to your case, given the way you manipulate the information into the final array is not universal (for example, neither the string "period" nor the numerical keys in the period array is not used anywhere in the final output while the other keys are used, and the final two child elements are combined into one record, etc).
Here is a bit techy way to prepare $data
in your example, where the way in this answer to "PHP convert nested array to single array while concatenating keys?" is adopted. With this, it uses only one foreach
(I think one-level loop is inevitable given the fiinal conversion is non-universal). Note it assumes the input JSON has no irregular structures.
$string = http_build_query($decodedFile);
$string = urldecode($string);
$string = str_replace(
array('[',']'),
array('_','') ,
$string
);
parse_str($string, $flat_array);
$data = [];
$tmp = [];
foreach ($flat_array as $ky => $val) {
$ary = explode('_', $ky);
$tmp[$ary[4] . '_periods_mins'] = $val;
if ($ary[4] == 'close') {
array_push($data, $tmp);
$tmp = [];
continue;
}
$tmp['business_id'] = $ary[0];
$tmp['day_of_week'] = $ary[1];
}
You are able to achieve this using the collect() helper, which will add an array to a collection instance and allow you to use its methods.
$processingFile = file_get_contents(storage_path('hours.txt'));
$data = json_decode($processingFile, true);
$insertData = [];
collect($data)
->each(function ($business, $businessKey) use (&$insertData) {
collect($business)
->each(function ($day, $dayKey) use ($businessKey, &$insertData) {
foreach ($day['period'] as $period) {
$insertData[] = [
'business_id' => $businessKey,
'days_of_week' => $dayKey,
'open_periods_mins' => $period['open'],
'close_periods_mins' => $period['close'],
];
}
});
});
if (count($insertData)) {
BusinessHour::insert($insertData);
}
Firstly, we get the $processingFile
and decode it to a $data
variable. $insertData
is created as a new empty array which will be used for later.
Then then wrap the $data
variable in a collect()
helper. For each business
we pass through the reference $insertData
variable. This is needed in order to update the variable outside of the collection closures.
Within each business
, we have days
so we collect $business
(which is actually days
) and for each day
, pass through the $businessKey
and the reference of the $insertData
variable.
After this, we use a normal foreach
to update the $insertData
array with new data.
At the end of the process, you then insert()
all data into records on the BusinessHour
model.
I hope this helps.