Laravel 中使用 Redis 生成自增主鍵

chuoke發表於2019-05-21

終於,開始使用 Laravel 開發正式專案,雖然看過很多文章,做過一些練習,但是真搞起來,仍然是個丈二。

考慮到國情,專案一開始就考慮分庫分表的事情,資料庫管控的自增主鍵值,不利於將來搞大事,但是又不想使用 uuid,所以要對所有表主鍵值進行統一管理,於是應用 Redis 生成.

主鍵值的填充可以在 Model 事件 creating 的時候進行,這裡有兩種方式,詳情請見下文。

1. 建立主鍵值生成器

獨立出來,在某些情況可以單獨呼叫。

<?php

namespace App\Support\Database;

use Illuminate\Support\Facades\Redis;
use Illuminate\Database\Eloquent\Model;

class PrimaryKeyValueGenerator
{
    /**
     * 自增值
     *
     * @param string $key
     * @return integer
     * @author Chuoke
     */
    public function incrementId($key = 'model-primary-key')
    {
        return Redis::incr($this->formatKey($key));
    }

    /**
     * 給模型例項生成主鍵值
     *
     * @param Model $model
     * @return void
     * @author Chuoke
     */
    public function incrementIdOfModel(Model $model)
    {
        $key = $this->buildKeyOfModel($model);

        return self::incrementId($key);
    }

    /**
     * 給 model 例項生成一個主鍵 key
     *
     * @param Model $model
     * @return string
     * @author Chuoke
     */
    public function buildKeyOfModel(Model $model)
    {
        return implode('_', ['table', $model->getTable(), 'id']);
    }

    /**
     * 格式化 key
     *
     * @param string $key
     * @return string
     * @author Chuoke
     */
    public function formatKey($key)
    {
        return strtoupper($key);
    }
}

2. 主鍵填充

方便起見,把填充方法封成 trait.


<?php

namespace App\Models\Traits;

use App\Support\Facades\PrimaryKeyValueGenerator;

trait MustFillPrimaryKey
{
    /**
     * 啟動填充主鍵值事件
     *
     * @return void
     * @author Chuoke
     */
    public static function bootMustFillPrimaryKeyEvent()
    {
        static::creating(function ($model) {
            $model->fillPrimaryKey();
        });
    }

    /**
     * 填充主鍵值
     *
     * @param \App\Modells\Model $model
     * @return void
     * @author Chuoke
     */
    public function fillPrimaryKey()
    {
        if ($this->needFillPrimaryKey()) {
            $id = PrimaryKeyValueGenerator::incrementIdOfModel($this);
            $this->setAttribute($this->getKeyName(), $id);
        }
    }

    /**
     * 判斷是否需要填充主鍵值
     *
     * @return boolean
     * @author Chuoke
     */
    public function needFillPrimaryKey()
    {
        return empty($this->getKey())
            && $this->getIncrementing()
            && $this->getKeyName() !== false;
    }
}

3. 事件監聽

Model 中的 trait 可以做一個和 trait 名稱一樣的 boot 方法,如:bootbootMustFillPrimaryKey, 這樣在啟動的時候會自動啟動,所以上面有一個這個類似的方法,只是個備份,如果是個別的需要這樣的操作,使用這種方式很方便。

我使用的是全域性事件方式.

先建立一個事件處理


<?php

namespace App\Events;

use Illuminate\Queue\SerializesModels;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;

class FillPrimaryKey
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $model;

    public function handle($e, $payload)
    {
        if (is_array($payload)) {
            foreach ($payload as $model) {
                $this->handle($e, $model);
            }
        } else if ($payload instanceof Model && \method_exists($payload, 'fillPrimaryKey')) {
            $payload->fillPrimaryKey();
        }
    }

}

然後在 App\Providers\EventServiceProvider 中註冊監聽所有的模型建立事件.


    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        'eloquent.creating: *' => [
            FillPrimaryKey::class,
        ],
    ];

這裡有個小坑,調了半天,回眸了若干次原始碼才發現的,就是那個 * 前面有個空格 ?

初出茅廬,一知半解,望有識之士多多指教 ?

初出茅廬,一知半解,望有識之士多多指教。抱拳...

相關文章