File: /home/armgroup/libs/app/Models/Product.php
<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Product extends Model
{
use HasFactory;
protected $guarded = ['id'];
protected $casts = [
'faq' => 'array',
];
public function getRouteKeyName()
{
return 'slug';
}
protected static function boot()
{
parent::boot();
static::addGlobalScope('order', function (Builder $builder) {
$builder->orderByRaw('IF(products.stock > 0, 0, 1), ISNULL(products.price), ISNULL(products.sort_order), products.sort_order ASC');
});
}
public function categories()
{
return $this->morphToMany(Category::class, 'categorizable')->withPivot('is_main');
}
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
public function approvedComments()
{
return $this->comments()->where('status', Comment::STATUS_APPROVED);
}
public function favorites()
{
return $this->morphMany(Favorite::class, 'favoritable');
}
public function scores()
{
return $this->morphMany(Score::class, 'scorable');
}
public function images()
{
return $this->morphMany(Image::class, 'imageable');
}
public function getUrlAttribute()
{
return route('products.show', $this->slug);
}
public function attributes()
{
return $this->belongsToMany(Attribute::class);
}
public function values()
{
return $this->belongsToMany(AttributeValue::class, 'attribute_product', 'product_id', 'value_id');
}
public function options()
{
return $this->belongsToMany(Option::class)
->withPivot('value_id', 'price', 'stock', 'code', 'special', 'special_started_at', 'special_ended_at')
->groupBy('option_id');
}
public function optionValues()
{
return $this->belongsToMany(OptionValue::class, 'option_product', 'product_id', 'value_id')
->withPivot('value_id', 'price', 'stock', 'code', 'special', 'special_started_at', 'special_ended_at');
}
public function activeOptionValues()
{
return $this->optionValues()->where('stock', '>', 0)->oldest('price');
}
public function scopeMinPrice($query)
{
return $query->orderByRaw('IF(products.stock > 0, 0, 1), ISNULL(products.price), CASE WHEN (products.special > 0 And products.special_started_at <= ? AND products.special_ended_at >= ? ) THEN products.special ELSE products.price END DESC', [Carbon::now(), Carbon::now()]);
}
public function scopeMaxPrice($query)
{
return $query->orderByRaw('IF(products.stock > 0, 0, 1), ISNULL(products.price), CASE WHEN (products.special > 0 And products.special_started_at <= ? AND products.special_ended_at >= ? ) THEN products.special ELSE products.price END ASC', [Carbon::now(), Carbon::now()]);
}
public function brand()
{
return $this->belongsTo(Brand::class)->withDefault();
}
public function getSpecialMaskAttribute()
{
if ($this->special && $this->special_started_at <= Carbon::now() && $this->special_ended_at >= Carbon::now()) {
return $this->special;
}
return null;
}
public function orders()
{
return $this->belongsToMany(Order::class)
->using(OrderProduct::class)
->withPivot('quantity', 'price', 'discount', 'option_id', 'value_id', 'name');
}
public function lockedStocks()
{
return $this->hasMany(LockedStock::class);
}
public function CanBuy(int $qty, $options, int $price): string
{
if (! $this->status) {
return "فروش {$this->name} موقتا غیر فعال شده است، برای ادامه خرید این محصول را از سبد خرید حذف کنید";
}
$hasOption = count($options);
$name = $this->name.($hasOption ? "({$options['name']})" : '');
if ($hasOption) {
$optionProduct = OptionProduct::where('product_id', $this->id)
->where('value_id', $options['value_id'])
->where('option_id', $options['option_id'])
->first();
if (! $optionProduct) {
return "سایت در حال بروزرسانی میباشد و امکان خرید {$name} نمیباشد. لطفا بعدا تلاش کنید";
}
$product_stock = $optionProduct->stock;
// $product_price = $optionProduct->special_mask ?: $optionProduct->price;
$product_price = ($this->special?: $this->price ) + $optionProduct->price;
} else {
$product_stock = $this->stock;
$product_price = $this->special ?: $this->price;
}
if ($product_stock < $qty || $this->checkLockedStock($product_stock, $options, $qty)) {
return "موجودی {$name} کافی نمیباشد!";
}
if ($product_price != $price) {
return "قیمت {$name} تغییر کرده است، لطفا محصول را پاک کرده و دوباره به سبد خرید اضافه کنید!";
}
return 'OK';
}
private function checkLockedStock($product_stock, $options, int $qty): bool
{
return $product_stock - ($this->lockedStocks()
->when(count($options), function ($query) use ($options) {
return $query->where('value_id', $options['value_id'])
->where('option_id', $options['option_id']);
})->where('created_at', '>=', Carbon::now()->subMinutes(LockedStock::LOCKED_TIME))
->sum('quantity') ?: 0) < $qty;
}
public function user()
{
return $this->belongsTo(User::class);
}
public function demands()
{
return $this->belongsToMany(User::class, 'product_demands')->using(ProductDemand::class);
}
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
public function getResized300200ImageAttribute()
{
if (! file_exists(public_path('resized-images/products/300-200/'.basename($this->image)))) {
$img = \Intervention\Image\Facades\Image::make($this->image)->resize(300, 200);
$img->save(public_path('resized-images/products/300-200/'.basename($this->image)), 85);
}
return 'resized-images/products/300-200/'.basename($this->image);
}
public function getDiscountAttribute()
{
return round((($this->price - $this->special) / $this->price) * 100);
}
public function getSpecialAttribute()
{
if (auth()->check() && auth()->user()->organization_id) {
$special = DB::table('organization_product')
->where('product_id', $this->id)
->where('organization_id', auth()->user()->organization_id)
->where('special_started_at', '<=', now())
->where('special_ended_at', '>=', now())
->select('organization_product.special')
->first();
if ($special) {
return $special->special;
}
}
return DB::table('organization_product')
->where('product_id', $this->id)
->whereNull('organization_id')
->where('special_started_at', '<=', now())
->where('special_ended_at', '>=', now())
->select('organization_product.special')
->first()?->special ?: null;
}
}