首页 > 解决方案 > Laravel - 如何采摘嵌套集合?

问题描述

好的,所以我正在构建一个记录一年中每个月的时间条目的表。我有一个名为 $group_months 的变量,如果我转储数据,我会得到:

Collection {#189 ▼
  #items: array:4 [▼
    "Comp Time Used" => Collection {#294 ▼
      #items: array:3 [▼
        10 => "12:00:00"
        11 => "09:00:00"
        "01" => "12:00:00"
      ]
    }
    "Vacation Time Used" => Collection {#324 ▼
      #items: array:1 [▼
        11 => "04:00:00"
      ]
    }
    "Sick Time" => Collection {#327 ▼
      #items: array:1 [▼
        10 => "03:15:00"
      ]
    }
    "OT Accrued" => Collection {#318 ▼
      #items: array:1 [▼
        10 => "12:00:00"
      ]
    }
  ]
}

我希望能够提取每个集合的标题并将它们用作我的表格的列。因此,我想提取“使用的比较时间”以将其用作表中的一列。然后我想取出“12:00:00”并将其用于我表中的 10 月行。(10 = 10 月)

表视图:

<table class="table table-striped table-sm">
                <thead>
                  <tr>
                    <th scope="col">Month</th>
                    <th scope="col">Overtime Hours</th>
                    <th scope="col">Compensation Hours</th>
                    <th scope="col">Vacation</th>
                    <th scope="col">Personal Hours</th>
                    <th scope="col">Sick Hours</th>
                  </tr>
                </thead>
                <tbody>
                  <tr>
                    <th scope="row">Jan</th>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td>
                  </tr>
                  <tr>
                    <th scope="row">Feb</th>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                  </tr>
                  <tr>
                    <th scope="row">Mar</th>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                  </tr>
                  <tr>
                    <th scope="row">Apr</th>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                  </tr>
                  <tr>
                    <th scope="row">May</th>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                  </tr>
                  <tr>
                    <th scope="row">Jun</th>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                  </tr>
                  <tr>
                    <th scope="row">Jul</th>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                  </tr>
                  <tr>
                    <th scope="row">Aug</th>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                 </tr>
                 <tr>
                    <th scope="row">Sep</th>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <th scope="row">Oct</th>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <th scope="row">Nov</th>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                <tr>
                    <th scope="row">Dec</th>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                </tbody>
     </table>

时间迁移:

 Schema::create('times', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id');
            $table->date('start_day');
            $table->text('category');
            $table->time('start_time');
            $table->time('finish_time');
            $table->time('duration');
            $table->text('notes');
            $table->timestamps();

            $table->foreign('user_id')->references('id')->on('users');

        });

表控制器:

 public function show(User $user) 
    {

        $data = $user->times()->whereYear('start_day', 2019)->get();
        $group_months = $data->groupBy(function($entry) {
            return $entry->category;
        })->map(function($items) {
            return $items->groupBy(function($entry) {
                return Carbon::parse($entry->start_day)->format('m');
            })
        ->map(function($items) {
            $duration = $items->first()->duration;
            return $duration;
            });
        });

 return view('table.show', compact('data','group_months'));

标签: phparrayslaravelcollections

解决方案


您当前正在查询所有数据,并将其分组在内存中,这并不是很有效。相反,您应该尽可能多地推送到数据库。这也意味着只查询相关数据categorymonthsum(duration)

public function show(User $user) 
{
    $data = $user->times()
        ->whereYear('start_day', 2019)
        ->groupBy('category', \DB::raw('month(start_date)'))
        ->select([
            'category',
            \DB::raw('month(start_date) as month'),
            \DB::raw('sum(duration) as duration'),
        ])
        ->orderBy(\DB::raw('month(start_date)'))
        ->get()
        ->mapToGroups(function ($item) {
            $month = \DateTime::createFromFormat('!m', $item['month'])->format('M');
            return [$month => [$item['category'] => $this->formatDuration($item['duration'])]];
        })
        ->mapWithKeys(function ($item, $key) {
            return [$key => $item->collapse()];
        });

    return view('table.show', compact('data'));
}

private function formatDuration($seconds)
{
    $duration = '';

    if ($seconds < 0) {
        $duration = '-';
        $seconds  = abs($seconds);
    }

    $hours    = floor($seconds / 3600);
    $seconds -= $hours * 3600;
    $minutes  = floor($seconds / 60);
    $seconds -= $minutes * 60;

    return $duration . sprintf('%d:%02d:%02d', $hours, $minutes, $seconds);
}

此时,$data包含以下结构:

[
    'Jan' => [
        'Category 1' => '1:20:40',
        'Category 2' => '15:05:40',
        'Category 4' => '0:00:50'
    ],
    'Feb' => [
        'Category 2' => '2:30:15',
        'Category 3' => '4:45:30'
    ]
]

我们可以使用这些数据以通用方式生成我们的表,使用数组索引作为月份编号。

<table class="table table-striped table-sm">
    <thead>
        <tr>
            <th scope="col">Month</th>
            <th scope="col">Overtime Hours</th>
            <th scope="col">Compensation Hours</th>
            <th scope="col">Vacation</th>
            <th scope="col">Personal Hours</th>
            <th scope="col">Sick Hours</th>
        </tr>
    </thead>
    <tbody>
        @foreach($data as $month => $row)
            <tr>
                <th scope="row">{{ $month }}</th>
                <td>{{ $row['Overtime Hours'] ?? '-' }}</td>
                <td>{{ $row['Compensation Hours'] ?? '-' }}</td>
                <td>{{ $row['Vacation'] ?? '-' }}</td>
                <td>{{ $row['Personal Hours'] ?? '-' }}</td>
                <td>{{ $row['Sick Hours'] ?? '-' }}</td>
            </tr>
        @endforeach
    </tbody>
</table>

如果您希望每个月都显示在表中,即使该月没有可用数据,您也可以使用array_fill(1, 12, [])as base 将缺失的索引添加到集合中:

public function show(User $user) 
{
    $data = collect(array_fill(1, 12, []))
        ->replace(
            $user->times()
                ->whereYear('start_day', 2019)
                ->groupBy('category', \DB::raw('month(start_date)'))
                ->select([
                    'category',
                    \DB::raw('month(start_date) as month'),
                    \DB::raw('sum(duration) as duration'),
                ])
                ->orderBy(\DB::raw('month(start_date)'))
                ->get()
                ->mapToGroups(function ($item) {
                    return [$item['month'] => [
                        $item['category'] => $this->formatDuration($item['duration'])
                    ]];
                })
                ->mapWithKeys(function ($item, $key) {
                    return [$key => $item->collapse()];
                })
        )
        ->mapToGroups(function ($item, $key) {
            $month = \DateTime::createFromFormat('!m', $key)->format('M');
            return [$month => $item];
        });

    return view('table.show', compact('data'));
}

推荐阅读