getAttributes(), $staticAttributes); $this->data = $dynamicAttributes; $this->setRawAttributes(array_only($this->getAttributes(), $staticAttributes)); } /** * Clear asset cache after saving to ensure `assetVar` form fields take * immediate effect. */ public function afterSave() { static::flushCache($this->theme); try { CombineAssets::resetCache(); } catch (Exception $ex) { } } /** * Clear the cache after deleting so that the record isn't served from the cache. */ public function afterDelete() { static::flushCache($this->theme); } /** * Returns the cache key used to store the data for the provided theme directory. */ public static function getCacheKey(string $dirName): string { return 'cms::theme.data.' . $dirName; } /** * Removes both the persistent and in memory cache of the provided theme directory's data. */ public static function flushCache(?string $dirName = null): void { if (is_null($dirName)) { self::$instances = []; return; } Cache::forget(static::getCacheKey($dirName)); unset(self::$instances[$dirName]); } /** * Returns a cached version of this model, based on a Theme object. * @param $theme Cms\Classes\Theme * @return self */ public static function forTheme($theme) { $dirName = $theme->getDirName(); if ($themeData = array_get(self::$instances, $dirName)) { return $themeData; } try { // The record is cached rather than queried on every request; it is invalidated // by afterSave() / afterDelete(), which also covers the initial creation below. $themeData = self::where('theme', $dirName) ->remember(self::$cacheTtl, self::getCacheKey($dirName)) ->first() ?: self::create(['theme' => $dirName]); } catch (Exception $ex) { // Database failed $themeData = new self(['theme' => $dirName]); } return self::$instances[$dirName] = $themeData; } /** * After fetching the model, intiialize model relationships based * on form field definitions. * @return void */ public function afterFetch() { $data = (array) $this->data + $this->getDefaultValues(); foreach ($this->getFormFields() as $id => $field) { if (!isset($field['type'])) { continue; } /* * Repeater and nested form fields store arrays and must be jsonable. */ if (in_array($field['type'], ['repeater', 'nestedform'])) { $this->jsonable[] = $id; } elseif ($field['type'] === 'fileupload') { if (array_get($field, 'multiple', false)) { $this->attachMany[$id] = File::class; } else { $this->attachOne[$id] = File::class; } unset($data[$id]); } } /* * Fill this model with the jsonable attributes kept in 'data'. */ $this->setRawAttributes((array) $this->getAttributes() + $data, true); } /** * Before model is validated, set the default values. * @return void */ public function beforeValidate() { if (!$this->exists) { $this->setDefaultValues(); } } /** * Creates relationships for this model based on form field definitions. */ public function initFormFields() { } /** * Sets default values on this model based on form field definitions. */ public function setDefaultValues() { foreach ($this->getDefaultValues() as $attribute => $value) { $this->{$attribute} = $value; } } /** * Gets default values for this model based on form field definitions. * @return array */ public function getDefaultValues() { $result = []; foreach ($this->getFormFields() as $attribute => $field) { if (($value = array_get($field, 'default')) === null) { continue; } $result[$attribute] = $value; } return $result; } /** * Returns all fields defined for this model, based on form field definitions. * @return array */ public function getFormFields() { if (!$theme = CmsTheme::load($this->theme)) { throw new Exception(Lang::get('Unable to find theme with name :name', $this->theme)); } $config = $theme->getFormConfig(); return array_get($config, 'fields', []) + array_get($config, 'tabs.fields', []) + array_get($config, 'secondaryTabs.fields', []); } /** * Returns variables that should be passed to the asset combiner. * @return array */ public function getAssetVariables() { $result = []; foreach ($this->getFormFields() as $attribute => $field) { if (!$varName = array_get($field, 'assetVar')) { continue; } $result[$varName] = $this->{$attribute}; } return $result; } /** * Applies asset variables to the combiner filters that support it. * @return void */ public static function applyAssetVariablesToCombinerFilters($filters) { $theme = CmsTheme::getActiveTheme(); if (!$theme) { return; } if (!$theme->hasCustomData()) { return; } $assetVars = $theme->getCustomData()->getAssetVariables(); foreach ($filters as $filter) { if (method_exists($filter, 'setPresets')) { $filter->setPresets($assetVars); } } } /** * Generate a cache key for the combiner, this allows variables to bust the cache. * @return string */ public static function getCombinerCacheKey() { $theme = CmsTheme::getActiveTheme(); if (!$theme->hasCustomData()) { return ''; } $customData = $theme->getCustomData(); return (string) $customData->updated_at ?: ''; } }