get(); $levels = DB::table('level_user')->orderBy('level_code')->get(); return view('setup.petugas.petugas', compact('petugas', 'levels')); } /** * Store a newly created resource in storage. */ public function store(Request $request) { $data = $request->validate($this->rules(), $this->messages()); // Normalisasi: string kosong jadi null (kolom date/int tidak terima ''). $data = array_map(fn ($v) => $v === '' ? null : $v, $data); $pegawai = Pegawai::create(collect($data)->except('foto')->all()); if ($request->hasFile('foto')) { $this->saveFoto($pegawai->id, $request->file('foto')); } return response()->json([ 'success' => true, 'message' => 'Petugas berhasil ditambahkan.', 'data' => $pegawai->fresh()->makeHidden('foto'), ], 201); } /** * Display the specified resource (tanpa binary foto). */ public function show($id) { $pegawai = Pegawai::select( 'id', 'nomer', 'nama', 'alamat', 'tgl_lahir', 'tgl_masuk', 'username', 'password', 'bisalogin', 'status', 'jeniskelamin', 'agama', 'telepon', 'alamat_lahir', 'alamat_sekarang', 'nip', 'statusabsen', 'level_pegawai', 'email' )->findOrFail($id); $hasFoto = (bool) DB::table('pegawai')->where('id', $id)->whereNotNull('foto')->exists(); return response()->json([ 'success' => true, 'data' => $pegawai, 'has_foto' => $hasFoto, ]); } /** * Update the specified resource in storage. */ public function update(Request $request, $id) { $pegawai = Pegawai::findOrFail($id); $data = $request->validate($this->rules($pegawai->id), $this->messages()); // Normalisasi: string kosong jadi null (kolom date/int tidak terima ''). $data = array_map(fn ($v) => $v === '' ? null : $v, $data); // Password kosong = tidak diubah (tetap pakai yang lama, plain-text ikut gaya legacy). if (empty($data['password'])) { unset($data['password']); } $pegawai->update(collect($data)->except('foto')->all()); if ($request->hasFile('foto')) { $this->saveFoto($pegawai->id, $request->file('foto')); } return response()->json([ 'success' => true, 'message' => 'Petugas berhasil diperbarui.', 'data' => $pegawai->fresh()->makeHidden('foto'), ]); } /** * Remove the specified resource from storage. */ public function destroy($id) { Pegawai::findOrFail($id)->delete(); return response()->json(['success' => true, 'message' => 'Petugas berhasil dihapus.']); } /** * Stream foto dari kolom bytea. Fallback ke placeholder bila kosong. */ public function foto($id) { $row = DB::table('pegawai')->select('foto')->where('id', $id)->first(); $binary = $row?->foto; if (is_resource($binary)) { $binary = stream_get_contents($binary); } if (empty($binary)) { return response()->file(public_path('images/no-image.jpg')); } $mime = (new \finfo(FILEINFO_MIME_TYPE))->buffer($binary) ?: 'image/jpeg'; return response($binary, 200, [ 'Content-Type' => $mime, 'Content-Length' => strlen($binary), 'Cache-Control' => 'private, max-age=3600', ]); } protected function messages(): array { return [ 'nomer.unique' => 'Nomer sudah dipakai petugas lain, ganti dengan yang unik.', 'username.unique' => 'Username sudah dipakai petugas lain, ganti dengan yang unik.', 'foto.image' => 'File foto harus berupa gambar.', 'foto.max' => 'Ukuran foto maksimal 2MB.', ]; } protected function rules(?int $ignoreId = null): array { $uniqueUsername = Rule::unique('pegawai', 'username'); $uniqueNomer = Rule::unique('pegawai', 'nomer'); if ($ignoreId) { $uniqueUsername->ignore($ignoreId, 'id'); $uniqueNomer->ignore($ignoreId, 'id'); } return [ 'nomer' => ['required', 'string', 'max:50', $uniqueNomer], 'nama' => 'required|string|max:50', 'username' => ['required', 'string', 'max:50', $uniqueUsername], // Plain-text ikut gaya data legacy. Wajib saat tambah, opsional saat ubah. 'password' => ($ignoreId ? 'nullable' : 'required') . '|string|max:50', 'level_pegawai' => 'nullable|string|exists:level_user,level_code', 'bisalogin' => 'nullable|integer|in:0,1', 'status' => 'nullable|integer|in:0,1', 'jeniskelamin' => 'nullable|integer|in:0,1', 'statusabsen' => 'nullable|integer|in:0,1', 'agama' => 'nullable|string|max:50', 'telepon' => 'nullable|string|max:255', 'alamat' => 'nullable|string|max:100', 'alamat_lahir' => 'nullable|string|max:255', 'alamat_sekarang' => 'nullable|string|max:255', 'nip' => 'nullable|string|max:50', 'email' => 'nullable|string|max:64', 'tgl_lahir' => 'nullable|date', 'tgl_masuk' => 'nullable|date', 'foto' => 'nullable|image|mimes:jpg,jpeg,png,bmp|max:2048', ]; } /** * Simpan binary ke kolom bytea tanpa nambah kolom baru. * bin2hex menghindari masalah escaping bytea. */ protected function saveFoto(int $id, $file): void { $binary = file_get_contents($file->getRealPath()); DB::table('pegawai')->where('id', $id)->update([ 'foto' => DB::raw("decode('" . bin2hex($binary) . "', 'hex')"), ]); } }