【区画別利用率状況】データ取得元修正
All checks were successful
Deploy main / deploy (push) Successful in 23s

This commit is contained in:
你的名字 2025-10-01 12:00:32 +09:00
parent 75bbec6f5a
commit 3d82536404
3 changed files with 64 additions and 114 deletions

View File

@ -38,76 +38,36 @@ class UsingStatusController extends Controller
public function index(Request $request, UsingStatusService $service)
{
try {
// CSRF トークンの自動検証Laravel 12標準機能
// リクエストパラメータの取得
// Laravel 12変更点$request->input()の使用を推奨
$parkId = $request->input('park_id', null);
$isSearchRequest = $request->has('search') || $request->isMethod('post');
// ログ出力(デバッグ用)
Log::info('区画別利用率状況ページアクセス', [
'park_id' => $parkId,
'is_search' => $isSearchRequest,
'method' => $request->method()
]);
// 駐輪場一覧の取得(選択用ドロップダウン)
$parkId = $request->input('park_id'); // GET/POST どちらでも取得
$parkList = $service->getParkList();
// 利用率統計データの取得
// Laravel 12変更点デフォルトで全データを表示ユーザー選択不要
$utilizationStats = $service->getUtilizationStats($parkId);
// データが空の場合の処理
if ($utilizationStats->isEmpty() && $parkId) {
// 指定された駐輪場のデータが見つからない場合
return redirect()->route('using_status')
->with('warning', '選択された駐輪場のデータが見つかりませんでした。');
// 駐輪場が選択されている場合のみ取得。「全て/空」の場合は空コレクションを返す
$utilizationStats = collect();
if ($parkId !== null && $parkId !== '') {
$utilizationStats = $service->getUtilizationStats($parkId);
}
// 検索要求でない場合は全データを表示
if (!$isSearchRequest && !$parkId) {
$utilizationStats = $service->getUtilizationStats(null);
}
// 合計値の計算
$totals = $service->calculateTotals($utilizationStats);
$hasData = $utilizationStats->isNotEmpty();
$isSearchRequest = ($request->isMethod('post') || $request->has('park_id'));
$selectedPark = $parkList->firstWhere('park_id', $parkId);
// 選択された駐輪場の情報
$selectedPark = null;
if ($parkId && $parkList->isNotEmpty()) {
$selectedPark = $parkList->firstWhere('park_id', $parkId);
}
// ビューに渡すデータの準備
$viewData = [
'parkList' => $parkList, // 駐輪場選択用リスト
'utilizationStats' => $utilizationStats, // 利用率統計データ
'totals' => $totals, // 合計値
'selectedParkId' => $parkId, // 選択された駐輪場ID
'selectedPark' => $selectedPark, // 選択された駐輪場情報
'isSearchRequest' => $isSearchRequest, // 検索リクエストかどうか
'hasData' => $utilizationStats->isNotEmpty() // データが存在するかどうか
];
// 成功メッセージの設定(検索時)
if ($isSearchRequest && $utilizationStats->isNotEmpty()) {
session()->flash('success', '利用率データを正常に取得しました。');
}
return view('admin.using_status.index', $viewData);
return view('admin.using_status.index', [
'parkList' => $parkList,
'utilizationStats' => $utilizationStats,
'totals' => $totals,
'selectedParkId' => $parkId,
'selectedPark' => $selectedPark,
'isSearchRequest' => $isSearchRequest,
'hasData' => $hasData,
]);
} catch (\Exception $e) {
// エラーログの出力
Log::error('区画別利用率状況ページエラー', [
'error' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'park_id' => $parkId ?? null
'file' => $e->getFile(),
'line' => $e->getLine(),
'park_id' => $parkId ?? null,
]);
// エラー発生時のリダイレクト
return redirect()->route('using_status')
->with('error', 'データの取得中にエラーが発生しました。管理者にお問い合わせください。');
}

View File

@ -16,54 +16,52 @@ class UsingStatusService
/**
* 駐輪場別利用率統計を取得
*
* 取得元:
* - 限界収容台数: park_number.park_limit
* - 現在収容台数: park_number.park_number
* - 空き : park_number.park_limit - park_number.park_number
* - 利用率 : (現在収容台数 / 限界収容台数) * 100
*
* @param int|null $parkId 駐輪場IDnull の場合は全て)
* @return Collection 統計データのコレクション
* @return Collection
*/
public function getUtilizationStats(?int $parkId = null): Collection
{
// 暫定的な収容台数実DBに無いカラムのため
$defaultCapacity = [
1 => 100, // 自転車
2 => 50, // 原付
3 => 30, // その他
];
// park_number に車種IDが ptype_id で入っている前提
// 異なる場合は 'pn.ptype_id' 部分を実テーブル定義に合わせて変更してください
$query = DB::table('park as p')
->leftJoin('price_a as pr', 'p.park_id', '=', 'pr.park_id')
->leftJoin('ptype as pt', 'pr.price_ptypeid', '=', 'pt.ptype_id')
->leftJoin('regular_contract as rc', function ($join) {
$join->on('pr.price_parkplaceid', '=', 'rc.price_parkplaceid')
->where('rc.contract_cancel_flag', '=', 0);
})
->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',
DB::raw('COUNT(rc.contract_id) as current_count'),
// 限界収容台数
'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"),
])
->whereNotNull('pr.price_parkplaceid')
->whereNotNull('pt.ptype_id')
->where('p.park_close_flag', '!=', 1);
if ($parkId) {
if (!empty($parkId)) {
$query->where('p.park_id', $parkId);
}
$results = $query
->groupBy(['p.park_id', 'p.park_name', 'pt.ptype_id', 'pt.ptype_subject'])
// 表示順:自転車 → 原付 → その他
->orderByRaw("CASE pt.ptype_subject WHEN '自転車' THEN 1 WHEN '原付' THEN 2 ELSE 3 END")
->orderBy('p.park_name')
->orderBy('pt.ptype_subject')
->get();
// 後計算で容量・空き・利用率を付与
foreach ($results as $result) {
$capacity = $defaultCapacity[$result->ptype_id] ?? 50;
$result->park_limit = $capacity;
$result->available = $capacity - $result->current_count;
$result->usage_rate = $capacity > 0 ? round(($result->current_count / $capacity * 100), 1) : 0;
}
return $results;
}

View File

@ -70,7 +70,7 @@ Laravel 12移行対応区画別利用率状況ページ
Laravel 12変更点CSRFトークンの自動処理
route() ヘルパーの使用
--}}
<form action="{{ route('using_status') }}" method="post" id="list-form">
<form action="{{ route('using_status') }}" method="get" id="list-form">
@csrf
<div class="row">
@ -82,7 +82,7 @@ Laravel 12移行対応区画別利用率状況ページ
<div class="form-group col-4">
<div class="input-group">
<select name="park_id" class="form-control" id="search_park">
<select name="park_id" class="form-control" id="search_park" onchange="this.form.submit()">
<option value="">全て</option>
{{--
Laravel 12変更点Bladeディレクティブの最適化
@ -106,7 +106,7 @@ Laravel 12移行対応区画別利用率状況ページ
</div>
{{-- データ表示テーブル --}}
<div class="form col-lg-12">
<div class="form col-lg-12 {{ empty($selectedParkId) ? 'd-none' : '' }}">
<div class="col-lg-12 row sample03-wrapper no_padding_right mb20">
<div class="col-lg-12 col-xl-12 col-md-12 col-sm-12 col-xs-12 table_right no_padding_right">
<div class="scroll">
@ -236,19 +236,22 @@ JavaScript処理の最適化
@section('scripts')
<script>
$(function () {
// Laravel 12対応Select2の初期化
$('#search_park').select2({
placeholder: '駐輪場を選択してください',
allowClear: true,
language: 'ja'
});
// 駐輪場選択時の自動フォーム送信
// 変更時は GET リダイレクトで遷移し、park_id を確実にサーバへ渡す
$('#search_park').on('change', function() {
var selectedValue = $(this).val();
if (selectedValue !== '') {
// 選択された場合は自動送信
$('#list-form').submit();
var v = $(this).val();
var base = '{{ route("using_status") }}';
if (v && v !== '') {
// 「第一駐輪場」など具体値 → クエリ付きで遷移
window.location.href = base + '?park_id=' + encodeURIComponent(v);
} else {
// 「全て」→ ベースURLへ表は表示しない
window.location.href = base;
}
});
@ -269,7 +272,7 @@ $(function () {
paging: false, // ページングを無効化
searching: false, // 検索を無効化
ordering: false, // ソートを無効化
info: false, // 情報表示を無効化
info: false, // 情報表示を無効化
responsive: true, // レスポンシブ対応
language: {
"emptyTable": "データがありません"
@ -324,7 +327,7 @@ function printTable() {
{{-- 印刷用CSS --}}
<style>
@media print {
.card-header, .breadcrumb, .alert, #search_park, .btn {
.card-header, .breadcrumb, .alert, #search_ark, .btn {
display: none !important;
}
@ -344,24 +347,13 @@ function printTable() {
}
/* Laravel 12対応利用率表示の色分け */
.text-danger {
color: #dc3545 !important;
}
.text-warning {
color: #ffc107 !important;
}
.text-success {
color: #28a745 !important;
}
.text-danger { color: #dc3545 !important; }
.text-warning { color: #ffc107 !important; }
.text-success { color: #28a745 !important; }
/* レスポンシブテーブル対応 */
@media (max-width: 768px) {
.table-responsive {
border: none;
}
.table-responsive { border: none; }
.table td, .table th {
font-size: 0.875rem;
padding: 0.5rem 0.25rem;