Skip to content

Commit 5e41107

Browse files
committed
Initial commit
0 parents  commit 5e41107

File tree

8 files changed

+323
-0
lines changed

8 files changed

+323
-0
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# laravel-dynamic-config
2+
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.

composer.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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/laravel": ">5.8"
7+
},
8+
"license": "MIT",
9+
"authors": [
10+
{
11+
"name": "Emad Ha",
12+
"email": "[email protected]"
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+
}

config/site-config.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
return [
3+
/* The Config database table name */
4+
'table' => '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+
];
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class CreateSiteConfigTable extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
if (!Schema::hasTable(config('emadha.site-config.table'))) {
17+
Schema::create(config('emadha.site-config.table'), function (Blueprint $table) {
18+
$table->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+
}

src/DynamicConfig.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
namespace EmadHa\DynamicConfig;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
7+
/**
8+
* Class DynamicConfig
9+
*
10+
* @property mixed v
11+
* @package EmadHa\DynamicConfig
12+
*/
13+
class DynamicConfig extends Model
14+
{
15+
/**
16+
* @var array
17+
*/
18+
protected $guarded = ['id'];
19+
20+
/**
21+
* DynamicConfig constructor.
22+
*
23+
* @param array $attributes
24+
*/
25+
public function __construct(array $attributes = [])
26+
{
27+
parent::__construct($attributes);
28+
$this->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+

src/ServiceProvider.php

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
namespace EmadHa\DynamicConfig;
4+
5+
use Illuminate\Database\QueryException;
6+
use Illuminate\Support\Facades\DB;
7+
use Illuminate\Support\Facades\Log;
8+
use Illuminate\Support\Facades\Schema;
9+
use mysql_xdevapi\Exception;
10+
11+
class ServiceProvider extends \Illuminate\Support\ServiceProvider
12+
{
13+
/**
14+
* Register services.
15+
*
16+
* @return void
17+
*/
18+
public function register()
19+
{
20+
21+
}
22+
23+
/**
24+
* Bootstrap services.
25+
*
26+
* @return void
27+
* @throws \Exception
28+
*/
29+
public function boot()
30+
{
31+
if ($this->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+
}

0 commit comments

Comments
 (0)