9/26 マージ
All checks were successful
Deploy preview (main_watanabe) / deploy (push) Successful in 13s

This commit is contained in:
Yu Watanabe 2025-09-26 17:27:15 +09:00
parent 52f568c5f4
commit eed5d85741
3 changed files with 202 additions and 66 deletions

View File

@ -31,14 +31,10 @@ class ParkingSearchController extends Controller
// 検索処理 // 検索処理
public function getParkData($city_name, $station_neighbor_station, $park_name) public function getParkData($city_name, $station_neighbor_station, $park_name)
{ {
// 検索仕様
// 駐輪場マスタの全件(条件を絞った場合はその条件に一致するもの)を取得。
// 併せて各マスタから追加情報を取得するが、その際のレコードは全て1対1で結びつく想定で暫定実装する
// ※設計書に詳細な記載なし。DBの定義上は1対多の可能性もあるが、その場合現在の画面イメージと矛盾するため、実態として無い想定で進める
// 駐輪場情報検索 // 駐輪場情報検索
$park = \DB::table('park as p') $park = \DB::table('park as p')
->select( ->select(
'p.park_id',
'p.park_name', 'p.park_name',
'p.park_adrs', 'p.park_adrs',
'p.price_memo', 'p.price_memo',
@ -49,15 +45,12 @@ class ParkingSearchController extends Controller
'p.update_grace_period_end_date', 'p.update_grace_period_end_date',
'p.update_grace_period_end_time', 'p.update_grace_period_end_time',
'c.city_name', 'c.city_name',
's.station_neighbor_station', 's.station_neighbor_station'
'z.psection_id',
'z.zone_standard'
) )
->leftJoin('city as c', 'p.city_id', '=', 'c.city_id') ->leftJoin('city as c', 'p.city_id', '=', 'c.city_id')
->leftJoin(\DB::raw( ->leftJoin(\DB::raw(
'(SELECT park_id, station_neighbor_station FROM station WHERE station_id IN (SELECT MAX(station_id) FROM station GROUP BY park_id)) as s' '(SELECT park_id, station_neighbor_station FROM station WHERE station_id IN (SELECT MAX(station_id) FROM station GROUP BY park_id)) as s'
),'p.park_id','=','s.park_id') ),'p.park_id','=','s.park_id');
->leftJoin('zone as z', 'p.park_id', '=', 'z.park_id');
// プルダウン指定の条件でwhere句を付与 // プルダウン指定の条件でwhere句を付与
if (!empty($city_name)) { if (!empty($city_name)) {
@ -76,42 +69,102 @@ class ParkingSearchController extends Controller
$now = date('Y-m-d H:i:s'); $now = date('Y-m-d H:i:s');
foreach ($park as $row) { foreach ($park as $row) {
// ゾーンマスタの情報から空き台数を取得する // ゾーンマスタの情報を取得する
$vacantInfo = \DB::table('zone') $zoneInfo = \DB::table('zone as z')
->selectRaw('SUM(zone_tolerance) - SUM(zone_number) as vacant') ->select(
->where('psection_id', $row->psection_id) 'z.psection_id',
->groupBy('psection_id') 'z.ptype_id',
->first(); \DB::raw('SUM(z.zone_standard) as zone_standard'),
\DB::raw('SUM(z.zone_number) as zone_number'),
\DB::raw('SUM(z.zone_tolerance) as zone_tolerance'),
'ps.psection_subject',
'pt.ptype_subject'
)
->join('ptype as pt', 'z.ptype_id', '=', 'pt.ptype_id')
->leftJoin('psection as ps', 'z.psection_id', '=', 'ps.psection_id')
->where('z.park_id', $row->park_id)
->groupBy('z.park_id', 'z.ptype_id', 'z.psection_id', 'ps.psection_subject', 'pt.ptype_subject')
->get();
// 定期予約マスタから予約中の台数を取得する // ゾーンマスタが0件の場合、次のデータへ
$reservedCount = \DB::table('reserve') if ($zoneInfo->isEmpty()) {
->where('psection_id', $row->psection_id) $form_data[] = [
->where('valid_flag', 1) 'park_name' => $row->park_name,
->count(); 'park_adrs' => $row->park_adrs,
'price_memo' => $row->price_memo,
'park_latitude' => $row->park_latitude,
'park_longitude' => $row->park_longitude,
'city_name' => $row->city_name,
'station_neighbor_station' => $row->station_neighbor_station,
'zone_data' => []
];
continue;
}
// 更新期間内判定 // 更新期間内判定
$update_start = $row->update_grace_period_start_date . ' ' . $row->update_grace_period_start_time; $update_start = $row->update_grace_period_start_date . ' ' . $row->update_grace_period_start_time;
$update_end = $row->update_grace_period_end_date . ' ' . $row->update_grace_period_end_time; $update_end = $row->update_grace_period_end_date . ' ' . $row->update_grace_period_end_time;
$is_update_period = ($now >= $update_start && $now <= $update_end); $is_update_period = ($now >= $update_start && $now <= $update_end);
// ボタン表示有無判定 // ゾーンマスタの件数分だけループする
$vacant = ($vacantInfo ? $vacantInfo->vacant : 0) - $reservedCount; $zone_data = [];
if ($vacant > 0 && $is_update_period) { // 定期契約ボタン (空き台数が1台以上かつ更新期間内) foreach ($zoneInfo as $zone) {
$status = 1;
} elseif ($vacant <= 0 && $is_update_period) { // 空き待ち予約ボタン (空き台数が0台以下かつ更新期間内) // 予約中件数取得
$status = 2; $reservedCount = \DB::table('reserve')
} elseif ($vacant <= 0 && !$is_update_period) { // 販売期間外ボタン (空き台数が0台以下かつ更新期間外) ->where('park_id', $row->park_id)
$status = 3; ->where('psection_id', $zone->psection_id)
} else { ->where('ptype_id', $zone->ptype_id)
$status = null; ->where('valid_flag', 1)
->count();
// ステータス(表示ボタン)判定
$status = 0; // 0:非表示, 1:定期契約, 2:空き待ち予約, 3:販売期間外
$zone_vacant = $zone->zone_tolerance - $zone->zone_number - $reservedCount;
if ($zone_vacant > 0 && $is_update_period) { // 定期契約ボタン (空き台数が1台以上かつ更新期間内)
$status = 1;
} elseif ($zone_vacant <= 0 && $is_update_period) { // 空き待ち予約ボタン (空き台数が0台以下かつ更新期間内)
$status = 2;
} elseif ($zone_vacant <= 0 && !$is_update_period) { // 販売期間外ボタン (空き台数が0台以下かつ更新期間外)
$status = 3;
}
// 返却用データに追加
$zone_data[] = [
'psection_subject' => $zone->psection_subject,
'ptype_subject' => $zone->ptype_subject,
'zone_standard' => $zone->zone_standard,
'zone_vacant' => $zone_vacant,
'status' => $status
];
} }
// $zone_dataを並び替え
usort($zone_data, function($a, $b) {
// 第一優先: ptype_subject昇順
$ptypeCmp = strcmp($a['ptype_subject'], $b['ptype_subject']);
if ($ptypeCmp !== 0) {
return $ptypeCmp;
}
// 第二優先: psection_subject昇順
$psectionCmp = strcmp($a['psection_subject'], $b['psection_subject']);
if ($psectionCmp !== 0) {
return $psectionCmp;
}
// 第三優先: status昇順
return $a['status'] <=> $b['status'];
});
// 画面返却用データに追加 // 画面返却用データに追加
$form_data[] = [ $form_data[] = [
'park_name' => $row->park_name, 'park_name' => $row->park_name,
'park_adrs' => $row->park_adrs,
'price_memo' => $row->price_memo,
'park_latitude' => $row->park_latitude,
'park_longitude' => $row->park_longitude,
'city_name' => $row->city_name, 'city_name' => $row->city_name,
'station_neighbor_station' => $row->station_neighbor_station, 'station_neighbor_station' => $row->station_neighbor_station,
'status' => $status 'zone_data' => $zone_data
]; ];
} }
@ -133,7 +186,10 @@ class ParkingSearchController extends Controller
'わ行'=>'わをんワヲン ' 'わ行'=>'わをんワヲン '
]; ];
// 車種区分リスト取得
$psections = \DB::table('psection')->select('psection_subject')->orderBy('psection_id', 'asc')->limit(4)->get();
// 情報返却 // 情報返却
return ['form_data' => $form_data, 'cities' => $cities, 'stations' => $stations, 'parks' => $parks, 'conditions' => $conditions]; return ['form_data' => $form_data, 'conditions' => $conditions, 'cities' => $cities, 'stations' => $stations, 'parks' => $parks, 'psections' => $psections];
} }
} }

View File

@ -31,7 +31,7 @@
<h3 class="other mt50">開示等の依頼の手続き、使用する様式</h3> <h3 class="other mt50">開示等の依頼の手続き、使用する様式</h3>
<p class="p1">開示等の依頼は、以下の手続き及び様式に則って実施致します。</p> <p class="p1">開示等の依頼は、以下の手続き及び様式に則って実施致します。</p>
<p class="p1">利用目的の通知:本書面の“開示対象個人情報の利用目的”をご覧下さい。</p> <p class="p1">利用目的の通知:本書面の“開示対象個人情報の利用目的”をご覧下さい。</p>
<p class="p1">開示、訂正・削除、利用停止:当社の定める様式にて実施致します。該当の受付け窓口にご連絡頂き、所定の様式『<a href="{{ asset('assets/page-img/privacy_disclosure.pdf') }}" target="_blank">個人情報開示等依頼書(PDF:9KB)</a>』を入手のうえ、手続きをお願い致します。</p> <p class="p1">開示、訂正・削除、利用停止:当社の定める様式にて実施致します。該当の受付け窓口にご連絡頂き、所定の様式『<a href="{{ asset('assets/privacy_disclosure.pdf') }}" target="_blank">個人情報開示等依頼書(PDF:9KB)</a>』を入手のうえ、手続きをお願い致します。</p>
<p class="p1">回答に関しては、記入済み個人情報開示等依頼書を、ご自宅への郵送のみとさせて頂きます。</p> <p class="p1">回答に関しては、記入済み個人情報開示等依頼書を、ご自宅への郵送のみとさせて頂きます。</p>
<h3 class="other mt50">開示対象個人情報の取扱いに関する苦情受付け窓口</h3> <h3 class="other mt50">開示対象個人情報の取扱いに関する苦情受付け窓口</h3>
<p class="p1">開示対象個人情報の取扱いに関する苦情、相談に関しましては、以下の窓口宛てにご連絡ください。</p> <p class="p1">開示対象個人情報の取扱いに関する苦情、相談に関しましては、以下の窓口宛てにご連絡ください。</p>

View File

@ -65,10 +65,12 @@
<table id="searchTable" class="tablesorter table table-striped"> <table id="searchTable" class="tablesorter table table-striped">
<thead> <thead>
<tr> <tr>
<th>駐輪場名</th> <th width="20%">駐輪場名</th>
<th>市町村名</th> <th width="15%">市町村名</th>
<th>駅名</th> <th width="5%">駅名</th>
<th></th> @foreach($psections as $psection)
<th width="15%">{{ $psection->psection_subject }}</th>
@endforeach
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -82,18 +84,39 @@
@endphp @endphp
@foreach($pagedData as $data) @foreach($pagedData as $data)
<tr> <tr>
<td><a href="#placeModal" data-toggle="modal" data-target="#placeModal">{{ $data['park_name'] }}</a></td> <td>
<a href="#placeModal"
data-toggle="modal"
data-target="#placeModal"
data-park_name="{{ $data['park_name'] }}"
data-park_adrs="{{ $data['park_adrs'] ?? '' }}"
data-price_memo="{{ $data['price_memo'] ?? '' }}"
data-park_latitude="{{ $data['park_latitude'] ?? '' }}"
data-park_longitude="{{ $data['park_longitude'] ?? '' }}"
data-city_name="{{ $data['city_name'] }}"
data-station="{{ $data['station_neighbor_station'] }}"
data-zone_data='@json($data["zone_data"])'>
{{ $data['park_name'] }}
</a>
</td>
<td>{{ $data['city_name'] }}</td> <td>{{ $data['city_name'] }}</td>
<td>{{ $data['station_neighbor_station'] }}</td> <td>{{ $data['station_neighbor_station'] }}</td>
<td> @foreach($psections as $psection)
@if($data['status'] == 1) <td>
<a href="{{route('user.info')}}" class="btn btn-block btn-sm btn-outline-success">定期契約</a> @foreach($data['zone_data'] as $zone)
@elseif($data['status'] == 2) @if($zone['psection_subject'] == $psection->psection_subject)
<a href="{{route('park_waitlist.index')}}" class="btn btn-block btn-sm btn-outline-primary">空き待ち予約</a> @if($zone['status'] == 1)
@elseif($data['status'] == 3) <a href="{{route('user.info')}}" class="btn btn-block btn-sm btn-outline-success">定期契約</a>
<a href="{{route('park_waitlist.index')}}" class="btn btn-block btn-sm btn-secondary">販売期間外</a> @elseif($zone['status'] == 2)
@endif <a href="{{route('park_waitlist.index')}}" class="btn btn-block btn-sm btn-outline-primary">空き待ち予約</a>
</td> @elseif($zone['status'] == 3)
<a href="{{route('park_waitlist.index')}}" class="btn btn-block btn-sm btn-secondary">販売期間外</a>
@endif
@break;
@endif
@endforeach
</td>
@endforeach
</tr> </tr>
@endforeach @endforeach
</tbody> </tbody>
@ -133,29 +156,86 @@
<div class="modal fade" id="placeModal" tabindex="-1" role="dialog" aria-hidden="true"> <div class="modal fade" id="placeModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document"> <div class="modal-dialog modal-lg" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <script>
<h5 class="modal-title" id="modalParkName">駐輪場名</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="閉じる"><span aria-hidden="true">&times;</span></button>
</div>
<script>
$(function() {
$('#placeModal').on('show.bs.modal', function (event) { $('#placeModal').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget); var button = $(event.relatedTarget);
var parkName = button.text(); var parkName = button.data('park_name') || '';
$('#modalParkName').text(parkName); var parkAdrs = button.data('park_adrs') || '';
var parkMemo = button.data('price_memo') || '';
var lat = button.data('park_latitude') || '';
var lng = button.data('park_longitude') || '';
$('#parkName').text(parkName).attr('data-park_name', parkName);
$('#parkAdrs').text('住所:' + parkAdrs).attr('data-park_adrs', parkAdrs);
$('#parkmemo').text(parkMemo).attr('data-price_memo', parkMemo);
$('#parkMap').attr('src', 'https://www.google.com/maps?q=' + lat + ',' + lng + '&z=15&output=embed');
// zone_dataはJSON文字列として渡されているのでパース
var zoneData = button.data('zone_data') || [];
// psection_subjectごとの標準収容台数を集計
var standardMap = {};
if (zoneData && Array.isArray(zoneData)) {
zoneData.forEach(function(zone) {
if (!standardMap[zone.psection_subject]) {
standardMap[zone.psection_subject] = 0;
}
standardMap[zone.psection_subject] += parseInt(zone.zone_standard, 10) || 0;
});
}
// 表示用文字列を生成
var standardText = '';
var keys = Object.keys(standardMap);
if (keys.length > 0) {
standardText = '【標準収容台数】';
standardText += keys.map(function(key) {
return key + '' + standardMap[key] + '台';
}).join(' / ');
}
$('#parkStandard').text(standardText);
// 各ゾーンの空き台数・ボタンを表示
var html = '';
if (zoneData && Array.isArray(zoneData)) {
var grouped = {};
zoneData.forEach(function(zone) {
if (!grouped[zone.ptype_subject]) grouped[zone.ptype_subject] = [];
grouped[zone.ptype_subject].push(zone);
});
Object.keys(grouped).forEach(function(ptype) {
html += '<h4 class="mt-3">' + ptype + '</h4>';
grouped[ptype].forEach(function(zone) {
html += '<div class="d-flex align-items-center mb-2">';
html += '<span>' + zone.psection_subject + ':空き' + zone.zone_vacant + '台</span>';
if (zone.status == 1) {
html += '<a href="{{route('user.info')}}" class="btn btn-sm btn-outline-success ml-2">定期契約</a>';
} else if (zone.status == 2) {
html += '<a href="{{route('park_waitlist.index')}}" class="btn btn-sm btn-outline-primary ml-2">空き待ち予約</a>';
} else if (zone.status == 3) {
html += '<a href="{{route('park_waitlist.index')}}" class="btn btn-sm btn-outline-secondary ml-2">販売期間外</a>';
}
html += '</div>';
});
});
}
$('#zoneData').html(html);
}); });
}); </script>
</script> <div class="modal-header">
<h5 class="modal-title" id="parkName" data-park_name=""></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="閉じる"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body"> <div class="modal-body">
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3240.722943699139!2d139.75162621525894!3d35.68382338019366!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x60188c0b185b3b75%3A0x3282e79fbc91959c!2z44CSMTAwLTAwMDEg5p2x5Lqs6YO95Y2D5Luj55Sw5Yy65Y2D5Luj55Sw77yR4oiS77yR!5e0!3m2!1sja!2sjp!4v1536695351221" width="100%" height="450" frameborder="0" style="border:0" allowfullscreen></iframe> <iframe id="parkMap" src="" width="100%" height="450" frameborder="0" style="border:0" allowfullscreen></iframe>
<p class="small">〒000-0000 東京都千代田区1-1 <br class="sp">標準収容台数XXX台</p> <p class="small">
<p class="text-danger">空き台数XXX台</p> <span id="parkAdrs"> </span>
<span id="parkStandard"></span><br />
<span id="parkmemo"></span>
</p>
<span id="zoneData"></span>
<div class="text-right"><button type="button" class="btn btn-outline-secondary" data-dismiss="modal">閉じる</button></div>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer"></div>
<button type="submit" class="btn btn-success" onClick="location.href='./SWC-08-02.html'">定期契約</button>
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">閉じる</button>
</div>
</div> </div>
</div> </div>
@endsection @endsection