Files
ilycats/app/Models/Cat.php
T

244 lines
7.9 KiB
PHP
Raw Normal View History

2026-02-08 15:16:35 +07:00
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Storage;
use Carbon\Carbon;
class Cat extends Model
{
use SoftDeletes;
/**
* SINKRONISASI TOTAL: 22 Kolom Database
*/
protected $fillable = [
'nama_kucing', 'ras', 'registry', 'jenis_kelamin', 'tanggal_lahir',
'foto', 'berat_terakhir', 'lokasi', 'riwayat_sakit_terakhir',
'jadwal_vaksin', 'jadwal_obat_cacing', 'jadwal_obat_kutu',
'jadwal_grooming', 'induk_jantan_id', 'induk_betina_id',
'lokasi_kandang', 'pic_id', 'berat_badan',
2026-09-23 12:45:02 +07:00
'pedigree','sertifikat_ped_file', 'microchip', 'status', 'kode_ems','treatment_status'
2026-02-08 15:16:35 +07:00
];
2026-09-23 12:45:02 +07:00
/**
* Logic Otomatisasi (Booted)
*/
protected static function booted()
{
// A. Sebelum simpan: Sinkronisasi berat_terakhir
static::saving(function ($cat) {
if ($cat->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());
}
}
});
}
2026-02-08 15:16:35 +07:00
/**
* 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',
2026-09-23 12:45:02 +07:00
'registry' => 'array', // Otomatis mengurus JSON di DB
2026-02-08 15:16:35 +07:00
'berat_badan' => 'decimal:2',
'berat_terakhir' => 'decimal:2',
];
}
/* -------------------------------------------------------------------------- */
/* RELATIONS */
/* -------------------------------------------------------------------------- */
2026-09-23 12:45:02 +07:00
public function treatments()
{
return $this->hasMany(Treatment::class, 'cat_id');
}
2026-02-08 15:16:35 +07:00
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'
]);
}
2026-09-23 12:45:02 +07:00
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);
}
2026-02-08 15:16:35 +07:00
/* -------------------------------------------------------------------------- */
2026-09-23 12:45:02 +07:00
/* ACCESSORS (Penyempurnaan Null-Safety & Logic) */
2026-02-08 15:16:35 +07:00
/* -------------------------------------------------------------------------- */
protected function genderIcon(): Attribute
{
return Attribute::make(
2026-09-23 12:45:02 +07:00
get: fn () => strtolower($this->jenis_kelamin ?? '') === 'jantan'
? '<i class="fas fa-mars text-indigo-500" title="Jantan"></i>'
: '<i class="fas fa-venus text-rose-500" title="Betina"></i>'
2026-02-08 15:16:35 +07:00
);
}
protected function fotoUrl(): Attribute
{
return Attribute::make(
2026-09-23 12:45:02 +07:00
get: function () {
if ($this->foto && Storage::disk('public')->exists($this->foto)) {
return Storage::url($this->foto);
}
return asset('images/default-cat.png');
}
2026-02-08 15:16:35 +07:00
);
}
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());
2026-09-23 12:45:02 +07:00
return "{$diff->y}th {$diff->m}bln {$diff->d}hr";
2026-02-08 15:16:35 +07:00
}
);
}
2026-09-23 12:45:02 +07:00
/**
* Badge Registry yang disempurnakan (lebih bersih)
*/
2026-02-08 15:16:35 +07:00
protected function registryBadges(): Attribute
2026-09-23 12:45:02 +07:00
{
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 '<span class="px-2 py-0.5 bg-slate-100 text-slate-400 border border-slate-200 rounded-lg text-[8px] font-black uppercase tracking-tighter">NON-PED</span>';
2026-02-08 15:16:35 +07:00
}
$config = [
2026-09-23 12:45:02 +07:00
'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',
2026-02-08 15:16:35 +07:00
];
$html = '<div class="flex flex-wrap gap-1">';
2026-09-23 12:45:02 +07:00
$list = is_array($registries) ? $registries : [$registries];
2026-02-08 15:16:35 +07:00
2026-09-23 12:45:02 +07:00
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 .= "<span class='px-2 py-0.5 border rounded-lg text-[8px] font-black uppercase tracking-tighter shadow-sm {$style}'>{$uReg}</span>";
2026-02-08 15:16:35 +07:00
}
$html .= '</div>';
return $html;
}
);
}
2026-09-23 12:45:02 +07:00
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);
}
2026-02-08 15:16:35 +07:00
}