├── .php-cs-fixer.dist.php ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── settings.php ├── database └── migrations │ └── create_settings_table.php.stub └── src ├── CastHandler.php ├── Casts ├── CarbonCast.php └── CarbonPeriodCast.php ├── Contracts ├── Castable.php └── Repository.php ├── EntryFilter.php ├── Exceptions ├── CastHandlerException.php └── ModelTypeException.php ├── Facades └── Settings.php ├── HasSettings.php ├── Repositories ├── DatabaseRepository.php └── Repository.php ├── Settings.php ├── SettingsServiceProvider.php └── Support └── settings.php /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | notPath('bootstrap/*') 5 | ->notPath('storage/*') 6 | ->notPath('resources/view/mail/*') 7 | ->in([ 8 | __DIR__ . '/src', 9 | __DIR__ . '/tests', 10 | ]) 11 | ->name('*.php') 12 | ->notName('*.blade.php') 13 | ->ignoreDotFiles(true) 14 | ->ignoreVCS(true); 15 | 16 | return (new PhpCsFixer\Config()) 17 | ->setRules([ 18 | '@PSR2' => true, 19 | 'array_syntax' => ['syntax' => 'short'], 20 | 'ordered_imports' => ['sort_algorithm' => 'alpha'], 21 | 'no_unused_imports' => true, 22 | 'not_operator_with_successor_space' => true, 23 | 'trailing_comma_in_multiline' => true, 24 | 'phpdoc_scalar' => true, 25 | 'unary_operator_spaces' => true, 26 | 'binary_operator_spaces' => true, 27 | 'blank_line_before_statement' => [ 28 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], 29 | ], 30 | 'phpdoc_single_line_var_spacing' => true, 31 | 'phpdoc_var_without_name' => true, 32 | 'method_argument_space' => [ 33 | 'on_multiline' => 'ensure_fully_multiline', 34 | 'keep_multiple_spaces_after_comma' => true, 35 | ], 36 | 'single_trait_insert_per_statement' => true, 37 | ]) 38 | ->setFinder($finder); 39 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-settings` will be documented in this file. 4 | 5 | ## 1.0.0 - 2021-05-29 6 | - initial release 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Smartisan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # App & Models Settings for Laravel 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/smartisan/laravel-settings.svg?style=flat-square)](https://packagist.org/packages/smartisan/laravel-settings) 4 | [![GitHub Tests Action Status](https://github.com/iamohd/laravel-settings/workflows/run-tests/badge.svg)](https://github.com/iamohd/laravel-settings/actions?query=workflow%3Arun-tests) 5 | [![Total Downloads](https://img.shields.io/packagist/dt/smartisan/laravel-settings.svg?style=flat-square)](https://packagist.org/packages/smartisan/laravel-settings) 6 | 7 | This package allows you to store application wide and model specific Laravel settings. Settings values 8 | are type-cast and stored properly. You can also define your own casts and store repository. 9 | 10 | ## Installation 11 | 12 | Install the package via composer: 13 | 14 | ```bash 15 | composer require smartisan/laravel-settings 16 | ``` 17 | 18 | Publish the config file with: 19 | 20 | ```bash 21 | php artisan vendor:publish --provider="Smartisan\Settings\SettingsServiceProvider" --tag="config" 22 | ``` 23 | 24 | Publish the migration file with: 25 | 26 | ```bash 27 | php artisan vendor:publish --provider="Smartisan\Settings\SettingsServiceProvider" --tag="migrations" 28 | ``` 29 | 30 | And finally run the migration with: 31 | 32 | ```bash 33 | php artisan migrate 34 | ``` 35 | 36 | ## Usage 37 | 38 | The package provides various APIs to access the settings manager. You can access it with Settings facade, settings() 39 | helper method and via HasSettings trait on your models. 40 | 41 | ### Store Settings 42 | 43 | You can set single entry, or multiple entries of settings. The values of objects will be cast according to the rules 44 | defined in settings.php config file. 45 | 46 | * One Entry 47 | 48 | ```php 49 | Settings::set('key', 'value'); 50 | ``` 51 | 52 | * Multiple Entries 53 | 54 | You can set multiple settings entries by passing an associative array of keys and values. Casts will be applied on all 55 | the payload, even on nested arrays. 56 | 57 | ```php 58 | Settings::set([ 59 | 'k1' => Carbon::now(), 60 | 'k2' => [ 61 | 'k3' => $user, 62 | 'k4' => [ 63 | 'k5' => $model 64 | ], 65 | ] 66 | ]); 67 | ``` 68 | 69 | * Specify Settings Group 70 | 71 | It's possible to categorize your settings into groups by calling group method. 72 | 73 | ```php 74 | Settings::group('name')->set('key', 'value'); 75 | 76 | Settings::group('name')->set([ 77 | 'k1' => 'v1', 78 | 'k2' => 'v2', 79 | ]); 80 | ``` 81 | 82 | * Model Specific Settings 83 | 84 | It's also possible to set settings for a specific model by calling for method 85 | 86 | ```php 87 | Settings::for($model)->set('key', 'value'); 88 | 89 | Settings::for($model)->set([ 90 | 'k1' => 'v1', 91 | 'k2' => 'v2', 92 | ]); 93 | ``` 94 | 95 | * Mixing Filters 96 | 97 | You can mix all filters together like this: 98 | 99 | ```php 100 | Settings::for($model)->group('name')->set('key', 'value'); 101 | ``` 102 | 103 | ### Retrieve Settings 104 | 105 | You can retrieve one or multiple entries and specify the default value if not exist. 106 | 107 | * One Entry 108 | 109 | ```php 110 | Settings::get('k1', 'default'); 111 | ``` 112 | 113 | * Multiple Entries 114 | 115 | If the entry key does not exist, the default value will be placed instead 116 | 117 | ```php 118 | Settings::get(['k1', 'k2', 'k3'], 'default'); 119 | ``` 120 | 121 | * All Entries 122 | 123 | If you want to retrieve all entries, you simply call all method. You can also specify the model or group. Also to 124 | excempt some specific keys. 125 | 126 | **Note:** Remember that retrieving all entries without specifying the group or model, will retrieve all entries that has 127 | no group or model set. You can consider these as (global app settings). 128 | 129 | ```php 130 | Settings::all(); 131 | 132 | Settings::for($model)->all(); 133 | 134 | Settings::for($model)->group('name')->all(); 135 | 136 | Settings::except('k1', 'k2')->for($model)->group('name')->all(); 137 | 138 | Settings::except('k1')->for($model)->group('name')->all(); 139 | ``` 140 | 141 | ### Forget Entry 142 | 143 | You can remove entries by calling forget method. 144 | 145 | ```php 146 | Settings::forget('key'); 147 | 148 | Settings::for($model)->group('name')->forget('key'); 149 | ``` 150 | 151 | ### Determine Existance 152 | 153 | You can determine whether the given settings entry key exists or not 154 | 155 | ```php 156 | Settings::exist('key'); 157 | 158 | Settings::for($model)->exist('key'); 159 | 160 | Settings::for($model)->group('name')->exist('key'); 161 | ``` 162 | 163 | ### Helper Method 164 | 165 | The package also ships with a settings helper method, you can use it instead of using Settings Facade 166 | 167 | ```php 168 | settings()->set('key', 'value'); 169 | ... 170 | ``` 171 | 172 | ### HasSettings Trait 173 | 174 | You can use HasSettings trait on your Eloquent models as well 175 | 176 | 1. First prepare your model 177 | 178 | ```php 179 | use Smartisan\Settings\HasSettings; 180 | 181 | class User extends Model 182 | { 183 | use HasSettings; 184 | } 185 | ``` 186 | 187 | 2. Now you can call settings() method on that model. This is equivelant to ```Settings::for($model)``` 188 | 189 | ```php 190 | $user->settings()->set('k1', 'v1'); 191 | ... 192 | ``` 193 | 194 | ## Custom Repositories 195 | 196 | If you don't want to use the database repository, you can easily create your own settings repository. To do that 197 | 198 | 1. Create a class of your repository that implements Repository interface and implement the following methods 199 | 200 | ```php 201 | use Smartisan\Settings\Contracts\Repository; 202 | 203 | class FileRepository implements Repository 204 | { 205 | public function get($key,$default = null) { 206 | // 207 | } 208 | 209 | public function set($key,$value = null) { 210 | // 211 | } 212 | 213 | public function forget($key) { 214 | // 215 | } 216 | 217 | public function all() { 218 | // 219 | } 220 | } 221 | ``` 222 | 223 | 2. In settings configuration file, add your own repository config to repositories attribute 224 | 225 | ```php 226 | 'repositories' => [ 227 | ... 228 | 229 | 'file' => [ 230 | ... 231 | ] 232 | ], 233 | ``` 234 | 235 | 3. Change the default repository in settings config file to your own repository implementation 236 | 237 | ## Custom Casts 238 | 239 | The package allows you to easily create your own casting rules of any object type. 240 | 241 | 1. Create your own cast class that implements Castable interface. 242 | 243 | **Note:** The set method is called when the value of the entries is being stored to the repository, and the get method is called 244 | when the value is being retrieved from the repository. 245 | 246 | ```php 247 | use Smartisan\Settings\Contracts\Castable; 248 | 249 | class CustomCast implements Castable 250 | { 251 | public function set($payload) { 252 | // 253 | } 254 | 255 | public function get($payload) { 256 | // 257 | } 258 | } 259 | ``` 260 | 261 | 2. Add the casts to the array of casts in settings config file 262 | 263 | ```php 264 | 'casts' => [ 265 | ... 266 | CustomDataType::class => CustomCast::class 267 | ] 268 | ``` 269 | 270 | **Note:** If you want to pass a parameter to your cast, you can set an object of the cast instead of cast class name 271 | 272 | ```php 273 | 'casts' => [ 274 | ... 275 | CustomDataType::class => new CustomCast('param') 276 | ] 277 | ``` 278 | 279 | ## Settings Cache 280 | 281 | You can easily enable or disable caching settings in settings.php config file. You can also specify which caching store to use 282 | 283 | ```php 284 | 'cache' => [ 285 | 'enabled' => env('SETTINGS_CACHE_ENABLED', false), 286 | 'store' => null, 287 | 'prefix' => null, 288 | ], 289 | ``` 290 | 291 | ## Testing 292 | 293 | To run the package's tests, simply call the following command 294 | 295 | ```bash 296 | composer test 297 | ``` 298 | 299 | ## Changelog 300 | 301 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 302 | 303 | ## Credits 304 | 305 | - [Mohammed Isa](https://github.com/iamohd) 306 | - [All Contributors](../../contributors) 307 | 308 | ## Alternatives 309 | 310 | * [spatie/laravel-settings](https://github.com/spatie/laravel-settings) 311 | * [akaunting/laravel-setting](https://github.com/akaunting/laravel-setting) 312 | 313 | ## License 314 | 315 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 316 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "smartisan/laravel-settings", 3 | "description": "App & Models Settings for Laravel", 4 | "keywords": [ 5 | "laravel-settings" 6 | ], 7 | "homepage": "https://github.com/iamohd/laravel-settings", 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Mohammed Isa", 12 | "email": "mohd.itcs@gmail.com" 13 | } 14 | ], 15 | "require": { 16 | "php": "^8.0.2", 17 | "ext-json": "*", 18 | "illuminate/database": "^9.0", 19 | "illuminate/support": "^9.0" 20 | }, 21 | "require-dev": { 22 | "mockery/mockery": "^1.4", 23 | "orchestra/testbench": "^7.0", 24 | "phpunit/phpunit": "^9.1" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "Smartisan\\Settings\\": "src" 29 | }, 30 | "files": [ 31 | "src/Support/settings.php" 32 | ] 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "Smartisan\\Settings\\Tests\\": "tests" 37 | } 38 | }, 39 | "scripts": { 40 | "test": "vendor/bin/phpunit", 41 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 42 | }, 43 | "config": { 44 | "sort-packages": true 45 | }, 46 | "extra": { 47 | "laravel": { 48 | "providers": [ 49 | "Smartisan\\Settings\\SettingsServiceProvider" 50 | ], 51 | "aliases": { 52 | "Settings": "Smartisan\\Settings\\Facades\\Settings" 53 | } 54 | } 55 | }, 56 | "minimum-stability": "dev", 57 | "prefer-stable": true 58 | } 59 | -------------------------------------------------------------------------------- /config/settings.php: -------------------------------------------------------------------------------- 1 | env('SETTINGS_REPOSITORY_DEFAULT', 'database'), 15 | 16 | /* 17 | |-------------------------------------------------------------------------- 18 | | Settings Repositories 19 | |-------------------------------------------------------------------------- 20 | | 21 | | Here are each of the settings repositories setup. You can always implmenent 22 | | your own repository and configure it here. 23 | | 24 | */ 25 | 'repositories' => [ 26 | 'database' => [ 27 | 'handler' => Smartisan\Settings\Repositories\DatabaseRepository::class, 28 | 'connection' => null, 29 | 'table' => 'settings', 30 | ], 31 | ], 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Settings Cache 36 | |-------------------------------------------------------------------------- 37 | | 38 | | Here you can enable or disable caching settings. By default the caching 39 | | is disabled. When store value is set to null, it will use the default 40 | | caching driver set by your application. 41 | | 42 | */ 43 | 'cache' => [ 44 | 'enabled' => env('SETTINGS_CACHE_ENABLED', false), 45 | 'store' => null, 46 | 'prefix' => null, 47 | ], 48 | 49 | /* 50 | |-------------------------------------------------------------------------- 51 | | Settings Casts 52 | |-------------------------------------------------------------------------- 53 | | 54 | | Beside PHP's casting rules for other types, here you can define which 55 | | casts handler to use for every corresponding instances. 56 | | 57 | */ 58 | 'casts' => [ 59 | Carbon\Carbon::class => \Smartisan\Settings\Casts\CarbonCast::class, 60 | Carbon\CarbonPeriod::class => \Smartisan\Settings\Casts\CarbonPeriodCast::class, 61 | ], 62 | 63 | ]; 64 | -------------------------------------------------------------------------------- /database/migrations/create_settings_table.php.stub: -------------------------------------------------------------------------------- 1 | id(); 17 | $table->string('group')->nullable(); 18 | $table->string('key'); 19 | $table->json('payload'); 20 | $table->nullableMorphs('settingable'); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists(config('settings.repositories.database.table')); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /src/CastHandler.php: -------------------------------------------------------------------------------- 1 | applyCast($payload); 24 | }); 25 | 26 | return $entries; 27 | } 28 | 29 | return $this->applyCast($payload); 30 | } 31 | 32 | /** 33 | * Determine the appropiate cast to the given payload value. 34 | * 35 | * @param mixed $payload 36 | * @return array 37 | */ 38 | protected function applyCast($payload) 39 | { 40 | $cast = $this->resolveCast($payload); 41 | 42 | if (! $cast->handler) { 43 | return [ 44 | '$value' => $payload, 45 | '$cast' => null, 46 | ]; 47 | } 48 | 49 | if (is_string($cast->handler) && 50 | class_exists($cast->handler) && 51 | Arr::exists(class_implements($cast->handler), Castable::class)) { 52 | return [ 53 | '$value' => app($cast->handler)->set($payload), 54 | '$cast' => $cast->type, 55 | ]; 56 | } 57 | 58 | if (is_object($cast->handler) && $cast->handler instanceof Castable) { 59 | return [ 60 | '$value' => ($cast->handler)->set($payload), 61 | '$cast' => get_class($payload), 62 | ]; 63 | } 64 | 65 | throw CastHandlerException::invalid($cast->handler); 66 | } 67 | 68 | /** 69 | * Resolve the corresponding cast of the given payload data type. 70 | * 71 | * @param mixed $payload 72 | * @return object 73 | */ 74 | protected function resolveCast($payload) 75 | { 76 | $casts = config('settings.casts'); 77 | 78 | foreach ($casts as $type => $handler) { 79 | if ($payload instanceof $type) { 80 | return (object) [ 81 | 'type' => $type, 82 | 'handler' => $handler, 83 | ]; 84 | } 85 | } 86 | 87 | return (object) [ 88 | 'type' => null, 89 | 'handler' => null, 90 | ]; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Casts/CarbonCast.php: -------------------------------------------------------------------------------- 1 | format(DATE_ATOM); 19 | } 20 | 21 | /** 22 | * Apply casting rules when retrieving the payload from the settings repository. 23 | * 24 | * @param string $payload 25 | * @return \Carbon\Carbon 26 | */ 27 | public function get($payload) 28 | { 29 | return new Carbon($payload); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Casts/CarbonPeriodCast.php: -------------------------------------------------------------------------------- 1 | $payload->getStartDate(), 20 | 'end' => $payload->getEndDate(), 21 | ]; 22 | } 23 | 24 | /** 25 | * Apply casting rules when storing the payload into the settings repository. 26 | * 27 | * @param array $payload 28 | * @return \Carbon\CarbonPeriod 29 | */ 30 | public function get($payload) 31 | { 32 | return new CarbonPeriod( 33 | $payload['start'], 34 | $payload['end'] 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Contracts/Castable.php: -------------------------------------------------------------------------------- 1 | model = $model; 44 | 45 | return $this; 46 | } 47 | 48 | /** 49 | * Set the group name of the settings entry. 50 | * 51 | * @param string $group 52 | * @return \Smartisan\Settings\EntryFilter $this 53 | */ 54 | public function setGroup($group) 55 | { 56 | $this->group = $group; 57 | 58 | return $this; 59 | } 60 | 61 | /** 62 | * Set the exempted settings entries. 63 | * 64 | * @param string|array $excepts 65 | * @return \Smartisan\Settings\EntryFilter $this 66 | */ 67 | public function setExcepts(...$excepts) 68 | { 69 | if (count($excepts) === 1 && is_array($excepts[0])) { 70 | $this->excepts = $excepts[0]; 71 | } else { 72 | $this->excepts = $excepts; 73 | } 74 | 75 | return $this; 76 | } 77 | 78 | /** 79 | * Get the settings entry group name. 80 | * 81 | * @return string 82 | */ 83 | public function getGroup() 84 | { 85 | return $this->group; 86 | } 87 | 88 | /** 89 | * Get the settings entry owner instance. 90 | * 91 | * @return mixed 92 | */ 93 | public function getModel() 94 | { 95 | return $this->model; 96 | } 97 | 98 | /** 99 | * Get list the of exempted settings entries key values. 100 | * 101 | * @return array 102 | */ 103 | public function getExcepts() 104 | { 105 | return $this->excepts; 106 | } 107 | 108 | /** 109 | * Reset the current filter to default values. 110 | * 111 | * @return void 112 | */ 113 | public function clear() 114 | { 115 | $this->model = null; 116 | 117 | $this->group = null; 118 | 119 | $this->excepts = []; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Exceptions/CastHandlerException.php: -------------------------------------------------------------------------------- 1 | connection = config('database.connection'); 41 | 42 | $this->table = config('settings.repositories.database.table'); 43 | 44 | $this->castHandler = app(CastHandler::class); 45 | } 46 | 47 | /** 48 | * Retrieve settings entry for the given key. 49 | * The configured values of entry filter will be used to filter the settings entries. 50 | * 51 | * @param string|array $key 52 | * @param mixed $default 53 | * @return mixed 54 | */ 55 | public function get($key, $default = null) 56 | { 57 | $entries = $this->getEntries($key, $default); 58 | 59 | if (is_array($key)) { 60 | return $entries->toArray(); 61 | } 62 | 63 | if ($entries->isNotEmpty()) { 64 | return $entries->first(); 65 | } 66 | 67 | return $default; 68 | } 69 | 70 | /** 71 | * Store settings entry for the given key. 72 | * The configured values of entry filter will be used to filter the settings entries. 73 | * 74 | * @param string|array $key 75 | * @param mixed $value 76 | * @return void 77 | */ 78 | public function set($key, $value = null) 79 | { 80 | if (is_array($key)) { 81 | collect($key)->each(function ($value, $key) { 82 | $this->updateOrInsertSettingsEntry($key, $value); 83 | }); 84 | 85 | return; 86 | } 87 | 88 | $this->updateOrInsertSettingsEntry($key, $value); 89 | } 90 | 91 | /** 92 | * Destroy the settings entry for the given key. 93 | * 94 | * @param string|array $key 95 | * @return void 96 | */ 97 | public function forget($key) 98 | { 99 | $key = collect($key); 100 | 101 | $model = $this->entryFilter->getModel(); 102 | 103 | DB::connection($this->connection) 104 | ->table($this->table) 105 | ->whereIn('key', $key->toArray()) 106 | ->where(function (Builder $builder) use ($model) { 107 | if ($model) { 108 | return $builder->where('settingable_type', get_class($model)) 109 | ->where('settingable_id', $model->getKey()); 110 | } 111 | 112 | return $builder->where('settingable_type', null) 113 | ->where('settingable_id', null); 114 | }) 115 | ->delete(); 116 | } 117 | 118 | /** 119 | * Retrieve all settings entry. 120 | * The configured values of entry filter will be used to filter the settings entries. 121 | * 122 | * @return array 123 | */ 124 | public function all() 125 | { 126 | return $this->getEntries(null, null)->toArray(); 127 | } 128 | 129 | /** 130 | * Retrieve the evalulated settings entries for the given key. 131 | * 132 | * @param string|array $keys 133 | * @param mixed $default 134 | * @return \Illuminate\Support\Collection 135 | */ 136 | protected function getEntries($keys, $default) 137 | { 138 | $keys = collect($keys); 139 | 140 | $group = $this->entryFilter->getGroup(); 141 | 142 | $model = $this->entryFilter->getModel(); 143 | 144 | $excepts = $this->entryFilter->getExcepts(); 145 | 146 | return DB::connection($this->connection) 147 | ->table($this->table) 148 | ->when($keys->count(), function (Builder $builder) use ($keys) { 149 | $builder->whereIn('key', $keys); 150 | }) 151 | ->when(count($excepts), function (Builder $builder) use ($excepts) { 152 | $builder->whereNotIn('key', $excepts); 153 | }) 154 | ->where('group', $group) 155 | ->where(function (Builder $builder) use ($model) { 156 | if ($model) { 157 | return $builder->where('settingable_type', get_class($model)) 158 | ->where('settingable_id', $model->getKey()); 159 | } 160 | 161 | return $builder->where('settingable_type', null) 162 | ->where('settingable_id', null); 163 | }) 164 | ->pluck('payload', 'key') 165 | ->when($keys->count() === 0, function (Collection $collection) { 166 | return $collection->flatMap(function ($payload, $key) { 167 | return [$key => json_decode($payload, true)]; 168 | }); 169 | }) 170 | ->when($keys->count(), function ($collection) use ($keys, $default) { 171 | return $collection->pipe(function ($entries) use ($keys, $default) { 172 | return $keys->flatMap(function ($key) use ($entries, $default) { 173 | if ($entries->has($key)) { 174 | return [$key => json_decode($entries->get($key), true)]; 175 | } 176 | 177 | return [$key => $default]; 178 | }); 179 | }); 180 | }); 181 | } 182 | 183 | /** 184 | * Update the given settings entry or insert when it does not exist. 185 | * 186 | * @param string $key 187 | * @param mixed $value 188 | * @return bool 189 | */ 190 | protected function updateOrInsertSettingsEntry($key, $value) 191 | { 192 | return DB::connection($this->connection) 193 | ->table($this->table) 194 | ->updateOrInsert([ 195 | 'key' => $key, 196 | 'group' => $this->entryFilter->getGroup(), 197 | 'settingable_type' => $this->entryFilter->getModel() ? get_class($this->entryFilter->getModel()) : null, 198 | 'settingable_id' => $this->entryFilter->getModel() ? $this->entryFilter->getModel()->getKey() : null, 199 | ], [ 200 | 'payload' => json_encode($this->castHandler->handle($value)), 201 | 'created_at' => now(), 202 | 'updated_at' => now(), 203 | ]); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/Repositories/Repository.php: -------------------------------------------------------------------------------- 1 | entryFilter = $filter; 25 | 26 | return $this; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Settings.php: -------------------------------------------------------------------------------- 1 | repository = $repository; 37 | 38 | $this->cache = $cache; 39 | 40 | $this->filter = app(EntryFilter::class); 41 | } 42 | 43 | /** 44 | * Store settings entry for the given key. 45 | * The configured values of entry filter will be used to filter the settings entries. 46 | * 47 | * @param string|array $key 48 | * @param mixed $value 49 | * @return void 50 | */ 51 | public function set($key, $value = null) 52 | { 53 | $this->forgetCacheIfEnabled($key); 54 | 55 | $this->repository 56 | ->withFilter($this->filter) 57 | ->set($key, $value); 58 | 59 | $this->filter->clear(); 60 | } 61 | 62 | /** 63 | * Retrieve settings entry for the given key. 64 | * The configured values of entry filter will be used to filter the settings entries. 65 | * 66 | * @param string|array $key 67 | * @param mixed $default 68 | * @return mixed 69 | */ 70 | public function get($key, $default = null) 71 | { 72 | if (config('settings.cache.enabled')) { 73 | return $this->cache->rememberForever($this->resolveCacheKey($key), function () use ($key, $default) { 74 | return $this->getEntries($key, $default); 75 | }); 76 | } 77 | 78 | return $this->getEntries($key, $default); 79 | } 80 | 81 | /** 82 | * Destroy the settings entry for the given key. 83 | * 84 | * @param string|array $key 85 | * @return void 86 | */ 87 | public function forget($key) 88 | { 89 | $this->forgetCacheIfEnabled($key); 90 | 91 | $this->repository 92 | ->withFilter($this->filter) 93 | ->forget($key); 94 | 95 | $this->filter->clear(); 96 | } 97 | 98 | /** 99 | * Retrieve all settings entry. 100 | * The configured values of entry filter will be used to filter the settings entries. 101 | * 102 | * @return array 103 | */ 104 | public function all() 105 | { 106 | if (config('settings.cache.enabled')) { 107 | return $this->cache->rememberForever($this->resolveCacheKey(null), function () { 108 | return $this->getAllEntries(); 109 | }); 110 | } 111 | 112 | return $this->getAllEntries(); 113 | } 114 | 115 | /** 116 | * Determines whether the given settings entry exists or not. 117 | * 118 | * @param string $key 119 | * @return bool 120 | */ 121 | public function exists($key) 122 | { 123 | $entry = $this->get($key); 124 | 125 | return isset($entry); 126 | } 127 | 128 | /** 129 | * Set the model owner of the settings entry. 130 | * 131 | * @param \Illuminate\Database\Eloquent\Model $model 132 | * @return \Smartisan\Settings\Settings 133 | */ 134 | public function for($model) 135 | { 136 | $this->filter->setModel($model); 137 | 138 | return $this; 139 | } 140 | 141 | /** 142 | * Set the group name of the settings entry. 143 | * 144 | * @param string $name 145 | * @return \Smartisan\Settings\Settings 146 | */ 147 | public function group($name) 148 | { 149 | $this->filter->setGroup($name); 150 | 151 | return $this; 152 | } 153 | 154 | /** 155 | * Set the exempted settings entries. 156 | * 157 | * @param string|array $excepts 158 | * @return \Smartisan\Settings\Settings 159 | */ 160 | public function except(...$excepts) 161 | { 162 | $this->filter->setExcepts(...$excepts); 163 | 164 | return $this; 165 | } 166 | 167 | /** 168 | * Resolve settings entry caching key. 169 | * 170 | * @param string|array|null $keys 171 | * @return string 172 | */ 173 | public function resolveCacheKey($keys) 174 | { 175 | $prefix = config('settings.cache.prefix'); 176 | 177 | $keys = is_array($keys) ? implode(',', $keys) : $keys; 178 | 179 | $group = $this->filter->getGroup(); 180 | 181 | $for = $this->filter->getModel() ? get_class($this->filter->getModel()) : null; 182 | 183 | $excepts = implode(',', $this->filter->getExcepts()); 184 | 185 | return "${prefix}settings.keys=${keys}&group=${group}&excepts=${excepts}&for=$for"; 186 | } 187 | 188 | /** 189 | * Retrieve the evalulated settings entries for the given key. 190 | * 191 | * @param string|array $key 192 | * @param mixed $default 193 | * @return mixed 194 | */ 195 | protected function getEntries($key, $default) 196 | { 197 | $payload = $this->repository 198 | ->withFilter($this->filter) 199 | ->get($key, $default); 200 | 201 | $this->filter->clear(); 202 | 203 | $this->stripSettingsPayload($payload); 204 | 205 | return $payload; 206 | } 207 | 208 | /** 209 | * Retrieve all settings entries. 210 | * 211 | * @return array 212 | */ 213 | protected function getAllEntries() 214 | { 215 | $payload = $this->repository 216 | ->withFilter($this->filter) 217 | ->all(); 218 | 219 | $this->filter->clear(); 220 | 221 | $this->stripSettingsPayload($payload); 222 | 223 | return $payload; 224 | } 225 | 226 | /** 227 | * Clear the given caching key values. 228 | * 229 | * @param string $key 230 | * @return void 231 | */ 232 | protected function forgetCacheIfEnabled($key) 233 | { 234 | if (config('settings.cache.enabled')) { 235 | $cacheKey = $this->resolveCacheKey(is_array($key) ? array_keys($key) : $key); 236 | 237 | if ($this->cache->has($cacheKey)) { 238 | $this->cache->forget($cacheKey); 239 | } 240 | } 241 | } 242 | 243 | /** 244 | * Evaluate the payload and strip additional attributes. 245 | * 246 | * @param mixed $payload 247 | * @return array 248 | */ 249 | protected function stripSettingsPayload(&$payload) 250 | { 251 | if (is_array($payload)) { 252 | if (array_key_exists('$value', $payload) && array_key_exists('$cast', $payload)) { 253 | $castType = $payload['$cast']; 254 | 255 | if ($castType) { 256 | if (! array_key_exists($castType, config('settings.casts'))) { 257 | throw CastHandlerException::missing($castType); 258 | } 259 | 260 | return $payload = app(config('settings.casts')[$castType]) 261 | ->get($payload['$value']); 262 | } 263 | 264 | $payload = $payload['$value']; 265 | } else { 266 | array_walk($payload, [$this, 'stripSettingsPayload']); 267 | } 268 | } 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /src/SettingsServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__ . '/../config/settings.php', 'settings'); 18 | 19 | $this->app->bind('settings', function () { 20 | $settingsConfig = config( 21 | sprintf('settings.repositories.%s', config('settings.default')) 22 | ); 23 | 24 | $cacheRepository = Cache::store( 25 | config('settings.cache.store') 26 | ); 27 | 28 | $settingsRepository = new $settingsConfig['handler']; 29 | 30 | return new Settings($settingsRepository, $cacheRepository); 31 | }); 32 | } 33 | 34 | /** 35 | * Boot registered package services. 36 | * 37 | * @return void 38 | */ 39 | public function boot(): void 40 | { 41 | if ($this->app->runningInConsole()) { 42 | $this->publishes([ 43 | __DIR__ . '/../config/settings.php' => config_path('settings.php'), 44 | ], 'config'); 45 | 46 | $this->publishes([ 47 | __DIR__ . '/../database/migrations/create_settings_table.php.stub' => database_path('migrations/' . date('Y_m_d_His', time()) . '_create_settings_table.php'), 48 | ], 'migrations'); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Support/settings.php: -------------------------------------------------------------------------------- 1 |