Completed
Pull Request — develop (#57)
by Tony
07:44
created

Settings::recursive_keys()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 17
rs 9.2
cc 4
eloc 11
nc 6
nop 3
1
<?php
2
/*
3
 * Copyright (C) 2016 Tony Murray <[email protected]>
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
/**
18
 * Settings.php
19
 *
20
 * @package    LibreNMS
21
 * @author     Tony Murray <[email protected]>
22
 * @copyright  2016 Tony Murray
23
 * @license    @license http://opensource.org/licenses/GPL-3.0 GNU Public License v3 or later
24
 */
25
namespace App;
26
27
28
use App\Models\DbConfig;
29
use Cache;
30
use Config;
31
use Illuminate\Contracts\Config\Repository as ConfigContract;
32
33
// adds the possibility to replace the default Config facade
34
35
class Settings implements ConfigContract
36
{
37
    /**
38
     * The tag for the cache.
39
     *
40
     * @var string
41
     */
42
    public static $cache_tag = "Settings";
43
    /**
44
     * The amount of time in minutes to store values in cache
45
     * @var int
46
     */
47
    private $cache_time;
48
49
    /**
50
     * Settings constructor.
51
     */
52
    public function __construct()
53
    {
54
        $this->cache_time = env('CACHE_LIFETIME', 60);
55
    }
56
57
    /**
58
     * Set a key value pair into the Settings store.
59
     * 
60
     * @param string $key A . separated path to this setting
61
     * @param array|null $value A value or an array. If value is an array it will be converted to a . separate path(s) concatinated onto the given key
62
     */
63
    public function set($key, $value = null)
64
    {
65
        // Clear cache that may contain this value
66
        $path = [];
67
        foreach (explode('.', $key) as $item) {
68
            $path[] = $item;
69
            Cache::tags(self::$cache_tag)->forget(join('.', $path));
70
        }
71
72
        // save the value to the database
73
        if (is_array($value)) {
74
            $settings = self::arrayToPath($value, $key);
75
            foreach ($settings as $k => $v) {
76
                DbConfig::updateOrCreate(['config_name' => $k], ['config_value' => $v]);
1 ignored issue
show
Bug introduced by
The method updateOrCreate() does not exist on App\Models\DbConfig. Did you maybe mean create()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
77
                Cache::tags(self::$cache_tag)->put($k, $v, $this->cache_time);
78
            }
79
        }
80
        else {
81
            DbConfig::updateOrCreate(['config_name' => $key], ['config_value' => $value]);
1 ignored issue
show
Bug introduced by
The method updateOrCreate() does not exist on App\Models\DbConfig. Did you maybe mean create()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
82
            Cache::tags(self::$cache_tag)->put($key, $value, $this->cache_time);
83
        }
84
85
    }
86
87
    /**
88
     * Convert a nested array of values to an array of . separated paths and values.
89
     *
90
     * @param $array array Nested array.
91
     * @param string $prefix Path to prepend to all keys.
92
     * @return array An array of path/value pairs.
93
     */
94
    private static function arrayToPath($array, $prefix = "")
95
    {
96
        return self::recursive_keys($array, $prefix);
97
    }
98
99
    private static function recursive_keys(array $array, $prefix = "", array $path = array())
100
    {
101
        if ($prefix != "") {
102
            $prefix = trim($prefix, '.') . '.';
103
        }
104
        $result = array();
105
        foreach ($array as $key => $val) {
106
            $currentPath = array_merge($path, array($key));
107
            if (is_array($val)) {
108
                $result = array_merge($result, self::recursive_keys($val, $prefix, $currentPath));
109
            }
110
            else {
111
                $result[$prefix . join('.', $currentPath)] = $val;
112
            }
113
        }
114
        return $result;
115
    }
116
117
    /**
118
     * Get a value from the Settings store.
119
     * 
120
     * @param string $key A full or partial . separated key.
121
     * @param null $default If the key isn't found, return this value. By default undefined keys return null.
122
     * @return mixed If the $key is a full path, a bare value will be returned.  If it is a partial path, a nested array will be retuned.
123
     */
124
    public function get($key, $default = null)
125
    {
126
        // return value from cache or fetch it and return it
127
        return Cache::tags(self::$cache_tag)->remember($key, $this->cache_time, function () use ($key, $default) {
128
            $db_data = DbConfig::key($key)->get(['config_name', 'config_value']);
129
130
            if (count($db_data) == 1 && $db_data->first()->config_name == $key) {
131
                // return a bare value if we are getting one item
132
                return $db_data->first()->config_value;
133
            }
134
            elseif (count($db_data) >= 1) {
135
                // convert collection to an array and merge with the config fallback
136
                $result = self::collectionToArray($db_data, $key);
137
                $config = Config::get('config.' . $key, $default);
138
                if (!is_null($config) && is_array($config)) {
139
                    $result = array_replace_recursive($config, $result);
140
                }
141
                return $result;
142
            }
143
            else {
144
                // fallback to the config or return the default value
145
                return Config::get('config.' . $key, $default);
146
            }
147
148
        });
149
    }
150
151
    /**
152
     * Convert an Eloquent Collection into a nested array
153
     *
154
     * @param $data \Illuminate\Database\Eloquent\Collection The Collection.
155
     * @param string $prefix Path to prepend. Do not include trailing .
156
     * @return array The resulting nested array.
157
     */
158
    private static function collectionToArray($data, $prefix = "")
159
    {
160
        $tree = array();
161
        foreach ($data as $item) {
162
            $key = $item->config_name;
163
            if (substr($key, 0, strlen($prefix)) == $prefix) {
164
                $key = substr($key, strlen($prefix));
165
            }
166
            $parts = explode('.', trim($key, '.'));
167
168
            $temp = &$tree;
169
            foreach ($parts as $part) {
170
                $temp = &$temp[$part];
171
            }
172
            $temp = $item->config_value;
173
            unset($temp);
174
        }
175
        return $tree;
176
    }
177
178
    /**
179
     * Check if the key is defined in the Settings store.
180
     * 
181
     * @param string $key Only full paths will return true.
182
     * @return bool 
183
     */
184
    public function has($key)
185
    {
186
        return (Cache::tags(self::$cache_tag)->has($key) || Config::has($key) || DbConfig::exactKey($key)->exists());
0 ignored issues
show
Bug introduced by
The method exactKey() does not exist on App\Models\DbConfig. Did you maybe mean scopeExactKey()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
187
    }
188
189
    /**
190
     * Forget a key.  Gets to forgotten keys will return null instead of the default.
191
     * @param $key string Only works for full paths.
192
     */
193
    public function forget($key)
194
    {
195
        // set to null to prevent falling back to Config
196
        DbConfig::key($key)->update(['config_value' => null]);
197
        Cache::tags(self::$cache_tag)->forget($key);
198
    }
199
200
    /**
201
     * Get all settings defined in the Settings store.
202
     * 
203
     * @return array A nested array of all settings.
204
     */
205
    public function all()
206
    {
207
        // no caching :(
208
        $config_settings = Config::all()['config'];
209
        $db_settings = self::collectionToArray(DbConfig::all());
210
        return array_replace_recursive($config_settings, $db_settings);
211
    }
212
213
    // ---- Local Utility functions ----
214
215
    /**
216
     * Clear the settings cache
217
     */
218
    public function flush()
219
    {
220
        Cache::tags(self::$cache_tag)->flush();
221
    }
222
223
    /**
224
     * Prepend a value onto an array configuration value.
225
     *
226
     * @param  string $key
227
     * @param  mixed $value
228
     * @return void
229
     */
230
    public function prepend($key, $value)
231
    {
232
        // TODO: Implement prepend() method.
233
    }
234
235
    /**
236
     * Push a value onto an array configuration value.
237
     *
238
     * @param  string $key
239
     * @param  mixed $value
240
     * @return void
241
     */
242
    public function push($key, $value)
243
    {
244
        // TODO: Implement push() method.
245
    }
246
}
247