510 lines
16 KiB
PHP
510 lines
16 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Enums\CkEditorConfigName;
|
|
use App\Enums\InputType;
|
|
use App\Enums\Sections\PageSectionEnum;
|
|
use App\Lib\Helpers\FileStorageHelper;
|
|
use App\Lib\Helpers\ToolboxHelper;
|
|
use App\Traits\Models\ActivityLog;
|
|
use App\Traits\Models\MiniCrudModel;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Support\Str;
|
|
use App\Models\StaticPage;
|
|
use App\Enums\StaticPageType;
|
|
|
|
/**
|
|
* @property \App\Enums\Sections\PageSectionEnum $section Slider that contains the slide.
|
|
* @property string $h2_title Title.
|
|
* @property string $text WYSIWYG - Content.
|
|
* @property array $data Minicrud content.
|
|
* @property-read \Carbon\Carbon $created_at Created date.
|
|
* @property-read \Carbon\Carbon $updated_at Updated date.
|
|
*
|
|
* @property-read \App\Models\StaticPage $page StaticPage Belongs to relationship.
|
|
* @property-read \App\Models\ActivityLog $activityLogs Activities logs One-to-many relationship.
|
|
*/
|
|
class Section extends Model
|
|
{
|
|
use ActivityLog;
|
|
/** @use HasFactory<\Database\Factories\SectionFactory> */
|
|
use HasFactory;
|
|
use MiniCrudModel;
|
|
|
|
/**
|
|
* The attributes that are mass assignable.
|
|
*
|
|
* @var list<string>
|
|
*/
|
|
protected $fillable = [
|
|
'section',
|
|
'h2_title',
|
|
'text',
|
|
'data',
|
|
'page_id',
|
|
'id',
|
|
'created_at',
|
|
'updated_at'
|
|
];
|
|
|
|
/**
|
|
* The attributes that should be cast.
|
|
*
|
|
* @var array<string, string>
|
|
*/
|
|
protected $casts = [
|
|
'section' => PageSectionEnum::class,
|
|
'data' => 'array',
|
|
];
|
|
|
|
/**
|
|
* Perform any actions required after the model boots.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected static function booted(): void
|
|
{
|
|
static::creating(function () {
|
|
throw new \RuntimeException("Creating a new section is not authorized");
|
|
});
|
|
static::updating(function (self $model) {
|
|
// * ASSERTIONS
|
|
ToolboxHelper::assertNoInternalBoUrls('text', $model->text);
|
|
|
|
// * TRANSFORMATIONS
|
|
$model->text = FileStorageHelper::storeModelFilesFromHtml($model, $model->text);
|
|
});
|
|
static::updated(function (self $model) {
|
|
$model->minicrudUpdated();
|
|
FileStorageHelper::removeOldModelFilesFromHtml(
|
|
$model->text,
|
|
ToolboxHelper::getOriginal($model, 'text')
|
|
);
|
|
});
|
|
static::deleting(function () {
|
|
throw new \RuntimeException('Deleting a section is not authorized');
|
|
});
|
|
}
|
|
|
|
// * METHODS.
|
|
|
|
/**
|
|
* Empty bloc for picture.
|
|
*
|
|
* @param array<string, mixed> $data
|
|
* @param string $img
|
|
* @param string $imgAlt
|
|
* @param string $imgTitle
|
|
* @param integer $width
|
|
* @param integer $height
|
|
* @param boolean $required
|
|
* @param string $name
|
|
* @return array<string, mixed>
|
|
*/
|
|
public static function emptyPictureBloc(
|
|
array $data,
|
|
string $img = '',
|
|
string $imgAlt = '',
|
|
string $imgTitle = '',
|
|
int $width = 100,
|
|
int $height = 100,
|
|
bool $required = false,
|
|
string $name = 'image',
|
|
): array {
|
|
$data[sprintf('bloc_%s', Str::of($name)->snake()->ascii()->remove(['\'', '-']))] = [
|
|
'label' => $name,
|
|
'dimensions' => ['width' => $width, 'height' => $height],
|
|
'inputType' => InputType::image->value,
|
|
'alt' => $imgAlt,
|
|
'title' => $imgTitle,
|
|
'value' => $img,
|
|
'validate' => $required ? 'required' : 'nullable',
|
|
'required' => $required
|
|
];
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Empty bloc for link.
|
|
*
|
|
* @param array<string, mixed> $data
|
|
* @param string $url
|
|
* @param boolean $required
|
|
* @param string $name
|
|
* @return array<string, mixed>
|
|
*/
|
|
public static function emptyLinkBloc(
|
|
array $data,
|
|
string $url = '',
|
|
bool $required = false,
|
|
string $name = "URL à ouvrir",
|
|
): array {
|
|
$data[sprintf('bloc_%s', Str::of($name)->snake()->ascii()->remove(['\'', '-']))] = [
|
|
'label' => $name,
|
|
'inputType' => InputType::url->value,
|
|
'value' => $url,
|
|
'validate' => 'url|' . ($required ? 'required' : 'nullable'),
|
|
'required' => $required
|
|
];
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Empty bloc for boolean new tab link.
|
|
*
|
|
* @param array<string, mixed> $data
|
|
* @param string $name
|
|
* @return array<string, mixed>
|
|
*/
|
|
public static function emptyLinkNewTabBloc(
|
|
array $data,
|
|
string $name = 'le lien',
|
|
): array {
|
|
$data["bloc_link_new_tab"] = [
|
|
'label' => "Ouvrir dans un nouvel onglet {$name}",
|
|
'inputType' => InputType::boolean->value,
|
|
'value' => true,
|
|
];
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Empty bloc for key numbers.
|
|
*
|
|
* @param array<string, mixed> $data
|
|
* @param string $name
|
|
* @param integer $value
|
|
* @param boolean $required
|
|
* @return array<string, mixed>
|
|
*/
|
|
public static function emptyKeyNumberBloc(
|
|
array $data,
|
|
string $name,
|
|
int $value = 0,
|
|
bool $required = false,
|
|
): array {
|
|
$data['bloc_key_' . Str::of($name)->snake()->ascii()->remove(['\'', '-'])] = [
|
|
'label' => "Chiffre clé {$name}",
|
|
'inputType' => InputType::number->value,
|
|
'value' => $value,
|
|
'validate' => $required ? 'required' : 'nullable',
|
|
'required' => $required
|
|
];
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Empty bloc for the button boolean.
|
|
*
|
|
* @param array<string, mixed> $data
|
|
* @param string $name
|
|
* @param boolean $value
|
|
* @return array<string, mixed>
|
|
*/
|
|
public static function emptyButtonBooleanBloc(
|
|
array $data,
|
|
string $name,
|
|
bool $value = false
|
|
): array {
|
|
$data['bloc_button_' . Str::of($name)->snake()->ascii()->remove(['\'', '-'])] = [
|
|
'label' => "Afficher le bouton '{$name}' ?",
|
|
'inputType' => InputType::boolean->value,
|
|
'value' => $value
|
|
];
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Empty bloc for a boolean.
|
|
*
|
|
* @param array<string, mixed> $data
|
|
* @param string $name
|
|
* @param boolean $value
|
|
* @return array<string, mixed>
|
|
*/
|
|
public static function emptyBooleanBloc(
|
|
array $data,
|
|
string $name,
|
|
bool $value = false
|
|
): array {
|
|
$data['bloc_boolean_' . Str::of($name)->snake()->ascii()->remove(['\'', '-'])] = [
|
|
'label' => "{$name} ?",
|
|
'inputType' => InputType::boolean->value,
|
|
'value' => $value
|
|
];
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Empty bloc for the h3 title.
|
|
*
|
|
* @param array<string, mixed> $data
|
|
* @param string $name
|
|
* @param string $value
|
|
* @param boolean $required
|
|
* @return array<string, mixed>
|
|
*/
|
|
public static function emptyStringBloc(
|
|
array $data,
|
|
string $name,
|
|
string $value = '',
|
|
bool $required = false
|
|
): array {
|
|
$data['bloc_string_' . Str::of($name)->snake()->ascii()->remove(['\'', '-'])] = [
|
|
'label' => $name,
|
|
'inputType' => InputType::string->value,
|
|
'validate' => 'max:255|' . ($required ? 'required' : 'nullable'),
|
|
'value' => $value,
|
|
'required' => $required
|
|
];
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Empty bloc for the WYSIWYG content.
|
|
*
|
|
* @param array<string, mixed> $data
|
|
* @param string $name
|
|
* @param string $value
|
|
* @param \App\Enums\CkEditorConfigName $config
|
|
* @param boolean $required
|
|
* @return array<string, mixed>
|
|
*/
|
|
public static function emptyWysiwygBloc(
|
|
array $data,
|
|
string $name,
|
|
string $value = '',
|
|
CkEditorConfigName $config = CkEditorConfigName::basic,
|
|
bool $required = false
|
|
): array {
|
|
$data['bloc_' . Str::of($name)->snake()->ascii()->remove(['\'', '-']) . '_content'] = [
|
|
'label' => "Contenu de la section {$name}",
|
|
'inputType' => InputType::text->value,
|
|
'validate' => $required ? 'required' : 'nullable',
|
|
'value' => $value,
|
|
'required' => $required,
|
|
'config' => $config->name
|
|
];
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Add part piece.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function addPartPiece(): void
|
|
{
|
|
$pieceTemplateLinesCount = self::countPartPieces();
|
|
|
|
$pieceToAdd = self::getAddablePartPieceBlock(
|
|
$this->section,
|
|
$this->countPartPieces() + 1
|
|
);
|
|
|
|
/** @var array<string, mixed> $rawData */
|
|
$rawData = $this->data;
|
|
$data = collect($rawData);
|
|
$linesToSkip = 1;
|
|
|
|
$groupedPieces = $data->skip($linesToSkip)->splitIn($data->count() / ($pieceTemplateLinesCount ?: 1))
|
|
->concat([$pieceToAdd])
|
|
->sort(function ($aCollect, $bCollect) {
|
|
if ($this->section->equals(PageSectionEnum::produce_rhd)) {
|
|
/** @var array<string, mixed> $aFirst */
|
|
$aFirst = collect($aCollect)->first();
|
|
/** @var array<string, mixed> $bFirst */
|
|
$bFirst = collect($bCollect)->first();
|
|
return strcmp(
|
|
strrev((string) collect($aFirst)->first()),
|
|
strrev((string) collect($bFirst)->first())
|
|
);
|
|
}
|
|
return 0;
|
|
});
|
|
$groupedPieces->prepend([$data->keys()->first() => $data->first()]);
|
|
$this->data = $groupedPieces->collapse()->all();
|
|
}
|
|
|
|
/**
|
|
* Remove part piece.
|
|
*
|
|
* @param integer $index
|
|
* @return integer Elements count removed.
|
|
*/
|
|
public function removePartPiece(int $index): int
|
|
{
|
|
$pieceTemplate = self::getPartPieces();
|
|
$pieceTemplateLinesCount = self::countPartPieces();
|
|
if (!$pieceTemplateLinesCount) {
|
|
return 0;
|
|
}
|
|
$linesToSkip = 1;
|
|
$data = collect($this->data);
|
|
$groupedPieces = $data->skip($linesToSkip)->splitIn($data->count() / count($pieceTemplate));
|
|
$removedGroup = $groupedPieces->splice($index - 1, 1);
|
|
$this->data = $groupedPieces->collapse()
|
|
->prepend($data->first(), $data->keys()->first())
|
|
->all();
|
|
return $removedGroup->count();
|
|
}
|
|
|
|
/**
|
|
* Count part pieces.
|
|
*
|
|
* @return integer
|
|
*/
|
|
public function countPartPieces(): int
|
|
{
|
|
$pieceTemplate = self::getPartPieces();
|
|
$data = collect($this->data);
|
|
$templateKeys = collect($pieceTemplate)->keys();
|
|
$dataCount = $data->count();
|
|
$repetitionCount = 0;
|
|
for ($i = 1; $i <= $dataCount; $i++) {
|
|
$indexKeys = $templateKeys->map(fn($key) => str_replace('__index', strval($i), $key));
|
|
if ($indexKeys->every(fn($key) => $data->has($key))) {
|
|
$repetitionCount++;
|
|
}
|
|
}
|
|
return $repetitionCount;
|
|
}
|
|
|
|
/**
|
|
* Get part pieces.
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
private function getPartPieces(): array
|
|
{
|
|
$pieceTemplate = self::getAddablePartPieceBlockTemplate($this->section);
|
|
return is_null($pieceTemplate) ? [] : $pieceTemplate;
|
|
}
|
|
|
|
/**
|
|
* Addable part piece block.
|
|
*
|
|
* @param \App\Enums\Sections\PageSectionEnum $sectionEnum
|
|
* @param integer $index
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
public static function getAddablePartPieceBlock(PageSectionEnum $sectionEnum, int $index = 1): array|null
|
|
{
|
|
$data = null;
|
|
$pieceTemplate = self::getAddablePartPieceBlockTemplate($sectionEnum);
|
|
if (!is_null($pieceTemplate)) {
|
|
$encoded = json_encode($pieceTemplate);
|
|
if ($encoded !== false) {
|
|
/** @var array<string, mixed> $data */
|
|
$data = json_decode(Str::of($encoded)->replace('__index', strval($index)), true);
|
|
}
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Addable part piece block.
|
|
*
|
|
* @param \App\Enums\Sections\PageSectionEnum $sectionEnum
|
|
* @return array<int, string>|null
|
|
*/
|
|
public static function getAddablePartPieceBlockIndexes(PageSectionEnum $sectionEnum): array|null
|
|
{
|
|
$data = self::getAddablePartPieceBlockTemplate($sectionEnum);
|
|
if (!is_null($data)) {
|
|
return array_keys($data);
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Addable part piece block template.
|
|
*
|
|
* @param \App\Enums\Sections\PageSectionEnum $sectionEnum
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
private static function getAddablePartPieceBlockTemplate(PageSectionEnum $sectionEnum): array|null
|
|
{
|
|
$data = null;
|
|
switch ($sectionEnum) {
|
|
case PageSectionEnum::produce_export:
|
|
$data = array_merge(
|
|
self::emptyWysiwygBloc(
|
|
[],
|
|
"accompagnant l'image __index de la partie",
|
|
''
|
|
),
|
|
self::emptyPictureBloc([], '', '', '', 100, 100, true, "Image __index de la partie"),
|
|
self::emptyLinkBloc([], '', false, "Lien de l'image__index de la partie")
|
|
);
|
|
break;
|
|
case PageSectionEnum::produce_rhd:
|
|
$data = array_merge(
|
|
self::emptyWysiwygBloc(
|
|
[],
|
|
"accompagnant l'image __index de la partie 1",
|
|
''
|
|
),
|
|
self::emptyPictureBloc([], '', '', '', 100, 100, true, "Image __index de la partie 1"),
|
|
self::emptyLinkBloc([], '', false, "Lien de l'image__index de la partie 1")
|
|
);
|
|
break;
|
|
default:
|
|
} //end switch
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Returns the list of sections for a page type.
|
|
*
|
|
* @param \App\Enums\StaticPageType $type
|
|
* @return \Illuminate\Database\Eloquent\Collection<int, self>
|
|
*/
|
|
public static function getSections(StaticPageType $type): \Illuminate\Database\Eloquent\Collection
|
|
{
|
|
$staticPageModel = StaticPage::query()->where('type', $type)->first();
|
|
$sectionModels = self::where('page_id', $staticPageModel->id)->get();
|
|
|
|
return $sectionModels;
|
|
}
|
|
|
|
/**
|
|
* Get the active section for a type page.
|
|
*
|
|
* @param \App\Enums\StaticPageType $type
|
|
* @return boolean
|
|
*/
|
|
public static function isActive(StaticPageType $type)
|
|
{
|
|
$staticPage = StaticPage::getStaticPage($type);
|
|
return $staticPage->sections->contains(
|
|
function ($section) {
|
|
return request()->is("bo/sections/$section->id/edit");
|
|
}
|
|
);
|
|
}
|
|
|
|
// * RELATIONS.
|
|
|
|
/**
|
|
* Get the page that owns the section.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\App\Models\StaticPage,$this>
|
|
*/
|
|
public function page(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
{
|
|
return $this->belongsTo(StaticPage::class);
|
|
}
|
|
|
|
/**
|
|
* Get the sections that own the carousel.
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany<\App\Models\Carousel,$this>
|
|
*/
|
|
public function carousels(): \Illuminate\Database\Eloquent\Relations\HasMany
|
|
{
|
|
return $this->hasMany(Carousel::class);
|
|
}
|
|
}
|