Laravel 中使用 Redis 生成自增主键
Laravel
中使用 Redis
生成自增主键
终于,开始使用
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;
}
}
设置 incrementing = false `` 避免执行
insertAndSetId() 导致插入后主键值为
0 `
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 FillModelPrimaryKey
{
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: *' => [
FillModelPrimaryKey::class,
],
];
这里有个小坑,调了半天,回眸了若干次源码才发现,那个 *
前面居然有个空格 ?
4. 无侵入式方案
下面这种方式是全局性的,更灵活,不需要引入 trait
,合理的利用了框架提供的能力,第三方扩展也会使用该方式生成主键,如果某些表有特殊的自定义情况,可以使用上面那种方式,当然也可以结合使用。
<?php
namespace App\Events;
use Illuminate\Queue\SerializesModels;
use Illuminate\Database\Eloquent\Model;
use App\Support\Facades\PrimaryKeyValueGenerator;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
class FillModelPrimaryKey
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
public function handle($e, $payload)
{
if (is_array($payload)) {
foreach ($payload as $model) {
$this->handle($e, $model);
}
} else if ($payload instanceof Model) {
if ($payload->getIncrementing() && empty($payload->getKey())) {
// 关闭主键自增,以避免内部获取自增主键值逻辑(insertAndSetId())导致主键被置空
$payload->setIncrementing(false);
$payload->setAttribute($payload->getKeyName(), $this->generatePk($payload));
}
}
}
/**
* 生成主键值
*
* @return integer
* @author Chuoke
*/
protected function generatePk($model)
{
return PrimaryKeyValueGenerator::getIdOfModel($model);
}
}
总结
无论是自增主键还是普通主键,只要有主键就行。统一生成有利于分库分表,还可以方便做其他特殊化的事情,这只是一个思路,虽然能够运用 Laravel 进行开发,但是还不够了解,有很多需要优化的地方,自定义框架的功能可以对框架进行深入的了解,慢慢进步吧。
初出茅庐,一知半解,望有识之士多多指教 ?
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: