'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', 'deleted_at', ]; /** * The accessors to append to the model's array form. * * @var array */ protected $appends = [ 'base_image', 'additional_images', 'media', 'formatted_price', 'formatted_price_range', 'has_percentage_special_price', 'special_price_percent', 'rating_percent', 'does_manage_stock', 'is_in_stock', 'is_out_of_stock', 'is_new', 'variant', ]; /** * The attributes that are translatable. * * @var array */ protected array $translatedAttributes = [ 'name', 'description', 'short_description', ]; /** * The attribute that will be slugged. * * @var string */ protected string $slugAttribute = 'name'; /** * Perform any actions required after the model boots. * * @return void */ protected static function booted(): void { static::addActiveGlobalScope(); static::saved(function ($product) { $attributes = request()->all(); if (!empty($attributes)) { $product->categories()->sync(array_get($attributes, 'categories', [])); $product->tags()->sync(array_get($attributes, 'tags', [])); $product->upSellProducts()->sync(array_get($attributes, 'up_sells', [])); $product->crossSellProducts()->sync(array_get($attributes, 'cross_sells', [])); $product->relatedProducts()->sync(array_get($attributes, 'related_products', [])); } $product->withoutEvents(function () use ($product) { $product->update([ 'selling_price' => ($product->hasSpecialPrice() ? $product->getSpecialPrice() : $product->price)->amount(), ]); }); }); } /** * Get table data for the resource * * @param Request $request * * @return ProductTable */ public function table(Request $request): ProductTable { $query = $this->newQuery() ->withoutGlobalScope('active') ->withName() ->withBaseImage() ->withPrice() ->addSelect(['id', 'is_active', 'created_at', 'updated_at']) ->when($request->has('except'), function ($query) use ($request) { $query->whereNotIn('id', explode(',', $request->except)); }); return new ProductTable($query); } public function clean(): array { $cleanExceptAttributes = [ 'description', 'short_description', 'translations', 'categories', 'files', 'in_stock', 'brand_id', 'tax_class', 'tax_class_id', 'viewed', 'is_active', 'created_at', 'updated_at', 'deleted_at', ]; return array_except( $this->toArray(), $cleanExceptAttributes ); } public function url(): string { return route('products.show', ['slug' => $this->slug]); } /** * Get the indexable data array for the product. * * @return array */ public function toSearchableArray(): array { # 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(): string { return 'product_translations'; } public function searchKey(): string { return 'product_id'; } public function searchColumns(): array { return ['name']; } /** * Help HasMedia trait to extract media * for this model from the HTTP request. * * @return mixed */ public function extractMediaFromRequest(): mixed { $media = collect(request('media', [])); return [ 'base_image' => $media->first(), 'additional_images' => $media->except( $media->keys()->first() )->toArray(), 'downloads' => request('downloads', []), ]; } }