This commit is contained in:
parent
75bbec6f5a
commit
3d82536404
@ -38,76 +38,36 @@ class UsingStatusController extends Controller
|
|||||||
public function index(Request $request, UsingStatusService $service)
|
public function index(Request $request, UsingStatusService $service)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// CSRF トークンの自動検証(Laravel 12標準機能)
|
$parkId = $request->input('park_id'); // GET/POST どちらでも取得
|
||||||
|
|
||||||
// リクエストパラメータの取得
|
|
||||||
// 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()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 駐輪場一覧の取得(選択用ドロップダウン)
|
|
||||||
$parkList = $service->getParkList();
|
$parkList = $service->getParkList();
|
||||||
|
|
||||||
// 利用率統計データの取得
|
// 駐輪場が選択されている場合のみ取得。「全て/空」の場合は空コレクションを返す
|
||||||
// Laravel 12変更点:デフォルトで全データを表示(ユーザー選択不要)
|
$utilizationStats = collect();
|
||||||
$utilizationStats = $service->getUtilizationStats($parkId);
|
if ($parkId !== null && $parkId !== '') {
|
||||||
|
$utilizationStats = $service->getUtilizationStats($parkId);
|
||||||
// データが空の場合の処理
|
|
||||||
if ($utilizationStats->isEmpty() && $parkId) {
|
|
||||||
// 指定された駐輪場のデータが見つからない場合
|
|
||||||
return redirect()->route('using_status')
|
|
||||||
->with('warning', '選択された駐輪場のデータが見つかりませんでした。');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 検索要求でない場合は全データを表示
|
|
||||||
if (!$isSearchRequest && !$parkId) {
|
|
||||||
$utilizationStats = $service->getUtilizationStats(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 合計値の計算
|
|
||||||
$totals = $service->calculateTotals($utilizationStats);
|
$totals = $service->calculateTotals($utilizationStats);
|
||||||
|
$hasData = $utilizationStats->isNotEmpty();
|
||||||
|
$isSearchRequest = ($request->isMethod('post') || $request->has('park_id'));
|
||||||
|
$selectedPark = $parkList->firstWhere('park_id', $parkId);
|
||||||
|
|
||||||
// 選択された駐輪場の情報
|
return view('admin.using_status.index', [
|
||||||
$selectedPark = null;
|
'parkList' => $parkList,
|
||||||
if ($parkId && $parkList->isNotEmpty()) {
|
'utilizationStats' => $utilizationStats,
|
||||||
$selectedPark = $parkList->firstWhere('park_id', $parkId);
|
'totals' => $totals,
|
||||||
}
|
'selectedParkId' => $parkId,
|
||||||
|
'selectedPark' => $selectedPark,
|
||||||
// ビューに渡すデータの準備
|
'isSearchRequest' => $isSearchRequest,
|
||||||
$viewData = [
|
'hasData' => $hasData,
|
||||||
'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);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// エラーログの出力
|
|
||||||
Log::error('区画別利用率状況ページエラー', [
|
Log::error('区画別利用率状況ページエラー', [
|
||||||
'error' => $e->getMessage(),
|
'error' => $e->getMessage(),
|
||||||
'file' => $e->getFile(),
|
'file' => $e->getFile(),
|
||||||
'line' => $e->getLine(),
|
'line' => $e->getLine(),
|
||||||
'park_id' => $parkId ?? null
|
'park_id' => $parkId ?? null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// エラー発生時のリダイレクト
|
|
||||||
return redirect()->route('using_status')
|
return redirect()->route('using_status')
|
||||||
->with('error', 'データの取得中にエラーが発生しました。管理者にお問い合わせください。');
|
->with('error', 'データの取得中にエラーが発生しました。管理者にお問い合わせください。');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 駐輪場ID(null の場合は全て)
|
* @param int|null $parkId 駐輪場ID(null の場合は全て)
|
||||||
* @return Collection 統計データのコレクション
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function getUtilizationStats(?int $parkId = null): Collection
|
public function getUtilizationStats(?int $parkId = null): Collection
|
||||||
{
|
{
|
||||||
// 暫定的な収容台数(実DBに無いカラムのため)
|
// park_number に車種IDが ptype_id で入っている前提
|
||||||
$defaultCapacity = [
|
// 異なる場合は 'pn.ptype_id' 部分を実テーブル定義に合わせて変更してください
|
||||||
1 => 100, // 自転車
|
|
||||||
2 => 50, // 原付
|
|
||||||
3 => 30, // その他
|
|
||||||
];
|
|
||||||
|
|
||||||
$query = DB::table('park as p')
|
$query = DB::table('park as p')
|
||||||
->leftJoin('price_a as pr', 'p.park_id', '=', 'pr.park_id')
|
->join('park_number as pn', 'p.park_id', '=', 'pn.park_id')
|
||||||
->leftJoin('ptype as pt', 'pr.price_ptypeid', '=', 'pt.ptype_id')
|
->join('ptype as pt', 'pn.ptype_id', '=', 'pt.ptype_id')
|
||||||
->leftJoin('regular_contract as rc', function ($join) {
|
|
||||||
$join->on('pr.price_parkplaceid', '=', 'rc.price_parkplaceid')
|
|
||||||
->where('rc.contract_cancel_flag', '=', 0);
|
|
||||||
})
|
|
||||||
->select([
|
->select([
|
||||||
'p.park_id',
|
'p.park_id',
|
||||||
'p.park_name',
|
'p.park_name',
|
||||||
'pt.ptype_id',
|
'pt.ptype_id',
|
||||||
'pt.ptype_subject',
|
'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);
|
->where('p.park_close_flag', '!=', 1);
|
||||||
|
|
||||||
if ($parkId) {
|
if (!empty($parkId)) {
|
||||||
$query->where('p.park_id', $parkId);
|
$query->where('p.park_id', $parkId);
|
||||||
}
|
}
|
||||||
|
|
||||||
$results = $query
|
$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('p.park_name')
|
||||||
->orderBy('pt.ptype_subject')
|
|
||||||
->get();
|
->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;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -70,7 +70,7 @@ Laravel 12移行対応:区画別利用率状況ページ
|
|||||||
Laravel 12変更点:CSRFトークンの自動処理
|
Laravel 12変更点:CSRFトークンの自動処理
|
||||||
route() ヘルパーの使用
|
route() ヘルパーの使用
|
||||||
--}}
|
--}}
|
||||||
<form action="{{ route('using_status') }}" method="post" id="list-form">
|
<form action="{{ route('using_status') }}" method="get" id="list-form">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -82,7 +82,7 @@ Laravel 12移行対応:区画別利用率状況ページ
|
|||||||
|
|
||||||
<div class="form-group col-4">
|
<div class="form-group col-4">
|
||||||
<div class="input-group">
|
<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>
|
<option value="">全て</option>
|
||||||
{{--
|
{{--
|
||||||
Laravel 12変更点:Bladeディレクティブの最適化
|
Laravel 12変更点:Bladeディレクティブの最適化
|
||||||
@ -106,7 +106,7 @@ Laravel 12移行対応:区画別利用率状況ページ
|
|||||||
</div>
|
</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 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="col-lg-12 col-xl-12 col-md-12 col-sm-12 col-xs-12 table_right no_padding_right">
|
||||||
<div class="scroll">
|
<div class="scroll">
|
||||||
@ -236,19 +236,22 @@ JavaScript処理の最適化
|
|||||||
@section('scripts')
|
@section('scripts')
|
||||||
<script>
|
<script>
|
||||||
$(function () {
|
$(function () {
|
||||||
// Laravel 12対応:Select2の初期化
|
|
||||||
$('#search_park').select2({
|
$('#search_park').select2({
|
||||||
placeholder: '駐輪場を選択してください',
|
placeholder: '駐輪場を選択してください',
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
language: 'ja'
|
language: 'ja'
|
||||||
});
|
});
|
||||||
|
|
||||||
// 駐輪場選択時の自動フォーム送信
|
// 変更時は GET リダイレクトで遷移し、park_id を確実にサーバへ渡す
|
||||||
$('#search_park').on('change', function() {
|
$('#search_park').on('change', function() {
|
||||||
var selectedValue = $(this).val();
|
var v = $(this).val();
|
||||||
if (selectedValue !== '') {
|
var base = '{{ route("using_status") }}';
|
||||||
// 選択された場合は自動送信
|
if (v && v !== '') {
|
||||||
$('#list-form').submit();
|
// 「第一駐輪場」など具体値 → クエリ付きで遷移
|
||||||
|
window.location.href = base + '?park_id=' + encodeURIComponent(v);
|
||||||
|
} else {
|
||||||
|
// 「全て」→ ベースURLへ(表は表示しない)
|
||||||
|
window.location.href = base;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -269,7 +272,7 @@ $(function () {
|
|||||||
paging: false, // ページングを無効化
|
paging: false, // ページングを無効化
|
||||||
searching: false, // 検索を無効化
|
searching: false, // 検索を無効化
|
||||||
ordering: false, // ソートを無効化
|
ordering: false, // ソートを無効化
|
||||||
info: false, // 情報表示を無効化
|
info: false, // 情報表示を無効化
|
||||||
responsive: true, // レスポンシブ対応
|
responsive: true, // レスポンシブ対応
|
||||||
language: {
|
language: {
|
||||||
"emptyTable": "データがありません"
|
"emptyTable": "データがありません"
|
||||||
@ -324,7 +327,7 @@ function printTable() {
|
|||||||
{{-- 印刷用CSS --}}
|
{{-- 印刷用CSS --}}
|
||||||
<style>
|
<style>
|
||||||
@media print {
|
@media print {
|
||||||
.card-header, .breadcrumb, .alert, #search_park, .btn {
|
.card-header, .breadcrumb, .alert, #search_ark, .btn {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,24 +347,13 @@ function printTable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Laravel 12対応:利用率表示の色分け */
|
/* Laravel 12対応:利用率表示の色分け */
|
||||||
.text-danger {
|
.text-danger { color: #dc3545 !important; }
|
||||||
color: #dc3545 !important;
|
.text-warning { color: #ffc107 !important; }
|
||||||
}
|
.text-success { color: #28a745 !important; }
|
||||||
|
|
||||||
.text-warning {
|
|
||||||
color: #ffc107 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-success {
|
|
||||||
color: #28a745 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* レスポンシブテーブル対応 */
|
/* レスポンシブテーブル対応 */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.table-responsive {
|
.table-responsive { border: none; }
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table td, .table th {
|
.table td, .table th {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
padding: 0.5rem 0.25rem;
|
padding: 0.5rem 0.25rem;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user