ldc-groupe/app/Models/Document.php

274 lines
7.8 KiB
PHP

<?php
namespace App\Models;
use Carbon\Carbon;
use App\Enums\YearsEnum;
use Illuminate\Support\Arr;
use App\Traits\Models\HasCache;
use App\Helpers\TranslationHelper;
use Illuminate\Support\Facades\App;
use App\Contracts\TranslatableModel;
use App\Traits\Models\HasUwilImages;
use App\Traits\Models\HasTranslations;
use Illuminate\Database\Eloquent\Model;
use App\Enums\Document\DocumentStatusEnum;
use Uwil\LaravelActivityLogs\Traits\ActivityLog;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Document extends Model implements TranslatableModel
{
use ActivityLog;
use HasFactory;
use HasTranslations;
use HasCache;
use HasUwilImages {
HasUwilImages::inertiaFill as traitInertiaFill;
}
/**
* Champs contenant des images dynamiques à gérer.
*
* @var array
*/
protected array $imageFields = [
'path'
];
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'path',
'exercice',
'status',
'published_at',
'unpublished_at'
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'status' => DocumentStatusEnum::class,
'exercice' => YearsEnum::class,
'published_at' => 'datetime',
'unpublished_at' => 'datetime',
];
/**
* The translatable columns.
*
* @var list<string>
*/
public $translatable = [
'name',
'path',
];
/**
* Relations should be included in the cache.
*
* @var array<string, string>
*/
protected static $cacheRelations = [
'categories',
'tags',
];
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted(): void
{
static::creating(function (self $model) {
self::manageFields($model);
});
static::updating(function (self $model) {
self::manageFields($model);
});
}
/**
* Adjusts model status using current status and publish dates.
* Case 1: publish model when all null -> set published_at date.
* Case 2: unpublish model when all null -> nothing to do.
* Case 3: publish model when published_at is already set -> only change status.
* Case 4: unpublish model when published_at is already set -> can't update model.
* Case 5: publish model when unpublished_at is already set -> can't update model.
* Case 6: unpublish model when unpublished_at is already set -> only change status.
*
* @param self $model
* @return void
*/
private static function makeStatusSense(self $model): void
{
/** @var \Carbon\Carbon|null */
$published_at = $model->published_at;
/** @var \Carbon\Carbon|null */
$unpublished_at = $model->unpublished_at;
$wantedStatus = $model->status;
// Check if dates are already set.
if ($published_at or $unpublished_at) {
$model->status = $model->planificated_status;
}
// If there is a diff, no update.
if ($wantedStatus !== $model->status) {
session()->flash('error', \trans(
'La publication est modifiée en concordance avec les dates enregistrées.
Pour modifier l\'état de publication, veuillez modifier ou enlever les dates correspondantes.'
));
}
// Set default published_at date when publishing model.
if (!$published_at && $model->status->equals(DocumentStatusEnum::published)) {
$model->published_at = Carbon::now();
}
}
/**
* Getter the current status following planification dates planificated_status.
*
* @return \App\Enums\Document\DocumentStatusEnum
*/
protected function getPlanificatedStatusAttribute(): \App\Enums\Document\DocumentStatusEnum
{
/** @var \Carbon\Carbon|null */
$published_at = $this->published_at;
/** @var \Carbon\Carbon|null */
$unpublished_at = $this->unpublished_at;
if ($published_at and $unpublished_at) {
return ($unpublished_at->greaterThanOrEqualTo(now()) and $published_at->lessThan(now()))
? DocumentStatusEnum::published
: DocumentStatusEnum::unpublished;
}
if ($published_at) {
return $published_at->lessThan(now())
? DocumentStatusEnum::published
: DocumentStatusEnum::unpublished;
}
if ($unpublished_at) {
return $unpublished_at->greaterThanOrEqualTo(now())
? DocumentStatusEnum::published
: DocumentStatusEnum::unpublished;
}
return DocumentStatusEnum::unpublished;
}
// * METHODS
/**
* Manage model fields.
*
* @param self $model
* @return void
*/
private static function manageFields(self $model): void
{
// Check unique fields.
TranslationHelper::assertTranslationFieldIsUnique($model, $model->name, 'name');
// Check translations.
self::validateFallbackTranslations($model);
// Set status.
self::makeStatusSense($model);
}
/**
* Assert fallback locale translation is present.
*
* @param self $model
* @return void
* @throws \Illuminate\Validation\ValidationException Translations missing.
*/
public static function validateFallbackTranslations(self $model): void
{
if (! $model->status->equals(DocumentStatusEnum::published)) {
return;
}
TranslationHelper::validateFallbackTranslations($model);
}
/**
* Get the url to access a specific resource.
*
* @param integer $id Id field of the target model.
* @return string
*/
public static function getAccessModel(int $id): string
{
$targetModel = self::getModelsCached()->firstWhere('id', $id);
if (!$targetModel) {
return null;
}
return sprintf('/%s', $targetModel->path);
}
// * RELATIONSHIPS
/**
* Relation to Tag
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->morphedByMany(
DocumentTag::class,
'documentable',
'documentables',
'document_id',
'documentable_id'
)->withTimestamps();
}
/**
* Relation to Category
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function categories(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->morphedByMany(
DocumentCategory::class,
'documentable',
'documentables',
'document_id',
'documentable_id'
)->withTimestamps();
}
public function inertiaFill(array $attributes)
{
$attributes = Arr::except($attributes, ['tags', 'categories']);
$this->traitInertiaFill($attributes);
}
/**
* Get all published news by category.
*
* @param string $category
*
* @return \Illuminate\Database\Eloquent\Collection
*/
public static function getPublishedDocumentsByCategoryInCurrentLocale(string $category)
{
$locale = App::getLocale();
return Document::getModelsCached()
->where('status', DocumentStatusEnum::published)
->filter(function ($documents) use ($category, $locale) {
return !empty($documents->getTranslation('path', $locale, false)) && $documents->categories->contains(
fn ($cat) => $cat->slug === $category
);
});
}
}