isDirty('berat_badan')) { $cat->berat_terakhir = $cat->berat_badan; } }); // B. Setelah update berhasil: Catat ke tabel history static::updated(function ($cat) { if ($cat->wasChanged('berat_badan')) { // Kita gunakan try-catch agar jika tabel history bermasalah, update kucing tidak ikut mati try { $cat->weightHistories()->create([ 'berat' => $cat->berat_badan, 'tanggal_timbang' => now(), // Pastikan kolom ini ada di migrasi Anda ]); } catch (\Exception $e) { // Log error diam-diam agar user tidak melihat Error 500 \Illuminate\Support\Facades\Log::error("Gagal mencatat history berat: " . $e->getMessage()); } } }); } /** * Casting Laravel 11/12 Style */ protected function casts(): array { return [ 'tanggal_lahir' => 'date', 'jadwal_vaksin' => 'date', 'jadwal_obat_cacing' => 'date', 'jadwal_obat_kutu' => 'date', 'jadwal_grooming' => 'date', 'registry' => 'array', // Otomatis mengurus JSON di DB 'berat_badan' => 'decimal:2', 'berat_terakhir' => 'decimal:2', ]; } /* -------------------------------------------------------------------------- */ /* RELATIONS */ /* -------------------------------------------------------------------------- */ public function treatments() { return $this->hasMany(Treatment::class, 'cat_id'); } public function weightHistories(): HasMany { return $this->hasMany(WeightHistory::class)->latest(); } public function sire(): BelongsTo { return $this->belongsTo(Cat::class, 'induk_jantan_id')->withDefault([ 'nama_kucing' => 'Unknown Sire' ]); } public function dam(): BelongsTo { return $this->belongsTo(Cat::class, 'induk_betina_id')->withDefault([ 'nama_kucing' => 'Unknown Dam' ]); } public function mattingsAsSire(): HasMany { return $this->hasMany(Matting::class, 'sire_id'); } public function mattingsAsDam(): HasMany { return $this->hasMany(Matting::class, 'dam_id'); } public function pic(): BelongsTo { return $this->belongsTo(User::class, 'pic_id')->withDefault([ 'name' => 'System' ]); } public function registrations(): HasMany { return $this->hasMany(CatRegistration::class)->orderByDesc('is_primary')->orderBy('id'); } public function primaryRegistration() { return $this->hasOne(CatRegistration::class)->where('is_primary', true); } /* -------------------------------------------------------------------------- */ /* ACCESSORS (Penyempurnaan Null-Safety & Logic) */ /* -------------------------------------------------------------------------- */ protected function genderIcon(): Attribute { return Attribute::make( get: fn () => strtolower($this->jenis_kelamin ?? '') === 'jantan' ? '' : '' ); } protected function fotoUrl(): Attribute { return Attribute::make( get: function () { if ($this->foto && Storage::disk('public')->exists($this->foto)) { return Storage::url($this->foto); } return asset('images/default-cat.png'); } ); } protected function age(): Attribute { return Attribute::make( get: function () { if (!$this->tanggal_lahir) return 'N/A'; $diff = $this->tanggal_lahir->diff(now()); if ($diff->y > 0) return "{$diff->y} Thn, {$diff->m} Bln"; if ($diff->m > 0) return "{$diff->m} Bln, {$diff->d} Hari"; return "{$diff->d} Hari"; } ); } protected function usiaDetail(): Attribute { return Attribute::make( get: function () { if (!$this->tanggal_lahir) return 'Data N/A'; $diff = $this->tanggal_lahir->diff(now()); return "{$diff->y}th {$diff->m}bln {$diff->d}hr"; } ); } /** * Badge Registry yang disempurnakan (lebih bersih) */ protected function registryBadges(): Attribute { return Attribute::make( get: function () { $registries = $this->registry; // Jika kosong atau eksplisit memilih NON-PED if (empty($registries) || (is_array($registries) && in_array('NON-PED', $registries))) { return 'NON-PED'; } $config = [ 'ICA' => 'bg-blue-50 text-blue-700 border-blue-100', 'CFA' => 'bg-rose-50 text-rose-700 border-rose-100', 'WCF' => 'bg-emerald-50 text-emerald-700 border-emerald-100', 'FFF' => 'bg-amber-50 text-amber-700 border-amber-100', ]; $html = '
'; $list = is_array($registries) ? $registries : [$registries]; foreach ($list as $reg) { if($reg === 'NON-PED') continue; // Skip jika tercampur (opsional) $uReg = strtoupper($reg); $style = $config[$uReg] ?? 'bg-slate-50 text-slate-600 border-slate-200'; $html .= "{$uReg}"; } $html .= '
'; return $html; } ); } public function medicalRecords() { // Mengambil riwayat, terbaru diletakkan paling atas return $this->hasMany(MedicalRecord::class)->orderBy('date_given', 'desc'); } /** * Sinkronkan kolom status perawatan dari treatment yang masih aktif. * Nilai 1 = dalam perawatan, 2 = sehat. */ public function updateTreatmentStatus(): void { $status = $this->treatments()->where('status_perawatan', 1)->exists() ? 1 : 2; $this->forceFill(['treatment_status' => $status])->saveQuietly(); } /** * Scope untuk filter kucing yang sedang sakit (Under Treatment) */ public function scopeUnderTreatment($query) { // Hapus logika whereHas lama, ganti dengan ini: return $query->where('treatment_status', 1); } public function scopeHealthy($query) { return $query->where('treatment_status', 2); } }