250 lines
9.9 KiB
PHP
250 lines
9.9 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Admin;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Models\Manager;
|
||
use App\Models\Park;
|
||
use App\Models\Device;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||
|
||
class ManagerController extends Controller
|
||
{
|
||
/** 一覧 */
|
||
public function list(Request $request)
|
||
{
|
||
$sortable = [
|
||
'manager_id','manager_name','manager_parkid','manager_tel',
|
||
'manager_alert1','manager_alert2','manager_quit_flag'
|
||
];
|
||
$sort = $request->input('sort', 'manager_id');
|
||
$sort_type = $request->input('sort_type', 'asc');
|
||
if (!in_array($sort, $sortable)) $sort = 'manager_id';
|
||
if (!in_array(strtolower($sort_type), ['asc','desc'])) $sort_type = 'asc';
|
||
|
||
$list = Manager::with(['park','device1','device2'])
|
||
->orderBy($sort, $sort_type)
|
||
->paginate(20);
|
||
|
||
return view('admin.managers.list', compact('list','sort','sort_type'));
|
||
}
|
||
|
||
/** 新規登録画面・登録処理 */
|
||
public function add(Request $request)
|
||
{
|
||
if ($request->isMethod('post')) {
|
||
$validated = $this->validated($request);
|
||
|
||
Manager::create($validated);
|
||
|
||
return redirect()
|
||
->route('managers')
|
||
->with('success', '登録しました。');
|
||
}
|
||
|
||
return view('admin.managers.add', $this->viewVars());
|
||
}
|
||
|
||
/** 編集(GET:画面表示 / POST:更新) */
|
||
public function edit(Request $request, $id)
|
||
{
|
||
$manager = Manager::findOrFail($id);
|
||
|
||
if ($request->isMethod('post')) {
|
||
$validated = $this->validated($request);
|
||
|
||
$manager->update($validated);
|
||
|
||
return redirect()
|
||
->route('managers')
|
||
->with('success', '更新されました。');
|
||
}
|
||
|
||
return view('admin.managers.edit', $this->viewVars($manager));
|
||
}
|
||
|
||
/** 詳細(閲覧) */
|
||
public function info($manager_id)
|
||
{
|
||
$manager = Manager::with(['park','device1','device2'])->findOrFail($manager_id);
|
||
$view = $this->viewVars($manager);
|
||
return view('admin.managers.info', $view);
|
||
}
|
||
|
||
/** 一括削除(一覧・詳細・編集共通で pk[] を受ける) */
|
||
public function delete(Request $request)
|
||
{
|
||
$ids = (array) $request->input('pk', []);
|
||
if (!$ids) {
|
||
return back()->with('error', '削除対象が選択されていません。');
|
||
}
|
||
|
||
DB::transaction(fn() => Manager::whereIn('manager_id', $ids)->delete());
|
||
|
||
// 一覧画面へリダイレクト + 成功メッセージ
|
||
return redirect()
|
||
->route('managers')
|
||
->with('success', '削除しました。');
|
||
}
|
||
|
||
|
||
/** CSV出力 */
|
||
public function export(): StreamedResponse
|
||
{
|
||
$headers = [
|
||
'Content-Type' => 'text/csv; charset=UTF-8',
|
||
'Content-Disposition' => 'attachment; filename=managers.csv',
|
||
];
|
||
|
||
$columns = [
|
||
'manager_id','manager_name','manager_type','manager_parkid',
|
||
'manager_device1','manager_device2','manager_mail','manager_tel',
|
||
'manager_alert1','manager_alert2','manager_quit_flag','manager_quitday'
|
||
];
|
||
|
||
return response()->stream(function () use ($columns) {
|
||
$out = fopen('php://output', 'w');
|
||
fwrite($out, "\xEF\xBB\xBF"); // BOM
|
||
fputcsv($out, $columns);
|
||
|
||
Manager::chunk(500, function ($rows) use ($out, $columns) {
|
||
foreach ($rows as $r) {
|
||
fputcsv($out, array_map(fn($c) => data_get($r, $c), $columns));
|
||
}
|
||
});
|
||
|
||
fclose($out);
|
||
}, 200, $headers);
|
||
}
|
||
|
||
/** CSVインポート(input name="file") */
|
||
public function import(Request $request)
|
||
{
|
||
if (!$request->hasFile('file')) {
|
||
return back()->with('error', 'CSVファイルを選択してください。');
|
||
}
|
||
$fp = fopen($request->file('file')->getRealPath(), 'r');
|
||
if (!$fp) return back()->with('error', 'CSVを読み込めませんでした。');
|
||
|
||
$header = fgetcsv($fp);
|
||
if (!$header) { fclose($fp); return back()->with('error', 'ヘッダ行が読み取れません。'); }
|
||
|
||
$required = [
|
||
'manager_id','manager_name','manager_type','manager_parkid',
|
||
'manager_device1','manager_device2','manager_mail','manager_tel',
|
||
'manager_alert1','manager_alert2','manager_quit_flag','manager_quitday'
|
||
];
|
||
foreach ($required as $c) {
|
||
if (!in_array($c, $header)) { fclose($fp); return back()->with('error', "CSVに {$c} がありません。"); }
|
||
}
|
||
|
||
DB::beginTransaction();
|
||
try {
|
||
while (($row = fgetcsv($fp)) !== false) {
|
||
$data = array_combine($header, $row); if (!$data) continue;
|
||
|
||
Manager::updateOrCreate(
|
||
['manager_id' => $data['manager_id']],
|
||
[
|
||
'manager_name' => $data['manager_name'] ?? null,
|
||
'manager_type' => $data['manager_type'] ?? null,
|
||
'manager_parkid' => $data['manager_parkid'] ?: null,
|
||
'manager_device1' => $data['manager_device1'] ?: null,
|
||
'manager_device2' => $data['manager_device2'] ?: null,
|
||
'manager_mail' => $data['manager_mail'] ?? null,
|
||
'manager_tel' => $data['manager_tel'] ?? null,
|
||
'manager_alert1' => (int)($data['manager_alert1'] ?? 0),
|
||
'manager_alert2' => (int)($data['manager_alert2'] ?? 0),
|
||
'manager_quit_flag' => (int)($data['manager_quit_flag'] ?? 0),
|
||
'manager_quitday' => $data['manager_quitday'] ?: null,
|
||
]
|
||
);
|
||
}
|
||
fclose($fp);
|
||
DB::commit();
|
||
return back()->with('success', 'インポートが完了しました。');
|
||
} catch (\Throwable $e) {
|
||
if (is_resource($fp)) fclose($fp);
|
||
DB::rollBack();
|
||
return back()->with('error', 'インポートに失敗しました:'.$e->getMessage());
|
||
}
|
||
}
|
||
|
||
/** バリデーション + 前処理 */
|
||
private function validated(Request $request): array
|
||
{
|
||
// 電話番号を全角に変換(半角入力があっても自動で全角に揃える)
|
||
$request->merge([
|
||
'manager_tel' => mb_convert_kana($request->input('manager_tel'), 'N') // 半角数字→全角数字
|
||
]);
|
||
|
||
// select の未選択 "" を null に補正
|
||
foreach (['manager_device2'] as $f) {
|
||
if ($request->input($f) === "") {
|
||
$request->merge([$f => null]);
|
||
}
|
||
}
|
||
|
||
return $request->validate([
|
||
'manager_name' => ['required','string','max:32'],
|
||
'manager_type' => ['required','string','max:10'],
|
||
'manager_parkid' => ['required','integer','exists:park,park_id'],
|
||
'manager_device1' => ['required','integer','exists:device,device_id'],
|
||
'manager_device2' => ['nullable','integer','exists:device,device_id'],
|
||
'manager_mail' => ['nullable','email','max:128'],
|
||
'manager_tel' => ['required','regex:/^[0-9]+$/u','max:13'], // 全角数字のみ
|
||
'manager_alert1' => ['nullable','boolean'],
|
||
'manager_alert2' => ['nullable','boolean'],
|
||
'manager_quit_flag' => ['required','in:0,1'],
|
||
'manager_quitday' => ['nullable','date'],
|
||
], [], [
|
||
'manager_name' => '駐輪場管理者名',
|
||
'manager_type' => '種別',
|
||
'manager_parkid' => '所属駐輪場ID',
|
||
'manager_device1' => '管理デバイス1',
|
||
'manager_device2' => '管理デバイス2',
|
||
'manager_mail' => 'メールアドレス',
|
||
'manager_tel' => '電話番号',
|
||
'manager_alert1' => 'アラート1送信',
|
||
'manager_alert2' => 'アラート2送信',
|
||
'manager_quit_flag' => '退職フラグ',
|
||
'manager_quitday' => '退職日',
|
||
]);
|
||
}
|
||
|
||
|
||
/** 画面に渡す変数を作る(_form.blade.php が個別変数を参照するため) */
|
||
private function viewVars(?Manager $m = null): array
|
||
{
|
||
$parks = Park::orderBy('park_name')->pluck('park_name','park_id')->toArray();
|
||
$devices = Device::orderBy('device_subject')->pluck('device_subject','device_id')->toArray();
|
||
|
||
return [
|
||
// _form が参照する個別変数
|
||
'manager_id' => $m->manager_id ?? null,
|
||
'manager_name' => $m->manager_name ?? null,
|
||
'manager_type' => $m->manager_type ?? null,
|
||
'manager_parkid' => $m->manager_parkid ?? null,
|
||
'manager_device1' => $m->manager_device1 ?? null,
|
||
'manager_device2' => $m->manager_device2 ?? null,
|
||
'manager_mail' => $m->manager_mail ?? null,
|
||
'manager_tel' => $m->manager_tel ?? null,
|
||
'manager_alert1' => (int)($m->manager_alert1 ?? 0),
|
||
'manager_alert2' => (int)($m->manager_alert2 ?? 0),
|
||
'manager_quit_flag' => isset($m) ? (int)$m->manager_quit_flag : 0,
|
||
'manager_quitday' => isset($m) && $m->manager_quitday ? $m->manager_quitday->format('Y-m-d') : null,
|
||
|
||
// セレクトの候補
|
||
'parks' => $parks,
|
||
'devices' => $devices,
|
||
|
||
// _form で必要なフラグ(各ビューで上書きしてもOK)
|
||
'isEdit' => isset($m),
|
||
'isInfo' => false,
|
||
'record' => $m, // 互換用(あなた的 edit.blade.php で参照しているなら)
|
||
];
|
||
}
|
||
}
|