HEX
Server: LiteSpeed
System: Linux sv4.hami.host 5.14.0-611.54.3.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 7 16:31:24 EDT 2026 x86_64
User: armgroup (1008)
PHP: 8.2.32
Disabled: show_source, system, shell_exec, passthru, exec, popen, proc_open, mail, socket_create, socket_create_listen, socket_create_pair, link, dl, openlog, syslog, stream_socket_server, curl_multi_init
Upload Files
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;
    }
}