Laravel - Как собрать вложенную коллекцию? - PullRequest
0 голосов
/ 25 октября 2019

Хорошо, я создаю таблицу, в которой записываются записи времени для каждого месяца года. У меня есть переменная с именем $ 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"
      ]
    }
  ]
}

Я бы хотел иметь возможность выбрать заголовок каждой коллекции и использовать их в качестве столбцов для моей таблицы. Поэтому я хочу отключить "Comp Time Used", чтобы использовать его в качестве столбца в моей таблице. Затем я хочу вынуть «12:00:00» и использовать его для моей октябрьской строки в моей таблице. (10 = октябрь)

TableView:

<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>

Times Migration:

 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');

        });

TableController:

 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'));

1 Ответ

1 голос
/ 25 октября 2019

Вы в настоящее время запрашиваете все свои данные, группируете их в памяти, что на самом деле не совсем эффективно. Вместо этого вы должны как можно больше подтолкнуть к базе данных. Это также означает, что запрашиваются только соответствующие данные: category, month и sum(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, []) в качестве базы:

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'));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...