MOON
Server: Apache
System: Linux server.royaltuning.hu 4.18.0-425.13.1.el8_7.x86_64 #1 SMP Tue Feb 21 04:20:52 EST 2023 x86_64
User: royaltuning (1001)
PHP: 8.2.32
Disabled: exec,passthru,shell_exec,system
Upload Files
File: //var/www/html/Modules/Product/Entities/Product.php
<?php

namespace Modules\Product\Entities;

use Carbon\Carbon;
use Modules\Support\Money;
use Modules\Tag\Entities\Tag;
use Modules\Media\Entities\File;
use Modules\Brand\Entities\Brand;
use Modules\Tax\Entities\TaxClass;
use Modules\Option\Entities\Option;
use Modules\Review\Entities\Review;
use Modules\Support\Eloquent\Model;
use Modules\Media\Eloquent\HasMedia;
use Modules\Meta\Eloquent\HasMetaData;
use Modules\Support\Search\Searchable;
use Modules\Category\Entities\Category;
use Modules\Product\Admin\ProductTable;
use Modules\Support\Eloquent\Sluggable;
use Modules\FlashSale\Entities\FlashSale;
use Modules\Support\Eloquent\Translatable;
use Illuminate\Database\Eloquent\SoftDeletes;
use Modules\Attribute\Entities\ProductAttribute;

use Spatie\Feed\Feedable;
use Spatie\Feed\FeedItem;

class Product extends Model implements Feedable
{
    use Translatable, Searchable, Sluggable, HasMedia, HasMetaData, SoftDeletes;

    /**
     * The relations to eager load on every query.
     *
     * @var array
     */
    protected $with = ['translations'];

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['brand_id', 'arukereso_in','tax_class_id', 'slug', 'sku', 'brand_product_number','qty_type', 'price', 'special_price', 'special_price_type', 'special_price_start', 'special_price_end', 'selling_price', 'manage_stock', 'qty', 'video', 'in_stock', 'is_virtual', 'is_active', 'new_from', 'new_to'];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'manage_stock' => 'boolean',
        'in_stock' => 'boolean',
        'is_active' => 'boolean',
    ];

    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = ['special_price_start', 'special_price_end', 'new_from', 'new_to', 'start_date', 'end_date', 'deleted_at'];

    /**
     * The accessors to append to the model's array form.
     *
     * @var array
     */
    protected $appends = ['base_image', 'formatted_price', 'rating_percent', 'is_in_stock', 'is_out_of_stock', 'is_new', 'has_percentage_special_price', 'special_price_percent'];

    /**
     * The attributes that are translatable.
     *
     * @var array
     */
    protected $translatedAttributes = ['name', 'description', 'short_description'];

    /**
     * The attribute that will be slugged.
     *
     * @var string
     */
    protected $slugAttribute = 'name';

    /**
     * Perform any actions required after the model boots.
     *
     * @return void
     */
    protected static function booted()
    {
        static::saved(function ($product) {
            if (!empty(request()->all())) {
                $product->saveRelations(request()->all());
            }

            $product->withoutEvents(function () use ($product) {
                $product->update(['selling_price' => $product->getSellingPrice()->amount()]);
            });
        });

        static::addActiveGlobalScope();
    }

    public static function newArrivals($limit)
    {
        return static::forCard()
            ->latest()
            ->take($limit)
            ->get();
    }

    public static function list($ids = [])
    {
        return static::select('id')
            ->withName()
            ->whereIn('id', $ids)
            ->when(!empty($ids), function ($query) use ($ids) {
                $idsString = collect($ids)
                    ->filter()
                    ->implode(',');

                $query->orderByRaw("FIELD(id, {$idsString})");
            })
            ->get()
            ->mapWithKeys(function ($product) {
                return [$product->id => $product->name];
            });
    }

    public function scopeForCard($query)
    {
        $query
            ->withName()
            ->withBaseImage()
            ->withPrice()
            ->withCount('options')
            ->with('reviews')
            ->addSelect(['products.id', 'products.slug', 'products.in_stock', 'products.manage_stock', 'products.qty', 'products.new_from', 'products.new_to']);
    }

    public function scopeWithPrice($query)
    {
        $query->addSelect(['products.price', 'products.special_price', 'products.special_price_type', 'products.selling_price', 'products.special_price_start', 'products.special_price_end']);
    }

    public function scopeWithName($query)
    {
        $query->with('translations:id,product_id,locale,name');
    }

    public function scopeWithBaseImage($query)
    {
        $query->with([
            'files' => function ($q) {
                $q->wherePivot('zone', 'base_image');
            },
        ]);
    }

    public function brand()
    {
        return $this->belongsTo(Brand::class)->withDefault();
    }

    public function categories()
    {
        return $this->belongsToMany(Category::class, 'product_categories');
    }

    public function taxClass()
    {
        return $this->belongsTo(TaxClass::class)->withDefault();
    }

    public function tags()
    {
        return $this->belongsToMany(Tag::class, 'product_tags');
    }

    public function reviews()
    {
        return $this->hasMany(Review::class);
    }

    public function attributes()
    {
        return $this->hasMany(ProductAttribute::class);
    }

    public function options()
    {
        return $this->belongsToMany(Option::class, 'product_options')
            ->withTrashed();
    }

    public function relatedProducts()
    {
        return $this->belongsToMany(static::class, 'related_products', 'product_id', 'related_product_id');
    }

    public function upSellProducts()
    {
        return $this->belongsToMany(static::class, 'up_sell_products', 'product_id', 'up_sell_product_id');
    }

    public function crossSellProducts()
    {
        return $this->belongsToMany(static::class, 'cross_sell_products', 'product_id', 'cross_sell_product_id');
    }

    public function filter($filter)
    {
        return $filter->apply($this);
    }

    public function getPriceAttribute($price)
    {
        return Money::inDefaultCurrency($price);
    }

    public function getSpecialPriceAttribute($specialPrice)
    {
        if (!is_null($specialPrice)) {
            return Money::inDefaultCurrency($specialPrice);
        }
    }

    public function getSellingPriceAttribute($sellingPrice)
    {
        if (FlashSale::contains($this)) {
            $sellingPrice = FlashSale::pivot($this)->price->amount();
        }

        return Money::inDefaultCurrency($sellingPrice);
    }

    public function getTotalAttribute($total)
    {
        return Money::inDefaultCurrency($total);
    }

    /**
     * Get the product's base image.
     *
     * @return \Modules\Media\Entities\File
     */
    public function getBaseImageAttribute()
    {
        return $this->files->where('pivot.zone', 'base_image')->first() ?: new File();
    }

    /**
     * Get product's additional images.
     *
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function getAdditionalImagesAttribute()
    {
        return $this->files->where('pivot.zone', 'additional_images')->sortBy('pivot.id');
    }

    /**
     * Get product's downloadable files.
     *
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function getDownloadsAttribute()
    {
        return $this->files
            ->where('pivot.zone', 'downloads')
            ->sortBy('pivot.id')
            ->flatten();
    }

    public function getFormattedPriceAttribute()
    {
        return product_price_formatted($this);
    }

    public function getRatingPercentAttribute()
    {
        if ($this->relationLoaded('reviews')) {
            return $this->ratingPercent();
        }
    }

    public function getIsInStockAttribute()
    {
        return $this->isInStock();
    }

    public function getIsOutOfStockAttribute()
    {
        return $this->isOutOfStock();
    }

    public function getIsNewAttribute()
    {
        return $this->isNew();
    }

    public function getHasPercentageSpecialPriceAttribute()
    {
        return $this->hasPercentageSpecialPrice();
    }

    public function getSpecialPricePercentAttribute()
    {
        return $this->getSpecialPricePercent();
    }

    public function getAttributeSetsAttribute()
    {
        return $this->getAttribute('attributes')->groupBy('attributeSet');
    }

    public function url()
    {
        return route('products.show', ['slug' => $this->slug]);
    }

    public function isInStock()
    {
        if (FlashSale::contains($this)) {
            return FlashSale::remainingQty($this) > 0;
        }

        if ($this->manage_stock && $this->qty === 0) {
            return false;
        }

        return $this->in_stock;
    }

    public function isOutOfStock()
    {
        return !$this->isInStock();
    }

    public function markAsInStock()
    {
        $this->withoutEvents(function () {
            $this->update(['in_stock' => true]);
        });
    }

    public function markAsOutOfStock()
    {
        $this->withoutEvents(function () {
            $this->update(['in_stock' => false]);
        });
    }

    public function hasAnyAttribute()
    {
        return $this->getAttribute('attributes')->isNotEmpty();
    }

    public function hasAnyOption()
    {
        return $this->options->isNotEmpty();
    }

    public function getSellingPrice()
    {
        if ($this->hasSpecialPrice()) {
            return $this->getSpecialPrice();
        }

        return $this->price;
    }

    public function getSpecialPrice()
    {
        $specialPrice = $this->attributes['special_price'];

        if ($this->special_price_type === 'percent') {
            $discountedPrice = ($specialPrice / 100) * $this->attributes['price'];

            $specialPrice = $this->attributes['price'] - $discountedPrice;
        }

        if ($specialPrice < 0) {
            $specialPrice = 0;
        }

        return Money::inDefaultCurrency($specialPrice);
    }

    public function hasPercentageSpecialPrice()
    {
        return $this->hasSpecialPrice() && $this->special_price_type === 'percent';
    }

    public function getSpecialPricePercent()
    {
        if ($this->hasPercentageSpecialPrice()) {
            return round($this->special_price->amount(), 2);
        }
    }

    public function hasSpecialPrice()
    {
        if (is_null($this->special_price)) {
            return false;
        }

        if ($this->hasSpecialPriceStartDate() && $this->hasSpecialPriceEndDate()) {
            return $this->specialPriceStartDateIsValid() && $this->specialPriceEndDateIsValid();
        }

        if ($this->hasSpecialPriceStartDate()) {
            return $this->specialPriceStartDateIsValid();
        }

        if ($this->hasSpecialPriceEndDate()) {
            return $this->specialPriceEndDateIsValid();
        }

        return true;
    }

    private function hasSpecialPriceStartDate()
    {
        return !is_null($this->special_price_start);
    }

    private function hasSpecialPriceEndDate()
    {
        return !is_null($this->special_price_end);
    }

    private function specialPriceStartDateIsValid()
    {
        return today() >= $this->special_price_start;
    }

    private function specialPriceEndDateIsValid()
    {
        return today() <= $this->special_price_end;
    }

    public function ratingPercent()
    {
        return ($this->reviews->avg->rating / 5) * 100;
    }

    public function isNew()
    {
        if ($this->hasNewFromDate() && $this->hasNewToDate()) {
            return $this->newFromDateIsValid() && $this->newToDateIsValid();
        }

        if ($this->hasNewFromDate()) {
            return $this->newFromDateIsValid();
        }

        if ($this->hasNewToDate()) {
            return $this->newToDateIsValid();
        }

        return false;
    }

    private function hasNewFromDate()
    {
        return !is_null($this->new_from);
    }

    private function hasNewToDate()
    {
        return !is_null($this->new_to);
    }

    private function newFromDateIsValid()
    {
        return today() >= $this->new_from;
    }

    private function newToDateIsValid()
    {
        return today() <= $this->new_to;
    }

    public function relatedProductList()
    {
        return $this->relatedProducts()
            ->withoutGlobalScope('active')
            ->pluck('related_product_id');
    }

    public function upSellProductList()
    {
        return $this->upSellProducts()
            ->withoutGlobalScope('active')
            ->pluck('up_sell_product_id');
    }

    public function crossSellProductList()
    {
        return $this->crossSellProducts()
            ->withoutGlobalScope('active')
            ->pluck('cross_sell_product_id');
    }

    public static function findBySlug($slug)
    {
        return self::with(['categories', 'tags', 'attributes.attribute.attributeSet', 'options', 'files', 'relatedProducts', 'upSellProducts'])
            ->where('slug', $slug)
            ->firstOrFail();
    }

    public static function getFeedItems()
    {
        return self::with(['categories', 'brand','tags', 'attributes.attribute.attributeSet', 'options', 'files', 'relatedProducts', 'upSellProducts'])->where("is_active", 1)->where("qty", ">", 0)->get();
    }

    public function toFeedItem(): FeedItem
    {
        return FeedItem::create([
            'id' => $this->id,
            'sku' => $this->sku,
            'brandNumber'=>$this->brand_product_number,
            'title' => $this->name,
            'summary' => $this->description ?? "",
            'updated' => Carbon::now(),
            'brand_id'=>$this->brand->id,
            'brand'=>$this->brand->name,
            'arukereso'=>$this->arukereso_in,
            'link' => url('') . "/termek/" . $this->slug,
            'authorName' => "Royal Tuning",
            'categories' => $this->categories,
            'price' => $this->selling_price->convertToCurrentCurrency()->amount(),
            'image' => $this->base_image->path ?? asset('themes/storefront/public/images/image-placeholder.png')
        ]);
    }

    public static function findById($id)
    {
        return self::with(['categories', 'tags', 'attributes.attribute.attributeSet', 'options', 'files', 'relatedProducts', 'upSellProducts'])
            ->where('id', $id)
            ->firstOrFail();
    }

    public function clean()
    {
        return array_except($this->toArray(), ['description', 'short_description', 'translations', 'categories', 'files', 'is_active', 'in_stock', 'brand_id', 'tax_class', 'tax_class_id', 'viewed', 'created_at', 'updated_at', 'deleted_at']);
    }

    /**
     * Get table data for the resource
     *
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function table($request)
    {


        $query = $this->newQuery()
            ->withoutGlobalScope('active')
            ->withName()
            ->withBaseImage()
            ->withPrice()
            ->addSelect(['id', 'sku', 'is_active', 'created_at'])
            ->when($request->has('except'), function ($query) use ($request) {
                $query->whereNotIn('id', explode(',', $request->except));
            });
        return new ProductTable($query);
    }

    /**
     * Save associated relations for the product.
     *
     * @param array $attributes
     * @return void
     */
    public function saveRelations($attributes = [])
    {
        $this->categories()->sync(array_get($attributes, 'categories', []));
        $this->tags()->sync(array_get($attributes, 'tags', []));
        $this->upSellProducts()->sync(array_get($attributes, 'up_sells', []));
        $this->crossSellProducts()->sync(array_get($attributes, 'cross_sells', []));
        $this->relatedProducts()->sync(array_get($attributes, 'related_products', []));
    }

    /**
     * Get the indexable data array for the product.
     *
     * @return array
     */
    public function toSearchableArray()
    {
        // MySQL Full-Text search handles indexing automatically.
        if (config('scout.driver') === 'mysql') {
            return [];
        }

        $translations = $this->translations()
            ->withoutGlobalScope('locale')
            ->get(['name', 'description', 'short_description']);

        return ['id' => $this->id, 'translations' => $translations];
    }

    public function searchTable()
    {
        return ['product_translations', 'products'];
    }

    public function joinField()
    {
        return ['products.id', 'product_translations.product_id'];
    }

    public function searchKey()
    {
        return 'product_id';
    }

    public function searchColumns()
    {
        return ['product_translations.name', 'products.sku'];
    }

    public function searchExactColumn()
    {
        return 'name';
    }
}