orderBy('user_categoryid', 'asc') ->get() ->mapWithKeys(function ($row) { $label = collect([ $row->usertype_subject1 ?? '', $row->usertype_subject2 ?? '', $row->usertype_subject3 ?? '', ])->filter(fn ($v) => $v !== '')->implode('/'); return [$row->user_categoryid => $label !== '' ? $label : (string) $row->user_categoryid]; }) ->toArray(); } /** * 利用者一覧 * - テーブル名: user * - 主キー: user_seq(AUTO_INCREMENT):contentReference[oaicite:3]{index=3} * - よく使う表示項目のみ選択(必要に応じて拡張) */ // 先确保:use App\Http\Controllers\Controller; を追加し、必要なら extends Controller にする // use Symfony\Component\HttpFoundation\StreamedResponse; は既にOK /** * 一覧(絞り込み + ページング) */ public function list(Request $request) { // ▼ 並び順(ホワイトリスト) $sortable = [ 'user_seq', 'user_id', 'member_id', 'contract_number', 'user_tag_serial', 'user_tag_serial_64', 'qr_code', 'tag_qr_flag', 'user_aid', 'user_place_qrid', 'user_categoryid', 'user_name', 'user_birthdate', 'user_age', 'user_mobile', 'user_homephone', 'user_primemail', 'user_submail', 'user_school', ]; $sort = $request->input('sort', 'user_seq'); $dirParam = strtolower((string) $request->input('dir', $request->input('sort_type', 'asc'))); $sortType = $dirParam === 'asc' ? 'asc' : 'desc'; if (!in_array($sort, $sortable, true)) { $sort = 'user_seq'; } // ▼ 絞り込み値('' のときだけ無視。'0' は有効値として扱う) $user_id = trim((string) $request->input('user_id', '')); $member_id = trim((string) $request->input('member_id', '')); $user_tag_serial = trim((string) $request->input('user_tag_serial', '')); $user_phonetic = trim((string) $request->input('user_phonetic', '')); $phone = trim((string) $request->input('phone', '')); // 携帯/自宅の両方対象 $crime = trim((string) $request->input('crime', '')); // 防犯登録番号(暫定: qr_code) $email = trim((string) $request->input('email', '')); $user_categoryid = (string) $request->input('user_categoryid', ''); $tag_qr_flag = (string) $request->input('tag_qr_flag', ''); // 0=タグ / 1=QR $quit_flag = (string) $request->input('quit_flag', ''); // 0=いいえ / 1=はい $quit_from = (string) $request->input('quit_from', ''); // YYYY-MM-DD $quit_to = (string) $request->input('quit_to', ''); // YYYY-MM-DD // ▼ ベースクエリ(一覧で使う列が多いので一旦 * を許容) $query = DB::table('user') ->leftJoin('usertype', 'user.user_categoryid', '=', 'usertype.user_categoryid') ->select( 'user.*', 'usertype.usertype_subject1', 'usertype.usertype_subject2', 'usertype.usertype_subject3' ); // ▼ テキスト系 if ($user_id !== '') $query->where('user.user_id', 'like', "%{$user_id}%"); if ($member_id !== '') $query->where('user.member_id', 'like', "%{$member_id}%"); if ($user_tag_serial !== '') $query->where('user.user_tag_serial', 'like', "%{$user_tag_serial}%"); if ($user_phonetic !== '') $query->where('user.user_phonetic', 'like', "%{$user_phonetic}%"); if ($phone !== '') { $query->where(function ($w) use ($phone) { $w->where('user.user_mobile', 'like', "%{$phone}%") ->orWhere('user.user_homephone', 'like', "%{$phone}%"); }); } if ($crime !== '') { // ※ dump に防犯登録番号の明確なカラムが無いため暫定的に qr_code を対象 $query->where('user.qr_code', 'like', "%{$crime}%"); } if ($email !== '') $query->where('user.user_primemail', 'like', "%{$email}%"); // ▼ セレクト/ラジオ('' 以外なら適用。'0' も通す) if ($user_categoryid !== '') $query->where('user.user_categoryid', $user_categoryid); if ($tag_qr_flag !== '') $query->where('user.tag_qr_flag', (int) $tag_qr_flag); if ($quit_flag !== '') $query->where('user.user_quit_flag', (int) $quit_flag); // ▼ 日付範囲(退会日) if ($quit_from !== '') $query->where('user.user_quitday', '>=', $quit_from); if ($quit_to !== '') $query->where('user.user_quitday', '<=', $quit_to); // ▼ 並び & ページング $list = $query->orderBy("user.{$sort}", $sortType)->paginate(20); // ▼ 画面に渡す(フォーム再描画用に絞り込み値も) return view('admin.users.list', [ 'list' => $list, 'sort' => $sort, 'sort_type' => $sortType, 'dir' => $sortType, 'user_id' => $user_id, 'member_id' => $member_id, 'user_tag_serial' => $user_tag_serial, 'user_phonetic' => $user_phonetic, 'phone' => $phone, 'crime' => $crime, 'email' => $email, 'user_categoryid' => $user_categoryid, 'tag_qr_flag' => $tag_qr_flag, 'quit_flag' => $quit_flag, 'quit_from' => $quit_from, 'quit_to' => $quit_to, 'categoryOptions' => $this->buildCategoryOptions(), ]); } /** * CSV 出力(一覧と同じ絞り込みを適用) */ public function export(Request $request): StreamedResponse { $q = DB::table('user'); // ▼ テキスト系 if (($v = trim((string) $request->input('user_id', ''))) !== '') $q->where('user_id', 'like', "%{$v}%"); if (($v = trim((string) $request->input('user_tag_serial', ''))) !== '') $q->where('user_tag_serial', 'like', "%{$v}%"); if (($v = trim((string) $request->input('user_phonetic', ''))) !== '') $q->where('user_phonetic', 'like', "%{$v}%"); if (($v = trim((string) $request->input('phone', ''))) !== '') { $q->where(function ($w) use ($v) { $w->where('user_mobile', 'like', "%{$v}%") ->orWhere('user_homephone', 'like', "%{$v}%"); }); } if (($v = trim((string) $request->input('email', ''))) !== '') $q->where('user_primemail', 'like', "%{$v}%"); // ▼ セレクト/ラジオ('' だけスキップ。'0' は適用) $val = (string) $request->input('user_categoryid', ''); if ($val !== '') $q->where('user_categoryid', $val); $val = (string) $request->input('tag_qr_flag', ''); if ($val !== '') $q->where('tag_qr_flag', (int) $val); $val = (string) $request->input('quit_flag', ''); if ($val !== '') $q->where('user_quit_flag', (int) $val); // ▼ 退会日 範囲 if (($from = (string) $request->input('quit_from', '')) !== '') $q->where('user_quitday', '>=', $from); if (($to = (string) $request->input('quit_to', '')) !== '') $q->where('user_quitday', '<=', $to); // ▼ 取得・並び $q->orderBy('user_seq', 'desc'); $rows = $q->get([ 'user_id', 'tag_qr_flag', 'user_categoryid', 'user_name', 'user_phonetic', 'user_birthdate', 'user_age', 'user_mobile', 'user_homephone', 'user_primemail', 'user_idcard', 'user_idcard_chk_flag', 'user_chk_day', 'user_quit_flag', 'user_quitday', ]); $headers = [ '利用者ID', 'タグ/QRフラグ', '利用者分類ID', '利用者名', 'フリガナ', '生年月日', '年齢', '携帯電話番号', '自宅電話番号', 'メールアドレス', '本人確認書類', '本人確認チェック済', '本人確認日時', '退会フラグ', '退会日', ]; $filename = 'users_' . date('Ymd_His') . '.csv'; return response()->streamDownload(function () use ($headers, $rows) { // ▼ BOM(Excel対策) echo "\xEF\xBB\xBF"; $out = fopen('php://output', 'w'); fputcsv($out, $headers); foreach ($rows as $r) { // ▼ 表示値変換 $tagQr = ((int) $r->tag_qr_flag === 1) ? 'QR' : 'タグ'; $idChk = ((int) ($r->user_idcard_chk_flag ?? 0) === 1) ? '手動チェックOK' : '未チェック'; $quitFlg = ((int) $r->user_quit_flag === 1) ? 'はい' : 'いいえ'; $birth = $r->user_birthdate ? mb_substr($r->user_birthdate, 0, 10) : ''; $chkDay = $r->user_chk_day ? mb_substr($r->user_chk_day, 0, 10) : ''; $quitDay = $r->user_quitday ? mb_substr($r->user_quitday, 0, 10) : ''; fputcsv($out, [ $r->user_id, $tagQr, $r->user_categoryid, $r->user_name, $r->user_phonetic, $birth, $r->user_age, $r->user_mobile, $r->user_homephone, $r->user_primemail, $r->user_idcard, $idChk, $chkDay, $quitFlg, $quitDay, ]); } fclose($out); }, $filename, [ 'Content-Type' => 'text/csv; charset=UTF-8', 'Content-Disposition' => 'attachment; filename="' . $filename . '"', ]); } /** * 利用者登録(GET: 画面表示 / POST: 登録実行) * - 必須は最小限(user_id, user_name, user_gender, user_primemail) * - created_at/updated_at は DB 側に任せるかここで now() を入れてもOK */ public function add(Request $request) { if ($request->isMethod('get')) { return view('admin.users.add'); } // ▼ バリデーション(user_id は半角数字のみ) $rules = [ 'user_id' => ['required', 'regex:/^\d+$/', 'digits_between:1,10'], // 半角数字(最大10桁) 'user_name' => ['required', 'string', 'max:255'], // 任意 'user_primemail' => ['nullable', 'email', 'max:255'], 'user_gender' => ['nullable', 'in:男性,女性'], 'member_id' => ['nullable', 'string', 'max:255'], 'user_mobile' => ['nullable', 'string', 'max:255'], 'user_homephone' => ['nullable', 'string', 'max:255'], 'user_birthdate' => ['nullable', 'date'], 'user_categoryid' => ['nullable', 'integer'], ]; // ▼ エラーメッセージ(日本語) $messages = [ 'user_id.required' => '利用者IDは必須です。', 'user_id.regex' => '利用者IDは半角数字のみで入力してください。', 'user_id.digits_between' => '利用者IDは最大10桁以内で入力してください。', 'user_name.required' => '氏名は必須です。', ]; // ▼ 属性名(日本語ラベル) $attributes = [ 'user_id' => '利用者ID', 'user_name' => '氏名', 'user_gender' => '性別', 'user_primemail' => '主メール', ]; $v = Validator::make($request->all(), $rules, $messages, $attributes); if ($v->fails()) { return back()->withErrors($v)->withInput(); } // 実在カラム名に合わせて挿入(不要なら削る/必要なら増やす) $data = [ 'user_id' => $request->input('user_id'), 'user_name' => $request->input('user_name'), 'user_gender' => $request->input('user_gender'), 'user_primemail' => $request->input('user_primemail'), 'member_id' => $request->input('member_id'), 'user_mobile' => $request->input('user_mobile'), 'user_homephone' => $request->input('user_homephone'), 'user_birthdate' => $request->input('user_birthdate'), 'user_categoryid' => $request->input('user_categoryid'), 'created_at' => now(), 'updated_at' => now(), ]; DB::table('user')->insert($data); return redirect()->route('users')->with('success', '利用者を登録しました。'); } /** * 利用者編集(GET: 表示 / POST: 更新) */ public function edit(Request $request, int $seq) { $user = DB::table('user')->where('user_seq', $seq)->first(); if (!$user) { abort(404, '利用者情報が見つかりません。'); } $operators = DB::table('ope') ->select('ope_id', 'ope_name') ->orderBy('ope_name') ->get(); $categoryOptions = $this->buildCategoryOptions(); if ($request->isMethod('get')) { return view('admin.users.edit', [ 'user' => $user, 'operators' => $operators, 'categoryOptions' => $categoryOptions, ]); } $rules = [ 'user_id' => ['required', 'regex:/^\d+$/', 'digits_between:1,10'], 'user_name' => ['required', 'string', 'max:255'], 'user_primemail' => ['nullable', 'email', 'max:255'], 'user_gender' => ['nullable', 'in:男性,女性,未入力'], 'member_id' => ['nullable', 'string', 'max:255'], 'user_mobile' => ['nullable', 'string', 'max:255'], 'user_homephone' => ['nullable', 'string', 'max:255'], 'user_birthdate' => ['nullable', 'date'], 'user_categoryid' => ['nullable', 'integer', 'exists:usertype,user_categoryid'], 'user_age' => ['nullable', 'integer', 'min:0'], 'user_chk_day' => ['nullable', 'date'], 'user_quitday' => ['nullable', 'date'], 'ope_id' => ['nullable', 'integer', 'exists:ope,ope_id'], ]; $messages = [ 'user_id.required' => '利用者IDは必須です。', 'user_id.regex' => '利用者IDは半角数字のみで入力してください。', 'user_id.digits_between' => '利用者IDは最大10桁以内で入力してください。', 'user_name.required' => '氏名は必須です。', ]; $validator = Validator::make($request->all(), $rules, $messages); if ($validator->fails()) { return back()->withErrors($validator)->withInput(); } $data = [ 'user_id' => $request->input('user_id'), 'member_id' => $request->input('member_id'), 'user_name' => $request->input('user_name'), 'user_gender' => $request->input('user_gender'), 'user_mobile' => $request->input('user_mobile'), 'user_homephone' => $request->input('user_homephone'), 'user_birthdate' => $request->input('user_birthdate'), 'user_age' => $request->input('user_age'), 'user_categoryid' => $request->input('user_categoryid'), 'user_phonetic' => $request->input('user_phonetic'), 'user_tag_serial' => $request->input('user_tag_serial'), 'user_tag_serial_64' => $request->input('user_tag_serial_64'), 'qr_code' => $request->input('qr_code'), 'tag_qr_flag' => $request->input('tag_qr_flag', '0'), 'user_aid' => $request->input('user_aid'), 'user_place_qrid' => $request->input('user_place_qrid'), 'user_primemail' => $request->input('user_primemail'), 'user_submail' => $request->input('user_submail'), 'ward_residents' => $request->input('ward_residents'), 'user_workplace' => $request->input('user_workplace'), 'user_school' => $request->input('user_school'), 'user_graduate' => $request->input('user_graduate'), 'user_idcard' => $request->input('user_idcard'), 'user_idcard_chk_flag' => $request->input('user_idcard_chk_flag', '0'), 'user_chk_day' => $request->input('user_chk_day'), 'user_chk_opeid' => $request->input('ope_id'), 'ope_id' => $request->input('ope_id'), 'user_regident_zip' => $request->input('user_regident_zip'), 'user_regident_pre' => $request->input('user_regident_pre'), 'user_regident_city' => $request->input('user_regident_city'), 'user_regident_add' => $request->input('user_regident_add'), 'user_relate_zip' => $request->input('user_relate_zip'), 'user_relate_pre' => $request->input('user_relate_pre'), 'user_relate_city' => $request->input('user_relate_city'), 'user_relate_add' => $request->input('user_relate_add'), 'user_tag_issue' => $request->input('user_tag_issue'), 'issue_permission' => $request->input('issue_permission', '1'), 'user_quit_flag' => $request->input('user_quit_flag', '0'), 'user_quitday' => $request->input('user_quitday'), 'user_remarks' => $request->input('user_remarks'), 'updated_at' => now(), ]; if ($request->filled('user_pass')) { $data['user_pass'] = Hash::make($request->input('user_pass')); } DB::table('user')->where('user_seq', $seq)->update($data); return redirect() ->route('users_edit', ['seq' => $seq]) ->with('status', '利用者情報を更新しました。'); } }