【问题标题】:Refactor code from foreach to Laravel Collections将代码从 foreach 重构为 Laravel 集合
【发布时间】:2019-03-22 08:24:09
【问题描述】:

我有一个这样的 JSON 文件

{
    "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
                }
            ]
        }
    }

我有一个action 正在将这个JSON 解码为数组,并逐步传递foreach-es,获取该数据,然后将其存储在数据库中。

有人说我可以重构所有这些动作,所以我不会绝对(或近似)任何 foreach 或 if。他还说叫representative/functional编程。

所以,我找到了这个概念和所有这些东西,但也不知道该怎么做。 我的imperial 代码:

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

我不知道如何一步一步 parse 并使用 Laravel Collections 或其他任何东西将其全部剪切到函数中...declarative paradigm

有人可以出于教学目的解释/重写该代码吗? 谢谢!

【问题讨论】:

  • 不应该将 json 包裹在 20 以上的数组中,因为 20、21 等是数组的多个对象吗?

标签: php laravel collections


【解决方案1】:

我不确定是否有任何通用方法适用于您的情况,因为您将信息操作到最终数组的方式不是通用的(例如,字符串“句点”和句点 数组在最终输出的任何地方都没有使用,而其他键被使用,最后两个子元素组合成一个记录,等等)。

在您的示例中,这是一种准备 $data 的技术方法,其中 this answer 中的方式是“PHP 在连接键时将嵌套数组转换为单个数组?”被采纳。 有了这个,它只使用一个foreach(我认为一级循环是不可避免的,因为最终的转换是非通用的)。请注意,它假定输入 JSON 没有不规则结构。

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

【讨论】:

    【解决方案2】:

    您可以使用collect() 帮助器来实现此目的,它会将数组添加到集合实例并允许您使用其方法。

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

    首先,我们获取$processingFile 并将其解码为$data 变量。 $insertData 被创建为一个新的空数组,供以后使用。

    然后将$data 变量包装在collect() 帮助器中。对于每个business,我们通过引用$insertData 变量。这是为了在集合闭包之外更新变量所必需的。

    在每个business 中,我们有days,因此我们收集$business(实际上是days)并且对于每个day,传递$businessKey$insertData 变量的引用.

    之后,我们使用普通的foreach 用新数据更新$insertData 数组。

    在该过程结束时,您随后将insert() 的所有数据写入BusinessHour 模型上的记录中。

    我希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      Laravel 集合在这种情况下非常有帮助,您可以将代码分解成多个部分,使其可读,同时获得函数式编程外观。

          $data = file_get_contents(storage_path('hours.txt'));
          $data = collect(json_decode($data, true));
          $batch = 5;
          $data = $this->initMap($data);
          $data = $this->finMap($data, $data->count(), $batch);
          if ($data->count() > 0){
            BusinessHour::insert($data->toArray());
          };
      

      这是最初的地图功能,它只是准备所有必要的数据,以便以后处理。

          public function initMap($data){
              return $data->flatmap(function($data, $bId) {
                  return collect($data)
                  ->map(function($data, $day) use ($bId) {
                      return collect([
                          'business_id' => $bId,
                          'day_of_week' => $day,
                          'open_periods_mins' => $data['period'][0]['open'],
                          'close_periods_mins' => $data['period'][0]['close']
                      ]);
                  });
              });
          }
      

      这是数据的最终确定。此计算是否需要根据 $batch 变量保存数据,如果不需要,剩余数据仍将在最后存储。

      PS:代码基于OP初始代码,我使用laravel 8。

        public function finMap($data, $total, $batch){
              $this->insertData = [];
              return $data->map(function($data, $index) use ($total, $batch) {
                  if (($total - $index) > ($total % $batch)){
                      $this->insertData[] = collect($data);
                      if (($index % $batch) == 0){
                          BusinessHour::insert($this->insertData);
                      }
                  }else{
                      return $data;
                  }
              })->filter(function($data){
                  return $data;
              });
          }
      

      【讨论】:

        猜你喜欢
        • 2018-11-18
        • 1970-01-01
        • 1970-01-01
        • 2017-10-27
        • 2019-08-21
        • 2013-01-10
        • 2016-02-19
        • 2014-03-12
        • 1970-01-01
        相关资源
        最近更新 更多