316 lines
11 KiB
PHP
316 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Support\Str;
|
|
use Spatie\Sitemap\Tags\Url;
|
|
use App\Enums\NutriscoreEnum;
|
|
use App\Lib\Helpers\ToolboxHelper;
|
|
use App\Enums\Advices\AdviceTypeEnum;
|
|
use Uwil\Seo\Laravel\Objects\SeoItem;
|
|
use Uwil\Seo\Laravel\Traits\SeoTrait;
|
|
use App\Lib\Helpers\FileStorageHelper;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Spatie\Sitemap\Contracts\Sitemapable;
|
|
use App\Enums\Products\ProductSpeciesEnum;
|
|
use App\Traits\Models\Orderable;
|
|
use Uwil\SitemapGenerator\Lib\SitemapHelper;
|
|
use Uwil\Seo\Laravel\Interfaces\SeoInterface;
|
|
use Uwil\Laravel\Redirections\Traits\IsRedirectable;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Uwil\Laravel\Redirections\Interfaces\RedirectableInterface;
|
|
|
|
/**
|
|
* @property integer $id Id.
|
|
* @property string $label Title.
|
|
* @property string $slug Slug of the title.
|
|
* @property integer $subrange_id Foreign key Subrange.
|
|
* @property string $seo_title SEO Title.
|
|
* @property string $seo_description SEO description.
|
|
* @property \App\Enums\Products\ProductSpeciesEnum $species Species of products.
|
|
* @property \App\Enums\NutriscoreEnum $nutriscore Nutriscore.
|
|
* @property string $sticker_sm Path of a small sticker.
|
|
* @property string $sticker_sm_alt small sticker's attribute alt.
|
|
* @property string $sticker_sm_title Small sticker's attribute title.
|
|
* @property array<string> $slider_pictures Array of paths of pictures.
|
|
* @property array<array<string,string>> $packaging Packaging.
|
|
* @property array<array<string,string>> $caliber Caliber.
|
|
* @property string $ingredients Ingredients.
|
|
* @property string $advantage Advantage.
|
|
* @property string $preparation Preparation.
|
|
* @property boolean $is_frenchpoultry Is frenchpoultry or not.
|
|
* @property boolean $is_vip Is vip or not.
|
|
* @property boolean $is_egalim Is egalim or not.
|
|
* @property boolean $published Status.
|
|
* @property integer $order Order.
|
|
* @property integer $priority_order Priority Order.
|
|
* @property-read \Illuminate\Support\Carbon $created_at Created date.
|
|
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
|
|
*
|
|
* @property-read \App\Models\Subrange $subrange Subrange Has-One relation.
|
|
* @property-read \Illuminate\Database\Eloquent\Collection<\App\Models\Advice> $advices
|
|
* Advices Belongs-To-Many relation.
|
|
* @property-read \Illuminate\Database\Eloquent\Collection<\App\Models\News> $news
|
|
* News Belongs-To-Many relation.
|
|
*/
|
|
class Product extends Model implements SeoInterface, Sitemapable, RedirectableInterface
|
|
{
|
|
use HasFactory;
|
|
use SeoTrait;
|
|
use IsRedirectable;
|
|
use Orderable;
|
|
|
|
/**
|
|
* The attributes that are mass assignable.
|
|
*
|
|
* @var array<int, string>
|
|
*/
|
|
protected $fillable = [
|
|
'label',
|
|
'slug',
|
|
'seo_title',
|
|
'seo_description',
|
|
'subrange_id',
|
|
'species',
|
|
'nutriscore',
|
|
'ingredients',
|
|
'advantage',
|
|
'preparation',
|
|
'sticker_sm',
|
|
'sticker_sm_alt',
|
|
'sticker_sm_title',
|
|
'slider_pictures',
|
|
'packaging',
|
|
'caliber',
|
|
'is_frenchpoultry',
|
|
'is_vip',
|
|
'is_egalim',
|
|
'order',
|
|
'published',
|
|
'priority_order',
|
|
];
|
|
|
|
/**
|
|
* The attributes that should be cast.
|
|
*
|
|
* @var array<string,string>
|
|
*/
|
|
protected $casts = [
|
|
'slider_pictures' => 'array',
|
|
'packaging' => 'array',
|
|
'caliber' => 'array',
|
|
'is_frenchpoultry' => 'bool',
|
|
'is_vip' => 'bool',
|
|
'is_egalim' => 'bool',
|
|
'published' => 'bool',
|
|
'species' => ProductSpeciesEnum::class,
|
|
'nutriscore' => NutriscoreEnum::class
|
|
];
|
|
|
|
/**
|
|
* Perform any actions required after the model boots.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected static function booted(): void
|
|
{
|
|
// * EVENT CLOSURES.
|
|
static::creating(function (self $model) {
|
|
self::manageFields($model);
|
|
self::bootOrderable($model);
|
|
});
|
|
static::updating(function (self $model) {
|
|
self::manageFields($model);
|
|
});
|
|
static::updated(function (self $model) {
|
|
FileStorageHelper::removeOldFile($model, 'sticker_sm');
|
|
FileStorageHelper::removeOldFiles(
|
|
!is_array($model->getOriginal('slider_pictures')) ? [] : $model->getOriginal('slider_pictures'),
|
|
$model->slider_pictures
|
|
);
|
|
});
|
|
static::deleted(function (self $model) {
|
|
FileStorageHelper::removeFile($model, 'sticker_sm');
|
|
FileStorageHelper::removeFiles(
|
|
$model,
|
|
!is_array($model->getOriginal('slider_pictures')) ? [] : $model->getOriginal('slider_pictures'),
|
|
);
|
|
self::setOrder($model);
|
|
});
|
|
}
|
|
|
|
// * METHODS
|
|
|
|
/**
|
|
* Common methods function.
|
|
*
|
|
* @param self $model
|
|
* @return void
|
|
*/
|
|
private static function manageFields(self $model): void
|
|
{
|
|
// * ASSERTIONS
|
|
self::assertLabelIsUnique($model->label, $model->id);
|
|
ToolboxHelper::assertNoInternalBoUrls('ingredients', $model->ingredients);
|
|
ToolboxHelper::assertNoInternalBoUrls('advantage', $model->advantage);
|
|
ToolboxHelper::assertNoInternalBoUrls('preparation', $model->preparation);
|
|
foreach ($model->packaging as $pack) {
|
|
ToolboxHelper::assertNoInternalBoUrls('packaging', $pack['value']);
|
|
}
|
|
foreach ($model->caliber as $calib) {
|
|
ToolboxHelper::assertNoInternalBoUrls('caliber', $calib['value']);
|
|
}
|
|
|
|
// * TRANSFORMATIONS
|
|
$model->slug = Str::slug($model->label);
|
|
$model->sticker_sm = FileStorageHelper::storeFile($model, $model->sticker_sm, true);
|
|
$model->ingredients = clean($model->ingredients);
|
|
$model->advantage = clean($model->advantage);
|
|
$model->preparation = clean($model->preparation);
|
|
$model->slider_pictures = clean($model->slider_pictures);
|
|
$model->packaging = clean($model->packaging);
|
|
$model->caliber = clean($model->caliber);
|
|
}
|
|
|
|
/**
|
|
* Asserts using validation that the label is unique.
|
|
*
|
|
* @param string $label
|
|
* @param integer|null $id
|
|
* @return void
|
|
*/
|
|
private static function assertLabelIsUnique(string $label, ?int $id = null): void
|
|
{
|
|
$table = (new self())->getTable();
|
|
ToolboxHelper::assertFieldIsUnique($table, 'label', $label, $id);
|
|
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($label), $id);
|
|
}
|
|
|
|
/**
|
|
* Get associated tips.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
|
*/
|
|
public function tipsQuery(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
|
{
|
|
return $this->advices()->where('type', AdviceTypeEnum::tips);
|
|
}
|
|
|
|
/**
|
|
* Get associated recipes.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
|
*/
|
|
public function recipesQuery(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
|
{
|
|
return $this->advices()->where('type', AdviceTypeEnum::recipes);
|
|
}
|
|
|
|
/**
|
|
* Get associated tips.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Collection<int,\App\Models\Advice>
|
|
*/
|
|
public function tips(): \Illuminate\Database\Eloquent\Collection
|
|
{
|
|
// @phpstan-ignore-next-line
|
|
return $this->tipsQuery()->get();
|
|
}
|
|
|
|
/**
|
|
* Get associated recipes.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Collection<int,\App\Models\Advice>
|
|
*/
|
|
public function recipes(): \Illuminate\Database\Eloquent\Collection
|
|
{
|
|
// @phpstan-ignore-next-line
|
|
return $this->recipesQuery()->get();
|
|
}
|
|
|
|
// * REDIRECTIONS.
|
|
|
|
/**
|
|
* Get the front url of this model.
|
|
* Should return the model.show route, with all binded elements if needed.
|
|
*
|
|
* @param string $modelKey
|
|
* @return string
|
|
*/
|
|
public function getFrontUrl(string $modelKey): string
|
|
{
|
|
return route('fo.product.show', $modelKey);
|
|
}
|
|
|
|
// * RELATIONSHIPS
|
|
|
|
/**
|
|
* Subrange Has-One relation.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
*/
|
|
public function subrange(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
{
|
|
return $this->belongsTo(Subrange::class);
|
|
}
|
|
|
|
/**
|
|
* Advices Belongs-To-Many relation.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
|
*/
|
|
public function advices(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
|
{
|
|
return $this->belongsToMany(Advice::class, 'advice_product');
|
|
}
|
|
|
|
/**
|
|
* News Belongs-To-Many relation.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
|
*/
|
|
public function news(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
|
{
|
|
return $this->belongsToMany(News::class);
|
|
}
|
|
|
|
/**
|
|
* Tags Morph-To-Many relationship.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
|
*/
|
|
public function tags(): \Illuminate\Database\Eloquent\Relations\MorphToMany
|
|
{
|
|
return $this->morphToMany(Tag::class, 'taggable');
|
|
}
|
|
|
|
/**
|
|
* toSitemapTag method
|
|
*
|
|
* @return Url|string|array
|
|
*/
|
|
public function toSitemapTag(): Url | string | array
|
|
{
|
|
$model = $this;
|
|
$route = 'fo.product.show';
|
|
$params = ['publishedProduct' => $model];
|
|
return SitemapHelper::generateUrl($route, $params, $model);
|
|
}
|
|
|
|
/**
|
|
* Transform to seoModel
|
|
*
|
|
* @return \Uwil\Seo\Laravel\Objects\SeoItem
|
|
*/
|
|
public function toSeoItem(): SeoItem
|
|
{
|
|
return new SeoItem(
|
|
\trans($this->getTable()),
|
|
$this->seo_title,
|
|
$this->seo_description,
|
|
$this->published ?? false,
|
|
\route(\config('seo.bo_route_prefix') . $this->getTable() . '.edit', $this),
|
|
route('fo.product.show', $this)
|
|
);
|
|
}
|
|
}
|