├── .gitattributes
├── LICENSE
├── README.md
├── composer.json
├── config
└── site-config.php
├── database
└── migrations
│ └── create_site_config_table.php.stub
└── src
├── DynamicConfig.php
└── ServiceProvider.php
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Emad Ha
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Larvel Dynamic Config
2 |
3 |
4 | ## Introduction
5 | This Package makes it possible for users to have their config files stored in a database table, making it easier to customize these values from UI.
6 |
7 | ## Installation
8 | You can install the package via composer:
9 | ``` bash
10 | composer require emadha/laravel-dynamic-config
11 | ```
12 | The package will automatically register itself.
13 |
14 | You can publish the config with:
15 |
16 | Config file:
17 | ``` bash
18 | php artisan vendor:publish --provider="EmadHa\DynamicConfig\ServiceProvider" --tag="config"
19 | ```
20 |
21 | Migration:
22 | ```bash
23 | php artisan vendor:publish --provider="EmadHa\DynamicConfig\ServiceProvider" --tag="migrations"
24 | ```
25 |
26 | ## Usage
27 |
28 | First of all, you need to decide which config file(s) you want them to be stored in database by adding `'dynamic'=> true` to the file, as simple as that:
29 | ```php
30 | # /config/app.php
31 | return [
32 | 'dynamic' => true,
33 | ...
34 | ];
35 | ```
36 |
37 | > * Note that `dynamic` indicator is defined in `/config/emadha/dynamic-conf.php`:
38 | > * You can add `dynamic=>true` to any config file to store it in database and fetch the values from there instead of using the actual config file
39 | > * The values defaults will be taken from the actual config file.
40 | > * You can have more than one config file loaded dynamically
41 | > * `dynamic=>true` can only be added in the first dimension of that config array.
42 |
43 |
44 | The main config file of this packages is located in `/config/emadha/dynamic-conf.php` and contains the following code:
45 | ```php
46 | 'confs',
50 |
51 | /*
52 | * The key that defines which config file should be loaded dynamically
53 | * and store into the database
54 | * Add that key to any config file to make it dynamic.
55 | */
56 | 'dynamic_key' => 'dynamics',
57 |
58 | /*
59 | * they key which will have the defaults of a config key
60 | * example: config('defaults.app.name'); This is added on runtime.
61 | */
62 | 'defaults_key' => 'defaults',
63 |
64 | /*
65 | * Delete orphan keys
66 | * if set to true and delete a key from the actual config file,
67 | * that key will be deleted from database.
68 | */
69 | 'auto_delete_orphan_keys' => true,
70 | ];
71 | ```
72 | The content of that file is pretty clear and well documented.
73 |
74 | ## Usage
75 |
76 | ```php
77 | dd(config('app.name')); // Will return a Model object for that db row (key)
78 | echo config('app.name'); // Will get the value from a config key using __toString() method from the DynamicConfig Model;
79 | config('app.name')->setTo('Some New Value'); // will update that config key in database
80 | config('app.name')->default(); // Will return the default value of that key (from the actual config file and not from the database)
81 | config('app.name')->revert(); // Will revert the key value in database back to default (to what it is in the actual config file)
82 | ```
83 | As simple as that.
84 |
85 | ## Why is that Useful?
86 | In case you ever wanted to control the site title from a UI (backend, frontend), in a dynamic way without having to edit the actual config file, then this a good approach.
87 |
88 | Another Example: Let's say you have a google analytics code a long with some other customizations, and you have these in `/config/site.php` as follows:
89 |
90 | ```php
91 | # /config/site.php
92 | return [
93 | 'dynamic'=>true,
94 | 'title'=>config('app.name'),
95 | 'description'=>'My Site Meta Description',
96 | 'google'=>[
97 | 'UA'=>'UA-XXXXXXXX-X',
98 | 'enabled'=>true,
99 | ],
100 | ];
101 | ```
102 | This config file cannot be easily modified from a user interface, thus your clients will not be able to edit this without editing the actual file,
103 | In that case this package will prove to be useful, adding the key `dynamic`=>true to that config file will make it store it's values in database using the same format as Laravel, therefore it will be no different for you to get the value of some key in that config file, example `config('site.google.UA)`, plus adding some nice features like updating the value and revert back to default.
104 |
105 | With that approach you can now create a backend input to customize these, using one line of code `config('site.google.UA')->setTo('XYZ');` and then use it in your blade like normal:
106 | ```blade
107 | {{-- welcome.blade.php--}}
108 |
{{ config('site.title') }}
109 |
110 | ```
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "emadha/laravel-dynamic-config",
3 | "description": "This Package makes it possible for users to have their config files stored in a database table, making it easier to customize these values from UI.",
4 | "type": "library",
5 | "require": {
6 | "laravel/framework": "^7.0|^8.0|^9.0|^10.0|^11.0"
7 | },
8 | "license": "MIT",
9 | "authors": [
10 | {
11 | "name": "Emad Ha",
12 | "email": "me@emadha.com"
13 | }
14 | ],
15 | "minimum-stability": "dev",
16 | "autoload": {
17 | "psr-4": {
18 | "EmadHa\\DynamicConfig\\": "src/"
19 | }
20 | },
21 | "extra": {
22 | "laravel": {
23 | "providers": [
24 | "EmadHa\\DynamicConfig\\ServiceProvider"
25 | ]
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/config/site-config.php:
--------------------------------------------------------------------------------
1 | 'conf',
5 |
6 | /*
7 | * The key that defines which config file should be loaded dynamically
8 | * and store into the database
9 | * Add that key to any config file to make it dynamic.
10 | */
11 | 'dynamic_key' => 'dynamics',
12 |
13 | /*
14 | * they key which will have the defaults of a config key
15 | * example: config('defaults.app.name'); This is added on runtime.
16 | */
17 | 'defaults_key' => 'defaults',
18 |
19 | /*
20 | * Delete orphan keys
21 | * if set to true and delete a key from the actual config file,
22 | * that key will be deleted from database.
23 | */
24 | 'auto_delete_orphan_keys' => true,
25 | ];
--------------------------------------------------------------------------------
/database/migrations/create_site_config_table.php.stub:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
19 | $table->string('k', 500)->unique();
20 | $table->text('v')->nullable();
21 | $table->timestamps();
22 | });
23 | }
24 | }
25 |
26 | /**
27 | * Reverse the migrations.
28 | *
29 | * @return void
30 | */
31 | public function down()
32 | {
33 | Schema::drop(config('emadha.site-config.table'));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/DynamicConfig.php:
--------------------------------------------------------------------------------
1 | setTable(config('emadha.site-config.table'));
29 | }
30 |
31 | /**
32 | * Update the current key value
33 | *
34 | * @param $value
35 | *
36 | * @return bool
37 | */
38 | public function setTo($value)
39 | {
40 | return $this->update(['v' => $value]);
41 | }
42 |
43 | /**
44 | * Get the default value of the specified key
45 | *
46 | * @return \Illuminate\Config\Repository|mixed
47 | */
48 | public function default()
49 | {
50 | return config(
51 | config('emadha.site-config.defaults_key') . '.' . $this->k
52 | );
53 | }
54 |
55 | /**
56 | * Revert the current key to it's original value
57 | * from the actual config file
58 | *
59 | * @return mixed
60 | */
61 | public function revert()
62 | {
63 | return config($this->k)->setTo(
64 | config(config('emadha.site-config.defaults_key') . '.' . $this->k)
65 | );
66 | }
67 |
68 | /**
69 | * @return mixed|string
70 | */
71 | public function __toString()
72 | {
73 | return $this->v;
74 | }
75 |
76 | }
77 |
78 |
--------------------------------------------------------------------------------
/src/ServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->runningInConsole()) {
32 | if (!class_exists('CreateSiteConfigTable')) {
33 | $timestamp = date('Y_m_d_His', time());
34 | $this->publishes([
35 | __DIR__ . '/../database/migrations/create_site_config_table.php.stub' => database_path('migrations/' . $timestamp . '_create_site_config_table.php'),
36 | ], 'migrations');
37 | }
38 |
39 | $this->publishes([
40 | __DIR__ . '/../config/site-config.php' => config_path('emadha/site-config.php'),
41 | ], 'config');
42 | }
43 |
44 | $this->initConfig();
45 | }
46 |
47 | private function initConfig()
48 | {
49 |
50 | # Check if the table exists
51 | if (!Schema::hasTable(config('emadha.site-config.table'))) {
52 |
53 | # Don't crash, Log the error instead
54 | Log::error(sprintf(
55 | get_class($this) . " is missing the the dynamic config table [`%s`]. you might need to do `php artisan vendor:publish` && `php artisan migrate`",
56 | config('emadha.site-config.table'))
57 | );
58 |
59 | return false;
60 | }
61 |
62 | # Create a new collection of what's dynamic
63 | $DefaultConfig = collect([]);
64 |
65 | # Return the config entries containing ['dynamic'=>true] key
66 | collect(config()->all())->each(function ($value, $key) use (&$DefaultConfig) {
67 |
68 | # Check if the current config key has dynamic key set to it, and it's true
69 | if (array_key_exists(config('emadha.site-config.dynamic_key'), $value)
70 | && $value[config('emadha.site-config.dynamic_key')] == true) {
71 |
72 | # unset that dynamic value
73 | unset($value[config('emadha.site-config.dynamic_key')]);
74 |
75 | # Add that to the DynamicConfig collection
76 | $DefaultConfig->put($key, $value);
77 | }
78 |
79 | });
80 |
81 | # Keep the defaults for reference
82 | config([config('emadha.site-config.defaults_key') => $DefaultConfig]);
83 |
84 | # Flatten the config table data
85 | $prefixedKeys = $this->prefixKey(null, $DefaultConfig->all());
86 |
87 | # Insert the flattened data into database
88 | foreach ($prefixedKeys as $_key => $_value) {
89 |
90 | # Get the row from database if it exists,
91 | # If not, add it using the value from the actual config file.
92 | DynamicConfig::firstOrCreate(['k' => $_key], ['v' => $_value]);
93 |
94 | }
95 |
96 | # Build the Config array
97 | $DynamicConfig = DynamicConfig::all();
98 |
99 | # Check if auto deleting orphan keys is enabled
100 | # and delete those if they don't exists in the actual config file
101 | if (config('emadha.site-config.auto_delete_orphan_keys') == true) {
102 |
103 | # Check for orphan keys
104 | $orphanKeys = array_diff_assoc($DynamicConfig->pluck('v', 'k')->toArray(), $prefixedKeys);
105 |
106 | # Delete orphan keys
107 | DynamicConfig::whereIn('k', array_keys($orphanKeys))->delete();
108 |
109 | }
110 |
111 | # Store these config into the config() helper, but as model objects
112 | # Thus making Model's method accessible from here
113 | # example: config('app.name')->revert().
114 | # Available methods are `revert`, `default` and `setTo($value)`
115 | $DynamicConfig->map(function ($config) use ($DefaultConfig) {
116 | config([$config->k => $config]);
117 | });
118 |
119 | }
120 |
121 | public function prefixKey($prefix, $array)
122 | {
123 | $result = [];
124 | foreach ($array as $key => $value) {
125 | if (is_array($value)) {
126 | $result = array_merge($result, self::prefixKey($prefix . $key . '.', $value));
127 | } else {
128 | $result[$prefix . $key] = $value;
129 | }
130 | }
131 | return $result;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------