Dcat Admin 自定義 Form 表單—將配置內容快取到 Redis 並統一呼叫

laravel_peng發表於2022-05-13

上一篇主要講系統配置 Form 表單如何自定義,以及配置的 Form 表單擴充套件的問題。雖然功能已經滿足使用,但也有其弊端:

  1. 系統配置是存在資料庫中,且配置內容改動次數較少,每次獲取仍需要查庫,在大量用到配置的場景下影響效能。
  2. 沒有統一的獲取配置資料的介面,後期維護麻煩。

針對以問題我們的對策是

  1. 將配置快取起來,使用 Redis 快取。(當然用檔案快取也是 ok 的,但 Redis 應用的場景更多!)
  2. 統一系統配置呼叫的介面。

首先看一下配置表的遷移檔案和資料

  • 很簡單,除了主鍵外,只剩下 name 和 value 兩個有效欄位。

    <?php
    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;
    class CreateSystemConfigsTable extends Migration
    {
      public function up()
      {
          Schema::create('system_configs', function (Blueprint $table) {
              $table->id();
              $table->string('name');
              $table->text('value');
              $table->timestamps();
          });
      }
      public function down()
      {
          Schema::dropIfExists('system_configs');
      }
    }
  • 資料庫表存入的記錄(現在只有兩行記錄,不過可以通過擴充套件 Tab 欄增加系統配置,就能增加表記錄行數)
    Dcat Admin 自定義 Form 表單—將配置內容快取到 Redis 並統一呼叫

  • 看第一行 name = config_one 資料的 value 欄位內容以及格式(json 格式)。
    Dcat Admin 自定義 Form 表單—將配置內容快取到 Redis 並統一呼叫

然後是 Redis 的配置

  • .env 檔案的配置
    REDIS_CLIENT=phpredis
    REDIS_HOST=127.0.0.1
    REDIS_PASSWORD=null
    REDIS_PORT=6379
  • databse.php 的配置
    'cache' => [
          'url' => env('REDIS_URL'),
          'host' => env('REDIS_HOST', '127.0.0.1'),
          'password' => env('REDIS_PASSWORD', null),
          'port' => env('REDIS_PORT', '6379'),
          'database' => env('REDIS_CACHE_DB', '1'),
    ],
  • redis 的呼叫方法
    # get 方法:
    Redis::connection('cache')->get($cacheKey);
    # set 方法:
    Redis::connection('cache')->set($cacheKey, json_encode($data));
    # del 方法:
    Redis::connection('cache')->del($cacheKey);

最後是系統配置的統一介面以及呼叫方法

  • 系統配置的統一介面

    <?php
    namespace App\Models;
    use Eloquent;
    use Illuminate\Database\Eloquent\Builder;
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Support\Carbon;
    use Exception;
    use Illuminate\Support\Arr;
    use Illuminate\Support\Facades\Redis;
    class SystemConfig extends Model
    {
      use HasFactory;
      const CACHE_KEY_PREFIX = 'System-Config';
      protected $table = 'system_configs';
      protected $fillable = ['name', 'value'];
    
      // 獲取系統配置
      public static function get($name, $key = null, $default = null)
      {
          if (empty($name)) {
              throw new Exception('配置名為空,請聯絡管理員');
          }
    
          $cacheKey = self::CACHE_KEY_PREFIX;
          $jsonData = Redis::connection('cache')->get($cacheKey);
          $data = json_decode($jsonData, true);
    
          // 如果快取沒有資料
          if (empty($data) || !is_array($data) || !Arr::exists($data, $name)) {
              $data = [];
              $config = self::query()->get()->toArray();
              foreach ($config as $item) {
                  $data[$item['name']] = json_decode($item['value'], true);
              }
              Redis::connection('cache')->set($cacheKey, json_encode($data));
          }
    
          // 如果 name 不存在,直接返回 null
          if (!Arr::exists($data, $name)) {
              return null;
          }
    
          // 獲取 name 對應的配置
          $value = Arr::get($data, $name, $default);
    
          // 如果 key 值不存在,直接返回 name 對應的配置
          if (!$key) {
              return $value;
          }
    
          // 如果 key 值存在,返回 name 對應配置中的 key 鍵對應的值
          return Arr::get(Arr::get($data, $name, $default), $key, $default);
      }
    }
  • 系統配置的呼叫示例:

    // 獲取整條配置資訊
    SystemConfig::get('config_one')// 獲取整條配置資訊中的一個配置項
    SystemConfig::get('config_one', 'email')// 獲取整條配置資訊中的一個配置項,如果沒有返回預設值
    SystemConfig::get('config_one', 'email', 'mail.qq.com')

但是配置仍會改動,所以在必要時清除快取

  • 一般系統配置沒有刪除操作,只有建立和更新,現在只針對更新刪除快取。我沒有用模型監聽事件,我採用的是模型事件的閉包,請參見 模型閉包的使用
      /**
       * 使用模型的閉包刪除快取
       */
      protected static function booted()
      {
          // 資料更新後 - 刪除快取
          static::updated(function ($config) {
              $cacheKey = self::CACHE_KEY_PREFIX;
              Redis::connection('cache')->del($cacheKey);
          });
      }
  • 這樣一來,在模型資料更新的時候,快取是被清理掉的。

結語

  • 好了,本節內容已經講完,也不是很複雜,只用到了 Redis 的 String 資料結構,也沒有什麼高深的方法,簡單的 getsetdel 方法就滿足了使用。
  • 如果有更好的辦法,歡迎在評論區討論。
本作品採用《CC 協議》,轉載必須註明作者和本文連結
Xiao Peng

相關文章