Initial commit: Struktur aplikasi ilycats
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\{Cat, User, Matting};
|
||||
use App\Traits\LogsActivity;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Support\Facades\{Storage, DB, Gate}; // Tambahkan Gate
|
||||
use Illuminate\Support\Carbon;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use SimpleSoftwareIO\QrCode\Facades\QrCode;
|
||||
use Intervention\Image\Laravel\Facades\Image;
|
||||
|
||||
class CatController extends Controller
|
||||
{
|
||||
use LogsActivity;
|
||||
|
||||
/**
|
||||
* Menampilkan daftar kucing dengan filter cerdas.
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
// Proteksi Akses: Read
|
||||
Gate::authorize('check-access', ['cats', 'read']);
|
||||
|
||||
$query = Cat::query()->with(['pic', 'sire', 'dam']);
|
||||
|
||||
// 1. Pencarian Global
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->search;
|
||||
$query->where(function($q) use ($search) {
|
||||
$q->where('nama_kucing', 'like', "%{$search}%")
|
||||
->orWhere('ras', 'like', "%{$search}%")
|
||||
->orWhere('microchip', 'like', "%{$search}%")
|
||||
->orWhere('pedigree', 'like', "%{$search}%")
|
||||
->orWhere('kode_ems', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Filter Spesifik
|
||||
if ($request->filled('status')) $query->where('status', $request->status);
|
||||
if ($request->filled('ras')) $query->where('ras', $request->ras);
|
||||
if ($request->filled('gender')) $query->where('jenis_kelamin', $request->gender);
|
||||
|
||||
$cats = $query->latest()->paginate(15)->withQueryString();
|
||||
|
||||
// 3. Statistik Dashboard (Tetap Utuh)
|
||||
$totalCats = Cat::count();
|
||||
$safeWords = ['sehat', 'normal', 'aktif', 'sembuh', '-', 'tidak ada', 'clean'];
|
||||
$sickCats = Cat::whereNotNull('riwayat_sakit_terakhir')
|
||||
->where(function($q) use ($safeWords) {
|
||||
foreach ($safeWords as $word) {
|
||||
$q->where('riwayat_sakit_terakhir', 'NOT LIKE', "%{$word}%");
|
||||
}
|
||||
})->count();
|
||||
|
||||
$breeds = Cat::distinct()->pluck('ras');
|
||||
|
||||
return view('pages.cats.index', compact('cats', 'totalCats', 'sickCats', 'breeds'));
|
||||
}
|
||||
|
||||
public function create(): View
|
||||
{
|
||||
// Proteksi Akses: Create
|
||||
Gate::authorize('check-access', ['cats', 'create']);
|
||||
|
||||
$sires = Cat::where('jenis_kelamin', 'Jantan')->get(['id', 'nama_kucing']);
|
||||
$dams = Cat::where('jenis_kelamin', 'Betina')->get(['id', 'nama_kucing']);
|
||||
$users = User::all(['id', 'name']);
|
||||
|
||||
return view('pages.cats.create', compact('sires', 'dams', 'users'));
|
||||
}
|
||||
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
Gate::authorize('check-access', ['cats', 'create']);
|
||||
|
||||
$validated = $this->validateCat($request);
|
||||
|
||||
return DB::transaction(function () use ($request, $validated) {
|
||||
if ($request->hasFile('foto')) {
|
||||
$validated['foto'] = $this->handleImageUpload($request->file('foto'));
|
||||
}
|
||||
|
||||
if (isset($validated['berat_badan'])) {
|
||||
$validated['berat_terakhir'] = $validated['berat_badan'];
|
||||
}
|
||||
|
||||
if (empty($validated['status'])) {
|
||||
$validated['status'] = ($request->induk_jantan_id || $request->induk_betina_id) ? 'Kitten' : 'Active';
|
||||
}
|
||||
|
||||
// Memastikan registry disimpan sebagai JSON jika model tidak melakukan casting
|
||||
if (isset($validated['registry'])) {
|
||||
$validated['registry'] = $validated['registry'];
|
||||
}
|
||||
|
||||
$cat = Cat::create($validated);
|
||||
$this->logAction('CREATED', $cat, "Mendaftarkan pasien baru: {$cat->nama_kucing}", 'Cat');
|
||||
|
||||
return redirect()->route('cats.index')->with('success', "{$cat->nama_kucing} berhasil didaftarkan.");
|
||||
});
|
||||
}
|
||||
|
||||
public function show(Cat $cat): View
|
||||
{
|
||||
Gate::authorize('check-access', ['cats', 'read']);
|
||||
|
||||
$cat->load([
|
||||
'treatments' => fn($q) => $q->latest(),
|
||||
'sire', 'dam', 'pic',
|
||||
'weightHistories' => fn($q) => $q->latest()->limit(12)
|
||||
]);
|
||||
return view('pages.cats.show', compact('cat'));
|
||||
}
|
||||
|
||||
public function edit(Cat $cat): View
|
||||
{
|
||||
// Proteksi: Akses Update
|
||||
Gate::authorize('check-access', ['cats', 'update']);
|
||||
|
||||
$sires = Cat::where('jenis_kelamin', 'Jantan')->where('id', '!=', $cat->id)->get(['id', 'nama_kucing']);
|
||||
$dams = Cat::where('jenis_kelamin', 'Betina')->where('id', '!=', $cat->id)->get(['id', 'nama_kucing']);
|
||||
$users = User::all(['id', 'name']);
|
||||
|
||||
return view('pages.cats.edit', compact('cat', 'sires', 'dams', 'users'));
|
||||
}
|
||||
|
||||
public function update(Request $request, Cat $cat): RedirectResponse
|
||||
{
|
||||
Gate::authorize('check-access', ['cats', 'update']);
|
||||
|
||||
$validated = $this->validateCat($request, $cat->id);
|
||||
|
||||
return DB::transaction(function () use ($request, $validated, $cat) {
|
||||
if ($request->hasFile('foto')) {
|
||||
if ($cat->foto) Storage::disk('public')->delete($cat->foto);
|
||||
$validated['foto'] = $this->handleImageUpload($request->file('foto'));
|
||||
}
|
||||
|
||||
if ($request->filled('berat_badan')) {
|
||||
$validated['berat_terakhir'] = $request->berat_badan;
|
||||
if ($cat->berat_badan != $request->berat_badan) {
|
||||
$cat->weightHistories()->create(['berat' => $request->berat_badan]);
|
||||
}
|
||||
}
|
||||
|
||||
$cat->update($validated);
|
||||
$this->logAction('UPDATED', $cat, "Update data medis/profil: {$cat->nama_kucing}", 'Cat');
|
||||
|
||||
return redirect()->route('cats.index')->with('success', 'Data berhasil diperbarui.');
|
||||
});
|
||||
}
|
||||
|
||||
public function destroy(Cat $cat): RedirectResponse
|
||||
{
|
||||
Gate::authorize('check-access', ['cats', 'delete']);
|
||||
|
||||
$isParent = Matting::where('sire_id', $cat->id)->orWhere('dam_id', $cat->id)->exists();
|
||||
|
||||
if ($isParent) {
|
||||
return back()->with('error', "Gagal! {$cat->nama_kucing} tidak bisa dihapus karena sudah menjadi Induk.");
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($cat) {
|
||||
$name = $cat->nama_kucing;
|
||||
if ($cat->foto) Storage::disk('public')->delete($cat->foto);
|
||||
|
||||
$this->logAction('DELETED', $cat, "Menghapus data permanen: {$name}", 'Cat');
|
||||
$cat->delete();
|
||||
|
||||
return redirect()->route('cats.index')->with('success', "Data {$name} telah dihapus.");
|
||||
});
|
||||
}
|
||||
|
||||
public function getCatData($id): JsonResponse
|
||||
{
|
||||
$cat = Cat::find($id);
|
||||
if (!$cat) return response()->json(['error' => 'Not found'], 404);
|
||||
|
||||
return response()->json([
|
||||
'tgl_lahir' => $cat->tanggal_lahir ? $cat->tanggal_lahir->format('Y-m-d') : '',
|
||||
'pedigree' => $cat->pedigree ?? '',
|
||||
'microchip' => $cat->microchip ?? '',
|
||||
'kode_ems' => $cat->kode_ems ?? '',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* PDF ID Card / Medical Pass
|
||||
* PENYEMPURNAAN: Perbaikan Barcode (Base64 SVG)
|
||||
*/
|
||||
public function downloadPdf(Cat $cat)
|
||||
{
|
||||
Gate::authorize('check-access', ['cats', 'read']);
|
||||
|
||||
$cat->load(['pic', 'sire', 'dam']);
|
||||
|
||||
// FIX: Konversi SVG ke Base64 agar tampil di domPDF tanpa Imagick
|
||||
$qrCodeRaw = QrCode::format('svg')
|
||||
->size(150)
|
||||
->margin(1)
|
||||
->generate(route('cats.show', $cat->id));
|
||||
|
||||
$qrCode = base64_encode($qrCodeRaw);
|
||||
|
||||
$pdf = Pdf::loadView('pages.cats.pdf-card', [
|
||||
'cat' => $cat,
|
||||
'qrCode' => $qrCode, // Dikirim sebagai Base64
|
||||
'printedAt' => Carbon::now()->translatedFormat('d F Y H:i')
|
||||
]);
|
||||
|
||||
return $pdf->setPaper([0, 0, 300, 500], 'portrait')
|
||||
->stream('MEDICAL_PASS_' . strtoupper($cat->nama_kucing) . '.pdf');
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* HELPERS (TETAP UTUH) */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
private function validateCat(Request $request, $id = null)
|
||||
{
|
||||
return $request->validate([
|
||||
'nama_kucing' => 'required|string|max:255',
|
||||
'ras' => 'required|string',
|
||||
'jenis_kelamin' => 'required|in:Jantan,Betina',
|
||||
'tanggal_lahir' => 'nullable|date',
|
||||
'foto' => 'nullable|image|mimes:jpg,jpeg,png|max:3072',
|
||||
'registry' => 'nullable|array',
|
||||
'registry.*' => 'string',
|
||||
'pedigree' => 'nullable|string|max:255',
|
||||
'microchip' => 'nullable|string|max:255',
|
||||
'kode_ems' => 'nullable|string|max:255',
|
||||
'status' => 'nullable|string',
|
||||
'induk_jantan_id' => 'nullable|exists:cats,id',
|
||||
'induk_betina_id' => 'nullable|exists:cats,id',
|
||||
'lokasi_kandang' => 'nullable|string',
|
||||
'pic_id' => 'nullable|exists:users,id',
|
||||
'berat_badan' => 'nullable|numeric|min:0',
|
||||
'riwayat_sakit_terakhir' => 'nullable|string',
|
||||
'jadwal_vaksin' => 'nullable|date',
|
||||
'jadwal_obat_cacing' => 'nullable|date',
|
||||
'jadwal_obat_kutu' => 'nullable|date',
|
||||
'jadwal_grooming' => 'nullable|date',
|
||||
]);
|
||||
}
|
||||
|
||||
private function handleImageUpload($file): string
|
||||
{
|
||||
$filename = 'cat_' . uniqid() . '.png';
|
||||
$path = 'cats/' . $filename;
|
||||
$img = Image::read($file)->cover(500, 500, 'center')->toPng();
|
||||
Storage::disk('public')->put($path, (string) $img);
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user