From 6f5789674eecb5c6e45c31d06790fba1d392bb61 Mon Sep 17 00:00:00 2001 From: Lorand Gombos Date: Tue, 17 Sep 2019 16:00:37 +0300 Subject: [PATCH] Redis Based Settings (#24) Redis based settings --- .scrutinizer.yml | 10 +- .travis.yml | 6 +- CHANGELOG.md | 5 +- README.md | 14 +- composer.json | 5 +- phpunit.xml.dist | 2 +- src/Managers/RedisSettingsManager.php | 52 +++++++ src/Traits/HasSettingsRedis.php | 48 ++++++ tests/ConsoleCommandTest.php | 2 +- tests/FieldSettingsManagerTest.php | 2 +- tests/Models/UserWithRedis.php | 19 +++ tests/RedisSettingsManagerTest.php | 215 ++++++++++++++++++++++++++ tests/TableSettingsManagerTest.php | 4 +- tests/TestCase.php | 9 +- 14 files changed, 374 insertions(+), 19 deletions(-) create mode 100644 src/Managers/RedisSettingsManager.php create mode 100644 src/Traits/HasSettingsRedis.php create mode 100644 tests/Models/UserWithRedis.php create mode 100644 tests/RedisSettingsManagerTest.php diff --git a/.scrutinizer.yml b/.scrutinizer.yml index f6f9a3d..7559a5a 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -6,10 +6,6 @@ build: - php-scrutinizer-run - command: phpcs-run use_website_config: true - - command: './vendor/bin/phpunit --coverage-clover=coverage.xml' - coverage: - file: coverage.xml - format: clover filter: excluded_paths: - 'tests/*' @@ -35,4 +31,8 @@ coding_style: around_operators: concatenation: true other: - after_type_cast: false \ No newline at end of file + after_type_cast: false +tools: + external_code_coverage: + timeout: 600 + runs: 3 diff --git a/.travis.yml b/.travis.yml index b82ea56..e7df03d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,4 +10,8 @@ before_script: - composer install script: - - vendor/bin/phpunit --coverage-clover=coverage.xml \ No newline at end of file + - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover + +after_script: + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/CHANGELOG.md b/CHANGELOG.md index 62331c0..a829323 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,10 @@ All notable changes to `glorand/laravel-model-settings` will be documented in this file -## 3.3.1 - 2019-09-04 + +## 3.4.1 - 2019-09-17 ### Added -- Laravel 6 support +- Redis support ## 3.3.0 - 2019-08-29 ### Added diff --git a/README.md b/README.md index 811ffb9..1fdaf87 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Bug reports, feature requests, and pull requests can be submitted by following o - [Updating your Eloquent Models](#update_models) - [Option 1 - `HasSettingsField` trait](#update_models_1) - [Option 2 - `HasSettingsTable` trait](#update_models_2) + - [Option 3 - `HasSettingsRedis` trait](#update_models_3) - [Default Settings](#default_settings) - [Usage](#usage) - [Get all model's settings](#get_all) @@ -113,7 +114,17 @@ class User extends Model } ``` -##Default settings +#### Option 3 - `HasSettingsRedis` trait +```php +use Glorand\Model\Settings\Traits\HasSettingsRedis; + +class User extends Model +{ + use HasSettingsRedis; +} +``` + +## Default settings ```php use Glorand\Model\Settings\Traits\HasSettingsTable; @@ -211,3 +222,4 @@ The MIT License (MIT). Please see [LICENSE](LICENSE) for more information. ## Related Stuff - [LaraNews - Laravel Model Settings](https://laravel-news.com/laravel-model-settings) +- [made with Laravel - Laravel Model Settings](https://madewithlaravel.com/laravel-model-settings) diff --git a/composer.json b/composer.json index 41a8662..e8e78e2 100644 --- a/composer.json +++ b/composer.json @@ -22,13 +22,14 @@ "illuminate/database": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0", "illuminate/support": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0", "illuminate/console": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0", - "illuminate/filesystem": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0" + "illuminate/filesystem": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0", + "ext-json": "*" }, "require-dev": { "phpunit/phpunit": "^7.5|^8.0", "orchestra/testbench": "~3.8.0|^4.0", "friendsofphp/php-cs-fixer": "^2.16@dev", - "predis/predis": "^1.1" + "josiasmontag/laravel-redis-mock": "^1.2" }, "suggest": { "predis/predis": "Required to use settings with Redis" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 2203059..7a09fa5 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -23,6 +23,6 @@ --> - + diff --git a/src/Managers/RedisSettingsManager.php b/src/Managers/RedisSettingsManager.php new file mode 100644 index 0000000..126e411 --- /dev/null +++ b/src/Managers/RedisSettingsManager.php @@ -0,0 +1,52 @@ +model->cacheKey(), json_encode($settings)); + + return $this; + } + + public function set(string $path, $value): SettingsManagerContract + { + $settings = $this->all(); + Arr::set($settings, $path, $value); + + return $this->apply($settings); + } + + /** + * Delete an item by its unique path. + * + * @param string|null $path + * @return \Glorand\Model\Settings\Contracts\SettingsManagerContract + */ + public function delete(string $path = null): SettingsManagerContract + { + { + if (!$path) { + $settings = []; + } else { + $settings = $this->all(); + Arr::forget($settings, $path); + } + + $this->apply($settings); + + return $this; + } + } +} diff --git a/src/Traits/HasSettingsRedis.php b/src/Traits/HasSettingsRedis.php new file mode 100644 index 0000000..0fd9cb1 --- /dev/null +++ b/src/Traits/HasSettingsRedis.php @@ -0,0 +1,48 @@ +cacheKey()); + + return Arr::wrap(json_decode($redisValue, true)); + } + + public function cacheKey(string $key = null): string + { + return sprintf( + "r-k-%s:%s:%s", + $this->getTable(), + $this->getKey(), + $this->updated_at->timestamp + ) . $key; + } + + abstract public function getTable(); + + abstract public function getKey(); +} diff --git a/tests/ConsoleCommandTest.php b/tests/ConsoleCommandTest.php index f3e3fdc..3e00a37 100644 --- a/tests/ConsoleCommandTest.php +++ b/tests/ConsoleCommandTest.php @@ -12,7 +12,7 @@ public function testModelSettingsTableCommand() config(['model_settings.settings_table_name' => 'custom_settings_table']); $this->artisan('model-settings:model-settings-table') ->assertExitCode(0); - + config(['model_settings.settings_table_name' => null]); $this->artisan('model-settings:model-settings-table') ->assertExitCode(0); diff --git a/tests/FieldSettingsManagerTest.php b/tests/FieldSettingsManagerTest.php index 824b031..5448313 100644 --- a/tests/FieldSettingsManagerTest.php +++ b/tests/FieldSettingsManagerTest.php @@ -18,7 +18,7 @@ class FieldSettingsManagerTest extends TestCase 'email' => "john@doe.com", ], ]; - /** @var array */ + /** @var array */ protected $defaultSettingsTestArray = [ 'project' => 'Main Project', ]; diff --git a/tests/Models/UserWithRedis.php b/tests/Models/UserWithRedis.php new file mode 100644 index 0000000..8f4335f --- /dev/null +++ b/tests/Models/UserWithRedis.php @@ -0,0 +1,19 @@ + [ + 'first_name' => "John", + 'last_name' => "Doe", + 'email' => "john@doe.com", + ], + ]; + /** @var array */ + protected $defaultSettingsTestArray = [ + 'project' => 'Main Project', + ]; + + public function setUp(): void + { + parent::setUp(); + $this->model = User::first(); + } + + public function testInit() + { + $traits = class_uses($this->model); + $this->assertTrue(array_key_exists(HasSettingsRedis::class, $traits)); + + $this->assertInstanceOf(MockPredisConnection::class, Redis::connection()); + } + + /** + * @throws \Glorand\Model\Settings\Exceptions\ModelSettingsException + */ + public function testAll() + { + $this->assertEquals($this->model->settings()->all(), []); + } + + /** + * @throws \Glorand\Model\Settings\Exceptions\ModelSettingsException + */ + public function testDefaultValue() + { + $this->model->defaultSettings = $this->defaultSettingsTestArray; + $this->assertEquals($this->defaultSettingsTestArray, $this->model->settings()->all()); + + $this->model->settings()->apply($this->testArray); + $this->assertEquals( + $this->model->settings()->all(), + array_merge($this->defaultSettingsTestArray, $this->testArray) + ); + } + + /** + * @throws \Exception + */ + public function testHas() + { + $this->model->settings()->apply($this->testArray); + $this->assertEquals($this->model->settings()->all(), $this->testArray); + + $this->assertTrue($this->model->settings()->has('user.first_name')); + $this->assertFalse($this->model->settings()->has('user.age')); + } + + /** + * @throws \Exception + */ + public function testGet() + { + $this->model->settings()->clear(); + $this->assertEquals($this->model->settings()->all(), []); + $this->assertEquals($this->model->settings()->get('user'), null); + $this->model->settings()->apply($this->testArray); + $this->assertEquals($this->model->settings()->get('user.first_name'), 'John'); + } + + /** + * @throws \Exception + */ + public function testGetMultiple() + { + $this->model->settings()->clear(); + $this->assertEquals($this->model->settings()->all(), []); + $values = $this->model->settings()->getMultiple(['user.first_name', 'user.last_name'], 'def_val'); + $this->assertEquals( + $values, + [ + 'user.first_name' => 'def_val', + 'user.last_name' => 'def_val', + ] + ); + + $this->model->settings()->apply($this->testArray); + $values = $this->model->settings()->getMultiple( + ['user.first_name', 'user.last_name', 'user.middle_name'], + 'def_val' + ); + $this->assertEquals( + $values, + [ + 'user.first_name' => 'John', + 'user.last_name' => 'Doe', + 'user.middle_name' => 'def_val', + ] + ); + } + + /** + * @throws \Exception + */ + public function testApply() + { + $this->model->settings()->apply($this->testArray); + $this->assertEquals($this->model->fresh()->settings()->all(), $this->testArray); + } + + /** + * @throws \Glorand\Model\Settings\Exceptions\ModelSettingsException + */ + public function testDelete() + { + $this->model->settings()->apply($this->testArray); + + $this->assertEquals($this->model->settings()->all(), $this->testArray); + $this->assertEquals($this->model->settings()->get('user.first_name'), 'John'); + + $this->model->settings()->delete('user.first_name'); + $this->assertEquals($this->model->settings()->get('user.first_name'), null); + + $this->model->settings()->delete(); + $this->assertEquals($this->model->settings()->all(), []); + } + + /** + * @throws \Glorand\Model\Settings\Exceptions\ModelSettingsException + */ + public function testDeleteMultiple() + { + $this->model->settings()->apply($this->testArray); + $this->assertEquals($this->model->settings()->all(), $this->testArray); + + $this->model->settings()->deleteMultiple(['user.first_name', 'user.last_name']); + $testData = $this->model->settings()->get('user'); + $this->assertArrayNotHasKey('first_name', $testData); + $this->assertArrayNotHasKey('last_name', $testData); + $this->assertArrayHasKey('email', $testData); + } + + /** + * @throws \Glorand\Model\Settings\Exceptions\ModelSettingsException + */ + public function testClear() + { + $this->model->settings()->apply($this->testArray); + $this->assertEquals($this->model->settings()->all(), $this->testArray); + + $this->model->settings()->clear(); + $this->assertEquals($this->model->settings()->all(), []); + } + + /** + * @throws \Exception + */ + public function testSet() + { + $this->assertEquals($this->model->settings()->all(), []); + + $this->model->settings()->set('user.age', 18); + $this->assertEquals($this->model->settings()->all(), ['user' => ['age' => 18]]); + } + + /** + * @throws \Glorand\Model\Settings\Exceptions\ModelSettingsException + */ + public function testSetMultiple() + { + $this->model->settings()->clear(); + $this->assertEquals($this->model->settings()->all(), []); + $testData = [ + 'a' => 'a', + 'b' => 'b', + ]; + $this->model->settings()->setMultiple($testData); + $this->assertEquals($this->model->settings()->all(), $testData); + + $this->model->settings()->setMultiple($this->testArray); + $this->assertEquals($this->model->settings()->all(), array_merge($testData, $this->testArray)); + } + + /** + * @throws \Exception + */ + public function testUpdate() + { + $this->model->settings()->clear(); + $this->assertEquals($this->model->settings()->all(), []); + + $this->model->settings()->set('user.age', 18); + $this->assertEquals($this->model->settings()->all(), ['user' => ['age' => 18]]); + + $this->model->settings()->update('user.age', 19); + $this->assertEquals($this->model->settings()->all(), ['user' => ['age' => 19]]); + } +} diff --git a/tests/TableSettingsManagerTest.php b/tests/TableSettingsManagerTest.php index ac39e06..bedf90b 100644 --- a/tests/TableSettingsManagerTest.php +++ b/tests/TableSettingsManagerTest.php @@ -6,8 +6,6 @@ use Glorand\Model\Settings\Tests\Models\UsersWithDefaultSettingsTable; use Glorand\Model\Settings\Tests\Models\UsersWithTable as User; use Glorand\Model\Settings\Traits\HasSettingsTable; -use Illuminate\Database\Eloquent\Relations\MorphOne; -use Illuminate\Support\Facades\DB; class TableSettingsManagerTest extends TestCase { @@ -21,7 +19,7 @@ class TableSettingsManagerTest extends TestCase 'email' => "john@doe.com", ], ]; - /** @var array */ + /** @var array */ protected $defaultSettingsTestArray = [ 'project' => 'Main Project', ]; diff --git a/tests/TestCase.php b/tests/TestCase.php index 2b3499b..1bb7e97 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -8,10 +8,12 @@ use Glorand\Model\Settings\Tests\Models\User; use Glorand\Model\Settings\Tests\Models\UsersWithTable; use Glorand\Model\Settings\Tests\Models\UserWithField; +use Glorand\Model\Settings\Tests\Models\UserWithRedis; use Glorand\Model\Settings\Tests\Models\WrongUser; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Schema; +use Lunaweb\RedisMock\Providers\RedisMockServiceProvider; use Orchestra\Testbench\TestCase as OrchestraTestCase; abstract class TestCase extends OrchestraTestCase @@ -43,14 +45,17 @@ protected function getPackageProviders($app) public function getEnvironmentSetUp($app) { $app['config']->set('auth.providers.users.model', UserWithField::class); + $app['config']->set('database.redis.client', 'mock'); + $app['config']->set('database.default', 'testing'); + $app->register(RedisMockServiceProvider::class); } protected function setUpDatabase() { $this->createSettingsTable(); - $this->createTables('users_with_table', 'users_with_field', 'wrong_users'); - $this->seedModels(UserWithField::class, UsersWithTable::class, WrongUser::class); + $this->createTables('users', 'users_with_table', 'users_with_field', 'wrong_users'); + $this->seedModels(UserWithField::class, UsersWithTable::class, WrongUser::class, UserWithRedis::class); } protected function createSettingsTable()