ldc-boutique-cnc/app/Models/Discount.php
2022-12-02 10:37:01 +01:00

262 lines
7.8 KiB
PHP

<?php
namespace App\Models;
use App\Enums\Discount\DiscountType;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Kwaadpepper\Enum\Traits\CastsEnums;
/**
* @property \App\Enums\Discount\DiscountType $type Discount type.
* @property integer $discount_value Discount value in percent or cents.
* @property boolean $status The discount is effective or not.
* @property \Carbon\Carbon $date_start The discount is effective date_start.
* @property \Carbon\Carbon $date_end The discount is effective date_end.
* @property integer $discount_value Discount value in percent or cents.
* @property-read integer|float $discount_calculation The discount value
* ready to be used for calculations.
*/
class Discount extends Model
{
use CastsEnums;
use HasFactory;
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'status' => 'bool',
'date_start' => 'datetime',
'date_end' => 'datetime',
];
/**
* The attributes that should be cast to Enums.
*
* @var array
*/
protected $enumCasts = [
'type' => DiscountType::class,
];
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'type', 'discount_value',
'status', 'date_start', 'date_end',
];
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted()
{
// * EVENT CLOSURES.
static::creating(function (self $model) {
// * ASSERTIONS
self::assertIsAboveZero(
'discount_value',
trans('Valeur de réduction'),
$model->getAttributeFromArray('discount_value')
);
// * TRANSFORMATIONS
$model->makeStatusSense();
});
static::created(function (self $model) {
$model->flashMessages();
});
static::updated(function (self $model) {
$model->flashMessages();
});
static::updating(function (self $model) {
// * ASSERTIONS
self::assertIsAboveZero(
'discount_value',
trans('Valeur de réduction'),
$model->getAttributeFromArray('discount_value')
);
// * TRANSFORMATIONS
$model->makeStatusSense();
});
}
// * RELATIONSHIPS
/**
* HasMany products relation
*
* @return \Illuminate\Database\Eloquent\Relations\hasMany
*/
public function products()
{
return $this->hasMany(Product::class);
}
// * GETTERS
/**
* Getter displayable_discount_value
*
* @return string
*/
public function getDisplayableDiscountValueAttribute(): string
{
return $this->type->equals(DiscountType::amount()) ?
number_format($this->discount_value / 100, 2, ',', '') . ' €' :
number_format($this->discount_value / 100, 2, ',', '') . ' %';
}
/**
* Getter discount_calculation
*
* @return float|integer
*/
public function getDiscountCalculationAttribute()
{
return $this->type->equals(DiscountType::amount()) ?
// * Return value in cents
$this->discount_value :
// * return a ratio (percent)
$this->discount_value / 10000;
}
// * METHODS
/**
* Associate Discount with product
*
* @param array<integer> $ids Array of \App\Models\Product ids.
* @return void
*/
public function associateProducts(array $ids)
{
// * Tournicota, HasMany ne gere pas nullable, meme issue que sur gengame web
$this->products->each(function (Product $product) {
$product->discount()->dissociate();
$product->saveQuietly();
});
if (!empty($ids)) {
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Product> */
$products = Product::query()->whereIn('id', $ids)->get();
$products->each(function (Product $product) {
$product->discount()->associate($this);
$product->saveQuietly();
});
}
}
/**
* Flash boolean attributes modification messages
*
* @param boolean $old Ignore old attribute comparison.
* @return void
*/
public function flashMessages(bool $old = true)
{
$booleanChecks = [
'status' => [
'info' => trans(':model est maintenant pris en compte', [
'model' => Str::singular(trans('models.classes.discounts'))
]),
'warning' => trans(':model n\'est maintenant plus pris en compte', [
'model' => Str::singular(trans('models.classes.discounts'))
])
],
];
$sessionMessages = [];
// * Merge messages
foreach ($booleanChecks as $attribute => $booleanCheck) {
if ($old and $this->getOriginal($attribute) == $this->{$attribute}) {
continue;
}
/** @var string $flashType */
$flashType = \array_keys($booleanCheck)[$this->{$attribute} ? 0 : 1];
if (!\array_key_exists($flashType, $sessionMessages)) {
$sessionMessages[$flashType] = [];
}
$sessionMessages[$flashType][] = $booleanCheck[$flashType];
}
foreach ($sessionMessages as $flashType => $messages) {
session()->flash($flashType, collect($messages)->implode("\n"));
}
}
/**
* Assert Is Above Zero
*
* @param string $attribute
* @param string $name
* @param float $value
* @return void
* @throws \Illuminate\Validation\ValidationException If the value is less or equals to 0.
*/
private static function assertIsAboveZero(string $attribute, string $name, float $value): void
{
Validator::make([
$attribute => $value
], [
$attribute => 'numeric|min:0'
], [], [
$attribute => $name
])->validate();
}
/**
* Adjusts model status using current status and start and end dates
*
* @return void
*/
public function makeStatusSense(): void
{
/** @var \Carbon\Carbon|null */
$date_start = $this->date_start;
/** @var \Carbon\Carbon|null */
$date_end = $this->date_end;
// * Ignore unpublish_at in the past
if ($date_end and $date_end->lessThan(now())) {
return;
}
// * Ignore publish_at in the futur
if ($date_start and $date_start->greaterThan(now())) {
return;
}
if ($date_start or $date_end) {
$this->status = $this->planificated_status;
}
}
/**
* Getter the current status following planification dates
*
* @return boolean
*/
public function getPlanificatedStatusAttribute(): bool
{
/** @var \Carbon\Carbon|null */
$date_start = $this->date_start;
/** @var \Carbon\Carbon|null */
$date_end = $this->date_end;
if ($date_start and $date_end) {
return $date_end->greaterThanOrEqualTo(now()) and $date_start->lessThan(now());
}
if ($date_start) {
return $date_start->lessThan(now());
}
if ($date_end) {
return $date_end->greaterThanOrEqualTo(now());
}
return false;
}
}