*/ protected $fillable = [ 'id_key', 'title', 'slug', 'content', 'page_id', 'config_id', 'status', 'isLocked', ]; /** * The attributes that should be cast. * * @var array */ protected $casts = [ 'content' => 'array', 'status' => PageStatusEnum::class, 'isLocked' => 'boolean', ]; /** * The translatable columns. * * @var list */ public $translatable = [ 'slug', 'title', 'content', ]; /** * Perform any actions required after the model boots. * * @return void */ protected static function booted(): void { static::creating(function (self $model) { self::manageFields($model); $model->id_key = $model->slug; }); static::created(function () { self::resetCache(); }); static::updating(function (self $model) { self::manageFields($model); self::checkParentsStatus($model); }); static::updated(function (self $model) { self::resetCache(); self::setAllChildPageStatus($model); // TODO: update this after recursive deleting images. // DynamicPageHelper::removeImgs($model); }); static::deleted(function () { self::resetCache(); }); static::restoring(function (self $model) { if ($model->config_id && $model->config()->onlyTrashed()->exists()) { throw ValidationException::withMessages([ 'error' => trans('crud.messages.cannot_be_restored', [ 'model' => Str::ucfirst(trans_choice('models.classes.dynamic-page', 1)) ]), ]); } $model->status = PageStatusEnum::draft; }); static::restored(function () { self::resetCache(); }); static::forceDeleted(function (self $model) { self::resetCache(); // TODO: update this after recursive deleting images. // DynamicPageHelper::removeImgs($model, true); }); } // * METHODS /** * Store recursively all uploaded files in array. * Overwrite trait method to filter fields with image_ prefix. * * @return array */ private function processContentFiles(array $content): array { return collect($content) ->map(function ($value, $key) { // Check the pattern of the key and the type of value. if ( ( Str::startsWith($key, DynamicFieldTypes::image->name . '_') || Str::startsWith($key, DynamicFieldTypes::file->name . '_') ) && $value instanceof UploadedFile ) { return FileHelper::storeFile($this, $value); } // Check if it's an array. if (is_array($value)) { return $this->processContentFiles($value); } return $value; }) ->all(); } /** * Unpublished all children pages if change status published from draft/unpublished. * * @param self $model * @return boolean */ private static function setAllChildPageStatus(self $model): void { if ( !$model->status->equals(PageStatusEnum::published) && !$model->getOriginal('status')->equals($model->status) ) { $model->childPages->map(function (DynamicPage $page) use ($model) { $page->status = $model->status; $page->saveOrFail(); }); } } /** * Publish current page if all parents are published. * * @param self $model * @return boolean * @throws \Illuminate\Validation\ValidationException Parent must be published. */ private static function checkParentsStatus(self $model): void { if ($model->status->equals(PageStatusEnum::published) && !self::getParentsStatus($model)) { throw ValidationException::withMessages([ 'error' => trans('validation.parent_page'), ]); } } /** * Check if all parent of all associated page are published. * * @param self $model * @return boolean */ private static function getParentsStatus(self $model): bool { $parent = $model->parentPage; while ($parent) { if (!$parent->status->equals(PageStatusEnum::published)) { return false; } $parent = $parent->parentPage; } return true; } /** * Manage model fields. * * @param self $model * @return void */ private static function manageFields(self $model): void { // Check unique fields. $slug = $model->slug ?? Str::slug($model->title); TranslationHelper::assertTranslationFieldIsUnique($model, $model->title, 'title'); TranslationHelper::assertTranslationFieldIsUnique($model, $slug, 'slug'); $model->setAttribute('slug', $slug); // Check translations. self::validateFallbackTranslations($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(PageStatusEnum::published)) { return; } TranslationHelper::validateFallbackTranslations($model); } /** * Get the view of a specific dynamic page. * * @return string */ public function getView(): string { /** @var string $view The view used for the route. */ $view = PageConfig::$defaultView; // Check if the view in config is defined and can be used. if (!empty($this->config->view)) { $candidate = sprintf('front.pages.%s', $this->config->view); if (view()->exists($candidate)) { $view = $candidate; } } // Check if a view with the slug exists. if ($view === PageConfig::$defaultView && !empty($this->slug)) { $candidate = sprintf('front.pages.%s', $this->slug); if (view()->exists($candidate)) { $view = $candidate; } } return $view; } /** * Reset all cache elements related to this model. * * @return void */ public static function resetCache(): void { Cache::forget('dynamicPages'); self::getDynamicPages(); } /** * Get all published dynamic pages. * * @return \Illuminate\Database\Eloquent\Collection */ public static function getDynamicPages(): \Illuminate\Database\Eloquent\Collection { return Cache::remember('dynamicPages', 86400, function () { return self::query() ->with(['parentPage', 'childPages', 'config']) ->get(); }); } // * RELATIONSHIPS /** * Get children pages for this page. * * @return \Illuminate\Database\Eloquent\Relations\HasMany<\App\Models\DynamicPage,$this> */ public function childPages(): \Illuminate\Database\Eloquent\Relations\HasMany { return $this->hasMany(DynamicPage::class, 'page_id'); } /** * Get parent page for this page. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\App\Models\DynamicPage,$this> */ public function parentPage(): \Illuminate\Database\Eloquent\Relations\BelongsTo { return $this->belongsTo(DynamicPage::class, 'page_id'); } /** * Get the config for this page. * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\App\Models\PageConfig,$this> */ public function config(): \Illuminate\Database\Eloquent\Relations\BelongsTo { return $this->belongsTo(PageConfig::class, 'config_id'); } }