join('park_number as pn', 'p.park_id', '=', 'pn.park_id') ->join('ptype as pt', 'pn.ptype_id', '=', 'pt.ptype_id') ->select([ 'p.park_id', 'p.park_name', 'pt.ptype_id', 'pt.ptype_subject', // 限界収容台数 'pn.park_limit', // 現在収容台数 DB::raw('COALESCE(pn.park_number, 0) as current_count'), // 空き = 収容上限 - 現在 DB::raw('GREATEST(0, COALESCE(pn.park_limit, 0) - COALESCE(pn.park_number, 0)) as available'), // 利用率 = 現在 / 上限 * 100 DB::raw("CASE WHEN COALESCE(pn.park_limit, 0) > 0 THEN ROUND((COALESCE(pn.park_number, 0) / pn.park_limit) * 100, 1) ELSE 0 END as usage_rate"), ]) ->where('p.park_close_flag', '!=', 1); if (!empty($parkId)) { $query->where('p.park_id', $parkId); } $results = $query // 表示順:自転車 → 原付 → その他 ->orderByRaw("CASE pt.ptype_subject WHEN '自転車' THEN 1 WHEN '原付' THEN 2 ELSE 3 END") ->orderBy('p.park_name') ->get(); return $results; } /** * 駐輪場一覧を取得(選択用) */ public function getParkList(): Collection { return Park::select('park_id', 'park_name') ->where('park_close_flag', '!=', 1) ->orderBy('park_name') ->get(); } /** * 合計行の計算 * * @param Collection $stats * @return array{total_limit:int,total_current:int,total_available:int,total_usage_rate:float} */ public function calculateTotals(Collection $stats): array { $totalLimit = (int) $stats->sum('park_limit'); $totalCurrent = (int) $stats->sum('current_count'); $totalAvailable = $totalLimit - $totalCurrent; $totalUsageRate = $totalLimit > 0 ? round(($totalCurrent / $totalLimit * 100), 1) : 0.0; return [ 'total_limit' => $totalLimit, 'total_current' => $totalCurrent, 'total_available' => $totalAvailable, 'total_usage_rate' => $totalUsageRate, ]; } }