'boolean', ]; public function brand(): BelongsTo { return $this->belongsTo(Brand::class); } public function category(): BelongsTo { return $this->belongsTo(Category::class); } public function variations(): HasMany { return $this->hasMany(ProductVariation::class); } public function attributes(): HasMany { return $this->hasMany(ProductAttribute::class); } public function getDefaultVariationAttribute() { $defaultInStock = $this->variations->firstWhere(function ($variation) { return $variation->is_default && $variation->stock > 0; }); if ($defaultInStock) { return $defaultInStock; } $anyInStock = $this->variations->firstWhere('stock', '>', 0); if ($anyInStock) { return $anyInStock; } return $this->variations->firstWhere('is_default', true) ?? $this->variations->first(); } /** * Получить URL главного изображения товара (из дефолтной вариации) */ public function getMainImageAttribute(): string { $defaultVariation = $this->default_variation; if ($defaultVariation) { return $defaultVariation->main_image_url; } return asset('assets/images/products/default.svg'); } /** * Получить изображения дефолтной вариации */ public function getDefaultVariationImages() { $defaultVariation = $this->default_variation; if ($defaultVariation) { return $defaultVariation->images; } return collect(); } public static function getForCarousel($categoryId = null, $limit = 9, $itemsPerSlide = 3) { $query = self::with(['category', 'brand', 'variations']) ->active(); if ($categoryId) { $query->byCategory($categoryId); } $products = $query->limit($limit)->get(); $products = $products->filter(function ($product) { return $product->variations->isNotEmpty(); }); $products = $products->sortByDesc(function ($product) { $defaultVariation = $product->default_variation; if ($defaultVariation && $defaultVariation->stock > 0) { return 3; } elseif ($defaultVariation) { return 2; } elseif ($product->variations->firstWhere('stock', '>', 0)) { return 1; } return 0; }); return $products->chunk($itemsPerSlide); } public function getRatingAttribute(): array { return [ 'score' => 4.9, 'count' => 1000 ]; } public function getMinPriceAttribute(): float { return $this->variations->min('price') ?? 0; } public function getMaxPriceAttribute(): float { return $this->variations->max('price') ?? 0; } public function getPriceRangeAttribute(): string { $min = $this->min_price; $max = $this->max_price; if ($min == $max) { return number_format($min, 0, '.', ' ') . ' ₽'; } return 'от ' . number_format($min, 0, '.', ' ') . ' ₽ до ' . number_format($max, 0, '.', ' ') . ' ₽'; } public function getTotalStockAttribute(): int { return $this->variations->sum('stock'); } public function getHasStockAttribute(): bool { return $this->total_stock > 0; } public function getFullNameAttribute(): string { return $this->brand ? "[{$this->brand->name}] {$this->name}" : $this->name; } public function scopeActive($query) { return $query->where('is_active', true); } public function scopeByBrand($query, $brandId) { return $query->where('brand_id', $brandId); } public function scopeByCategory($query, $categoryId) { return $query->where('category_id', $categoryId); } }