├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── Torann │ └── Registry │ │ ├── Cache.php │ │ ├── Facades │ │ └── Registry.php │ │ ├── Registry.php │ │ ├── RegistryServiceProvider.php │ │ └── Timestamps │ │ ├── Redis.php │ │ └── TimestampInterface.php ├── config │ └── registry.php └── migrations │ └── 2015_02_15_185537_create_system_registries_table.php └── tests └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | 8 | before_script: 9 | - curl -s http://getcomposer.org/installer | php 10 | - php composer.phar install --dev 11 | 12 | script: phpunit -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The BSD 2-Clause License 2 | Copyright (c) 2013-2015, Daniel Stainback 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Registry Manager for Laravel 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/torann/registry/v/stable.png)](https://packagist.org/packages/torann/registry) [![Total Downloads](https://poser.pugx.org/torann/registry/downloads.png)](https://packagist.org/packages/torann/registry) 4 | 5 | Registry manager for Laravel 5. An alternative for managing application configurations and settings. Now with the magic of caching, so no more database calls to simply get site setting. 6 | 7 | ---------- 8 | 9 | ## Installation 10 | 11 | - [Registry on Packagist](https://packagist.org/packages/torann/registry) 12 | - [Registry on GitHub](https://github.com/Torann/laravel-registry) 13 | - [Laravel 4 Installation](http://lyften.com/projects/laravel-registry/doc/laravel-4.html) 14 | 15 | To get the latest version of Registry simply require it in your `composer.json` file. 16 | 17 | ~~~ 18 | "torann/registry": "0.2.*@dev" 19 | ~~~ 20 | 21 | You'll then need to run `composer install` to download it and have the autoloader updated. 22 | 23 | Once Registry is installed you need to register the service provider with the application. Open up `app/app.php` and find the `providers` key. 24 | 25 | ```php 26 | 'providers' => array( 27 | 'Torann\Registry\RegistryServiceProvider', 28 | ) 29 | ``` 30 | 31 | Registry also ships with a facade which provides the static syntax for creating collections. You can register the facade in the aliases key of your `app/app.php` file. 32 | 33 | ```php 34 | 'aliases' => array( 35 | 'Registry' => 'Torann\Registry\Facades\Registry', 36 | ) 37 | ``` 38 | 39 | ### Publish the configurations and migration 40 | 41 | Run this on the command line from the root of your project: 42 | 43 | ~~~ 44 | $ php artisan vendor:publish 45 | ~~~ 46 | 47 | A configuration file will be publish to `config/registry.php` and a migration file to `database/migrations/` 48 | 49 | ## Documentation 50 | 51 | [View the official documentation](http://lyften.com/projects/laravel-registry/). 52 | 53 | ## Change Log 54 | 55 | #### v0.2.0 56 | 57 | - Update to Laravel 5 58 | 59 | #### v0.1.3 60 | 61 | - Added timestamp managers for multi-instance websites 62 | - Added custom caching 63 | 64 | #### v0.1.2 65 | 66 | - Added config for custom table name 67 | - Added forced variable types 68 | - Code cleanup 69 | 70 | #### v0.1.1 71 | 72 | - Bug fixes 73 | 74 | #### v0.1.0 75 | 76 | - First release -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "torann/registry", 3 | "description": "Laravel registry manager for application configurations", 4 | "keywords": ["registry", "laravel", "settings", "configuration"], 5 | "license": "BSD-2-Clause", 6 | "authors": [ 7 | { 8 | "name": "Daniel Stainback", 9 | "email": "daniel@lyften.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=5.3.0", 14 | "illuminate/support": "~5.0" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "Torann\\Registry\\": "src/Torann/Registry" 19 | } 20 | }, 21 | "extra": { 22 | "branch-alias": { 23 | "dev-master": "0.2-dev" 24 | } 25 | }, 26 | "minimum-stability": "dev" 27 | } 28 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Torann/Registry/Cache.php: -------------------------------------------------------------------------------- 1 | path = $cachePath.DIRECTORY_SEPARATOR.'torann_registry.json'; 42 | 43 | // Instantiate timestamp manager 44 | if (class_exists($timestampManager)) { 45 | $this->timestampManager = new $timestampManager(); 46 | } 47 | 48 | // Load values 49 | if (file_exists($this->path)) { 50 | $this->entries = json_decode(file_get_contents($this->path), true); 51 | } 52 | } 53 | 54 | /** 55 | * Get a all cached values. 56 | * 57 | * @param mixed $default 58 | * @return mixed 59 | */ 60 | public function all($default = array()) 61 | { 62 | return (! empty($this->entries) ) ? $this->entries : $default; 63 | } 64 | 65 | /** 66 | * Get a key's value from the cache. 67 | * 68 | * @param string $key 69 | * @param mixed $default 70 | * @return mixed 71 | */ 72 | public function get($key = null, $default = null) 73 | { 74 | return isset($this->entries[$key]) ? $this->entries[$key] : $default; 75 | } 76 | 77 | /** 78 | * Add a key's value to the cache. 79 | * 80 | * @param string $key 81 | * @param string $value 82 | * @return bool 83 | */ 84 | public function add($key, $value) 85 | { 86 | $this->entries[$key] = $value; 87 | 88 | return $this->save(); 89 | } 90 | 91 | /** 92 | * Set value to the cache. 93 | * 94 | * @param array $value 95 | * @return bool 96 | */ 97 | public function set(array $value) 98 | { 99 | $this->entries = $value; 100 | 101 | return $this->save(); 102 | } 103 | 104 | /** 105 | * Remove a key from cache. 106 | * 107 | * @param string $key 108 | * @return bool 109 | */ 110 | public function remove($key) 111 | { 112 | unset($this->entries[$key]); 113 | 114 | return $this->save(); 115 | } 116 | 117 | /** 118 | * Remove all cached entries. 119 | * 120 | * @return bool 121 | */ 122 | public function flush() 123 | { 124 | $this->entries = null; 125 | 126 | return $this->save(); 127 | } 128 | 129 | /** 130 | * Get last updated timestamp. 131 | * 132 | * @return string 133 | */ 134 | public function getTimestamp() 135 | { 136 | return $this->get($this->timestampKey); 137 | } 138 | 139 | /** 140 | * Check if cached as expired. 141 | * 142 | * @return bool 143 | */ 144 | public function expired() 145 | { 146 | // Update if empty 147 | if (empty($this->entries)) { 148 | return true; 149 | } 150 | 151 | // Check timestamps 152 | if ($this->timestampManager) { 153 | return $this->timestampManager->check($this->getTimestamp()); 154 | } 155 | 156 | return false; 157 | } 158 | 159 | /** 160 | * Save to cache. 161 | * 162 | * @return bool 163 | */ 164 | public function save() 165 | { 166 | // Update time - now 167 | $updated = time(); 168 | 169 | // Update timestamp 170 | $this->entries[$this->timestampKey] = $updated; 171 | 172 | // Update timestamp manager 173 | if ($this->timestampManager) { 174 | $this->timestampManager->update($updated); 175 | } 176 | 177 | return (bool) file_put_contents($this->path, json_encode($this->entries)); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/Torann/Registry/Facades/Registry.php: -------------------------------------------------------------------------------- 1 | database = $database; 45 | $this->config = $config; 46 | $this->cache = $cache; 47 | 48 | // Ensure cache is set 49 | $this->setCache(); 50 | } 51 | 52 | /** 53 | * Get value from registry 54 | * 55 | * @param string $key 56 | * @param string $default 57 | * @return mixed 58 | */ 59 | public function get($key, $default = null) 60 | { 61 | list($baseKey, $searchKey) = $this->fetchKey($key); 62 | 63 | $value = $this->fetchValue($baseKey, $searchKey); 64 | 65 | return (! is_null($value)) ? $value : $default; 66 | } 67 | 68 | /** 69 | * Store value into registry 70 | * 71 | * @param string $key 72 | * @param mixed $value 73 | * @return bool 74 | */ 75 | public function set($key, $value) 76 | { 77 | list($baseKey, $searchKey) = $this->fetchKey($key); 78 | 79 | $registry = $this->get($baseKey); 80 | 81 | if ($registry === null) { 82 | $registry = $this->cache->get($baseKey); 83 | } 84 | 85 | if (! is_null($registry)) { 86 | return $this->overwrite($key, $value); 87 | } 88 | 89 | if ($baseKey != $searchKey) 90 | { 91 | $object = array(); 92 | $level = ''; 93 | $keys = explode('.', $searchKey); 94 | 95 | foreach ($keys as $key) 96 | { 97 | $level .= '.'.$key; 98 | (trim($level, '.') == $searchKey) ? array_set($object, trim($level, '.'), $value) : array_set($object, trim($level, '.'), array()); 99 | } 100 | 101 | $this->database->table($this->config['table'])->insert(array('key' => $baseKey, 'value' => json_encode($object))); 102 | 103 | // Add to cache 104 | $this->cache->add($baseKey, $object); 105 | } 106 | else 107 | { 108 | $this->database->table($this->config['table'])->insert(array('key' => $baseKey, 'value' => json_encode($value))); 109 | 110 | // Add to cache 111 | $this->cache->add($baseKey, $value); 112 | } 113 | 114 | return true; 115 | } 116 | 117 | /** 118 | * Overwrite existing value from registry 119 | * 120 | * @param string $key 121 | * @param mixed $value 122 | * @throws Exception 123 | * @return bool 124 | */ 125 | public function overwrite($key, $value) 126 | { 127 | list($baseKey, $searchKey) = $this->fetchKey($key); 128 | 129 | $registry = $this->get($baseKey); 130 | 131 | if ($registry === null) { 132 | $registry = $this->cache->get($baseKey); 133 | } 134 | 135 | if (is_null($registry)) { 136 | throw new Exception("Item [$key] does not exists"); 137 | } 138 | 139 | if ($baseKey != $searchKey) 140 | { 141 | array_set($registry, $searchKey, $value); 142 | $this->database->table($this->config['table'])->where('key', '=', $baseKey)->update(array('value' => json_encode($registry))); 143 | 144 | $this->cache->add($baseKey, $registry); 145 | } 146 | else 147 | { 148 | $this->database->table($this->config['table'])->where('key', '=', $baseKey)->update(array('value' => json_encode($value))); 149 | 150 | $this->cache->add($baseKey, $value); 151 | } 152 | 153 | //$this->cache->forever('torann.registry', $this->cache_storage); 154 | 155 | return true; 156 | } 157 | 158 | /** 159 | * Store an array 160 | * 161 | * @param array $values 162 | * @return bool 163 | */ 164 | public function store(array $values) 165 | { 166 | foreach ($values as $key=>$value) 167 | { 168 | // Ensure proper type 169 | $value = $this->forceTypes($value); 170 | 171 | // Json to save 172 | $jsonValue = json_encode($value); 173 | 174 | // Update 175 | $this->database->statement("INSERT INTO system_registries ( `key`, `value` ) VALUES ( ?, ? ) 176 | ON DUPLICATE KEY UPDATE `key` = ?, `value` = ?", 177 | array($key, $jsonValue, $key, $jsonValue)); 178 | 179 | $this->cache->add($key, $value); 180 | } 181 | 182 | //$this->cache->forever('torann.registry', $this->cache_storage); 183 | 184 | return true; 185 | } 186 | 187 | /** 188 | * Remove existing value from registry 189 | * 190 | * @param string $key 191 | * @throws Exception 192 | * @return bool 193 | * @throws Exception 194 | */ 195 | public function forget($key) 196 | { 197 | list($baseKey, $searchKey) = $this->fetchKey($key); 198 | 199 | $registry = $this->get($baseKey); 200 | 201 | if ($registry === null) { 202 | $registry = $this->cache->get($baseKey); 203 | } 204 | 205 | if (is_null($registry)) { 206 | throw new Exception("Item [$key] does not exists"); 207 | } 208 | 209 | if ($baseKey !== $searchKey) 210 | { 211 | array_forget($registry, $searchKey); 212 | $this->database->table($this->config['table'])->where('key', '=', $baseKey)->update(array('value' => json_encode($registry))); 213 | 214 | // Update cache 215 | $this->cache->add($baseKey, $registry); 216 | } 217 | else 218 | { 219 | $this->database->table($this->config['table'])->where('key', '=', $baseKey)->delete(); 220 | 221 | // Remove from cache 222 | $this->cache->remove($baseKey); 223 | } 224 | 225 | //$this->cache->forever('torann.registry', $this->cache_storage); 226 | 227 | return true; 228 | } 229 | 230 | /** 231 | * Clear registry 232 | * 233 | * @return bool 234 | */ 235 | public function flush() 236 | { 237 | $this->cache->flush(); 238 | 239 | return $this->database->table($this->config['table'])->truncate(); 240 | } 241 | 242 | /** 243 | * Fetch all values 244 | * 245 | * @param mixed $default 246 | * @return mixed 247 | */ 248 | public function all($default = array()) 249 | { 250 | return $this->cache->all($default); 251 | } 252 | 253 | /** 254 | * Cast values to native PHP variable types. 255 | * 256 | * @param mixed $data 257 | * @return mixed 258 | */ 259 | protected function forceTypes($data) 260 | { 261 | if (in_array($data, array('true', 'false'))) 262 | { 263 | $data = ($data === 'true' ? 1 : 0); 264 | } 265 | else if (is_numeric($data)) 266 | { 267 | $data = (int) $data; 268 | } 269 | else if (gettype($data) === 'array') 270 | { 271 | foreach($data as $key=>$value) 272 | { 273 | $data[$key] = $this->forceTypes($value); 274 | } 275 | } 276 | 277 | return $data; 278 | } 279 | 280 | /** 281 | * Get registry key 282 | * 283 | * @param string $key 284 | * @return array 285 | */ 286 | protected function fetchKey($key) 287 | { 288 | if (str_contains($key, '.')) 289 | { 290 | $keys = explode('.', $key); 291 | $search = array_except($keys, 0); 292 | 293 | return array(array_get($keys, 0), implode('.', $search)); 294 | } 295 | 296 | return array($key, null); 297 | } 298 | 299 | /** 300 | * Get key value 301 | * 302 | * @param string $key 303 | * @param string $searchKey 304 | * @return mixed 305 | */ 306 | protected function fetchValue($key, $searchKey = null) 307 | { 308 | $object = $this->cache->get($key); 309 | 310 | if (is_null($object)) { 311 | return null; 312 | } 313 | 314 | return $searchKey ? array_get($object, $searchKey, null) : $object; 315 | } 316 | 317 | /** 318 | * Set cache 319 | * 320 | * @return array 321 | */ 322 | protected function setCache() 323 | { 324 | // Check if cache has expired 325 | if ($this->cache->expired() === false) { 326 | return; 327 | } 328 | 329 | // Instantiate values 330 | $values = array(); 331 | 332 | // Get values from database 333 | foreach($this->database->table($this->config['table'])->get() as $setting) 334 | { 335 | $values[$setting->key] = json_decode($setting->value, true); 336 | } 337 | 338 | // Cache values 339 | $this->cache->set($values); 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /src/Torann/Registry/RegistryServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 23 | __DIR__.'/../../config/registry.php' => config_path('registry.php'), 24 | ]); 25 | 26 | $this->publishes([ 27 | __DIR__ . '/../../migrations/' => base_path('/database/migrations') 28 | ], 'migrations'); 29 | } 30 | 31 | /** 32 | * Register the service provider. 33 | * 34 | * @return void 35 | */ 36 | public function register() 37 | { 38 | $this->registerCache(); 39 | $this->registerRegistry(); 40 | } 41 | 42 | /** 43 | * Register the collection repository. 44 | * 45 | * @return void 46 | */ 47 | protected function registerRegistry() 48 | { 49 | $this->app['registry'] = $this->app->share(function($app) 50 | { 51 | $config = $app->config->get('registry', array()); 52 | 53 | return new Registry($app['db'], $app['registry.cache'], $config); 54 | }); 55 | } 56 | 57 | /** 58 | * Register the collection repository. 59 | * 60 | * @return void 61 | */ 62 | protected function registerCache() 63 | { 64 | $this->app['registry.cache'] = $this->app->share(function($app) 65 | { 66 | $meta = $app->config->get('registry.cache_path'); 67 | $timestampManager = $app->config->get('registry.timestamp_manager'); 68 | return new Cache($meta, $timestampManager); 69 | }); 70 | } 71 | 72 | /** 73 | * Get the services provided by the provider. 74 | * 75 | * @return array 76 | */ 77 | public function provides() 78 | { 79 | return array('registry'); 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /src/Torann/Registry/Timestamps/Redis.php: -------------------------------------------------------------------------------- 1 | $cached_at); 18 | } 19 | 20 | /** 21 | * Update timestamp. 22 | * 23 | * @param string $cached_at 24 | * @return bool 25 | */ 26 | public function update($cached_at) 27 | { 28 | return Timestamp::set('registry_updated_at', $cached_at); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Torann/Registry/Timestamps/TimestampInterface.php: -------------------------------------------------------------------------------- 1 | 'system_registries', 15 | 16 | /* 17 | |-------------------------------------------------------------------------- 18 | | Cache timestamp 19 | |-------------------------------------------------------------------------- 20 | | 21 | | Used for multi-instance web servers. This can be used to ensure 22 | | the registry for all instances are kept up to date. 23 | | 24 | | For Redis: \\Torann\\Registry\\Timestamps\\Redis 25 | | 26 | */ 27 | 28 | 'timestamp_manager' => '', 29 | 30 | /* 31 | |-------------------------------------------------------------------------- 32 | | Cache file path 33 | |-------------------------------------------------------------------------- 34 | | 35 | | Where to store the json cache file 36 | | 37 | */ 38 | 39 | 'cache_path' => storage_path().'/app', 40 | ); 41 | -------------------------------------------------------------------------------- /src/migrations/2015_02_15_185537_create_system_registries_table.php: -------------------------------------------------------------------------------- 1 | string('key'); 18 | $table->text('value'); 19 | 20 | $table->primary('key'); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::drop(\Config::get('registry.table', 'system_registries')); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Torann/laravel-registry/dbefcade2b9da327a88c41c1695cae7acf3da478/tests/.gitkeep --------------------------------------------------------------------------------