Browse Source

Use Laravel Cache to optimize access to user Settings

Bubka 2 years ago
parent
commit
77eebbd35d
2 changed files with 86 additions and 6 deletions
  1. 31 3
      app/Services/SettingService.php
  2. 55 3
      tests/Feature/Services/SettingServiceTest.php

+ 31 - 3
app/Services/SettingService.php

@@ -7,10 +7,10 @@ use App\Models\Option;
 use Exception;
 use Illuminate\Support\Arr;
 use Illuminate\Support\Collection;
-use Illuminate\Support\Facades\App;
 use Illuminate\Support\Facades\Crypt;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Cache;
 use Throwable;
 
 class SettingService
@@ -22,12 +22,27 @@ class SettingService
      */
     private Collection $settings;
 
+    /**
+     * Cache duration
+     *
+     * @var int $minutes
+     */
+    private int $minutes = 10;
+
+    /**
+     * Name of the cache item where options are persisted
+     */
+    public const CACHE_ITEM_NAME = 'userOptions';
+
     /**
      * Constructor
      */
     public function __construct()
     {
-        self::build();
+        $this->settings = Cache::remember(self::CACHE_ITEM_NAME, now()->addMinutes($this->minutes), function () {
+            self::build();
+            return $this->settings;
+        });
     }
 
     /**
@@ -74,7 +89,7 @@ class SettingService
             Log::info(sprintf('Setting %s is now %s', var_export($setting, true), var_export($this->restoreType($value), true)));
         }
 
-        self::build();
+        self::buildAndCache();
     }
 
     /**
@@ -86,6 +101,8 @@ class SettingService
     {
         Option::where('key', $name)->delete();
         Log::info(sprintf('Setting %s deleted', var_export($name, true)));
+
+        self::buildAndCache();
     }
 
     /**
@@ -121,6 +138,17 @@ class SettingService
         $this->settings = $settings;
     }
 
+    /**
+     * Build and cache the options collection
+     * 
+     * @return void
+     */
+    private function buildAndCache()
+    {
+        self::build();
+        Cache::put(self::CACHE_ITEM_NAME, $this->settings, now()->addMinutes($this->minutes));
+    }
+
     /**
      * Replaces boolean by a patterned string as appstrack/laravel-options package does not support var type
      *

+ 55 - 3
tests/Feature/Services/SettingServiceTest.php

@@ -3,8 +3,10 @@
 namespace Tests\Feature\Services;
 
 use App\Facades\Settings;
+use App\Services\SettingService;
 use App\Models\TwoFAccount;
 use Illuminate\Support\Facades\Crypt;
+use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\DB;
 use Tests\FeatureTestCase;
 
@@ -172,14 +174,17 @@ class SettingServiceTest extends FeatureTestCase
     /**
      * @test
      */
-    public function test_set_setting_persist_correct_value()
+    public function test_set_setting_persist_correct_value_in_db_and_cache()
     {
         $value = Settings::set(self::SETTING_NAME, self::SETTING_VALUE_STRING);
+        $cached = Cache::get(SettingService::CACHE_ITEM_NAME); // returns a Collection
 
         $this->assertDatabaseHas('options', [
             self::KEY   => self::SETTING_NAME,
             self::VALUE => self::SETTING_VALUE_STRING,
         ]);
+
+        $this->assertEquals($cached->get(self::SETTING_NAME), self::SETTING_VALUE_STRING);
     }
 
     /**
@@ -278,6 +283,7 @@ class SettingServiceTest extends FeatureTestCase
             self::SETTING_NAME     => self::SETTING_VALUE_STRING,
             self::SETTING_NAME_ALT => self::SETTING_VALUE_INT,
         ]);
+        $cached = Cache::get(SettingService::CACHE_ITEM_NAME); // returns a Collection
 
         $this->assertDatabaseHas('options', [
             self::KEY   => self::SETTING_NAME,
@@ -288,6 +294,9 @@ class SettingServiceTest extends FeatureTestCase
             self::KEY   => self::SETTING_NAME_ALT,
             self::VALUE => self::SETTING_VALUE_INT,
         ]);
+
+        $this->assertEquals($cached->get(self::SETTING_NAME), self::SETTING_VALUE_STRING);
+        $this->assertEquals($cached->get(self::SETTING_NAME_ALT), self::SETTING_VALUE_INT);
     }
 
     /**
@@ -319,18 +328,20 @@ class SettingServiceTest extends FeatureTestCase
     /**
      * @test
      */
-    public function test_del_remove_setting_from_db()
+    public function test_del_remove_setting_from_db_and_cache()
     {
         DB::table('options')->insert(
             [self::KEY => self::SETTING_NAME, self::VALUE => strval(self::SETTING_VALUE_STRING)]
         );
 
-        $value = Settings::delete(self::SETTING_NAME);
+        Settings::delete(self::SETTING_NAME);
+        $cached = Cache::get(SettingService::CACHE_ITEM_NAME); // returns a Collection
 
         $this->assertDatabaseMissing('options', [
             self::KEY   => self::SETTING_NAME,
             self::VALUE => self::SETTING_VALUE_STRING,
         ]);
+        $this->assertFalse($cached->has(self::SETTING_NAME));
     }
 
     /**
@@ -354,4 +365,45 @@ class SettingServiceTest extends FeatureTestCase
 
         $this->assertFalse(Settings::isUserDefined('showTokenAsDot'));
     }
+
+    /**
+     * @test
+     */
+    public function test_cache_is_requested_at_instanciation()
+    {
+        Cache::shouldReceive('remember')
+                    ->andReturn(collect([]));
+
+        $settingService = new SettingService();
+
+        Cache::shouldHaveReceived('remember');
+    }
+
+    /**
+     * 
+     */
+    public function test_cache_is_updated_when_setting_is_set()
+    {
+        Cache::shouldReceive('remember', 'put')
+                    ->andReturn(collect([]), true);
+
+        $settingService = new SettingService();
+        $settingService->set(self::SETTING_NAME, self::SETTING_VALUE_STRING);
+
+        Cache::shouldHaveReceived('put');
+    }
+
+    /**
+     * 
+     */
+    public function test_cache_is_updated_when_setting_is_deleted()
+    {
+        Cache::shouldReceive('remember', 'put')
+                    ->andReturn(collect([]), true);
+
+        $settingService = new SettingService();
+        $settingService->delete(self::SETTING_NAME);
+
+        Cache::shouldHaveReceived('put');
+    }
 }