belongsTo(Country::class); } public function price(): HasMany { return $this->hasMany(Price::class); } public function isStarred(): bool { return (bool)$this->starred; } public function currentPrice(): HasOne { return $this->hasOne(Price::class)->latestOfMany('created_at'); } public function lowestPrice(): HasOne { return $this->hasOne(Price::class)->ofMany('price', 'MIN'); } public function newestPrice(): HasOne { return $this->hasOne(Price::class)->latest()->take(1); } public function stock(): HasMany { return $this->hasMany(Stock::class); } public function currentStock(): HasOne { return $this->stock()->one()->ofMany()->withDefault(fn(Stock $stock) => $stock->stock = 0)->take(1); } public function toggleStarred(): self { $this->starred = !$this->starred; return $this; } public function categories(): Attribute { return Attribute::make( get: fn(string $value) => array_reverse(json_decode($value, 1)), set: fn(array $value) => json_encode($value), ); } public function promotions(): Attribute { return Attribute::make( get: fn(?string $value) => json_decode($value ?? '{"hasPromotion": false}', 1), set: function (\stdClass $value) { $value->slug = Str::slug($value->tag); return json_encode($value); } ); } public function isDiscontinued(): bool { return $this->lastSeen < now()->format('Y-m-d'); } public function isNew(): bool { return $this->created_at->format('Y-m-d') > now()->modify('-30 days')->format('Y-m-d'); } }