515 lines
21 KiB
PHP
515 lines
21 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Admin;
|
||
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Illuminate\Support\Facades\Validator;
|
||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||
use Illuminate\Support\Facades\Hash;
|
||
|
||
class UsersController
|
||
{
|
||
/**
|
||
* 利用者分類選択肢を取得
|
||
*/
|
||
private function buildCategoryOptions(): array
|
||
{
|
||
return DB::table('usertype')
|
||
->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_pass' => ['required', 'string', 'min:8', 'confirmed'],
|
||
// 任意
|
||
'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' => '氏名は必須です。',
|
||
'user_pass.required' => 'パスワードは必須です。',
|
||
'user_pass.min' => 'パスワードは8文字以上で入力してください。',
|
||
'user_pass.confirmed' => 'パスワードと確認用パスワードが一致しません。',
|
||
];
|
||
|
||
// ▼ 属性名(日本語ラベル)
|
||
$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();
|
||
|
||
// ▼ 退会処理専用(hiddenフィールド quit_action があれば退会処理)
|
||
if ($request->has('quit_action')) {
|
||
DB::table('user')->where('user_seq', $seq)->update([
|
||
'user_quit_flag' => 1,
|
||
'user_quitday' => now()->format('Y-m-d'),
|
||
'ope_id' => $request->input('ope_id') ?? auth()->user()->ope_id ?? null,
|
||
'updated_at' => now(),
|
||
]);
|
||
|
||
return redirect()
|
||
->route('users_edit', ['seq' => $seq])
|
||
->with('status', '退会処理が完了しました。');
|
||
}
|
||
|
||
|
||
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_pass' => ['nullable', 'string', 'min:8', 'confirmed'],
|
||
'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', '利用者情報を更新しました。');
|
||
}
|
||
|
||
/**
|
||
* 利用者削除(POST: 削除実行)
|
||
* - user_seq をキーに削除処理を行う
|
||
* - 削除前に存在確認を行い、存在しない場合はエラーを返す
|
||
* - 削除完了後、一覧画面へリダイレクト
|
||
*/
|
||
public function delete(Request $request)
|
||
{
|
||
// ▼ パラメータ取得
|
||
$userSeq = (int) $request->input('user_seq');
|
||
|
||
// ▼ 対象レコード存在確認
|
||
$user = DB::table('user')->where('user_seq', $userSeq)->first();
|
||
if (!$user) {
|
||
// 該当データなし
|
||
return redirect()
|
||
->route('users')
|
||
->with('error', '利用者情報が見つかりません。');
|
||
}
|
||
|
||
// ▼ 削除処理実行
|
||
DB::table('user')->where('user_seq', $userSeq)->delete();
|
||
|
||
// ▼ 正常終了メッセージを一覧画面に表示
|
||
return redirect()
|
||
->route('users')
|
||
->with('success', '利用者を削除しました。');
|
||
}
|
||
|
||
}
|