在开发中发现 composer 的分表扩展,packages 中的并不好用,以下是我在x项目中的一个分表 实践
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
trait SplitTableTrait
{
//是否分表,默认false,即不分表
//protected $isSplitTable = false;
//是否自动创建分表
//protected bool $isAutoCreate = false;
//表
public string $endTable;
private string $originTable;
private string $ym;
private string $suffix;
public function init($suffix = null): void
{
//默认原表
$this->originTable = $this->table;
//默认最终表
$this->endTable = $this->table;
$this->ym = Carbon::now()->format('Ym');
//isSplitTable参数为true时进行分表,否则不分表
if ($this->isSplitTable) {
//初始化后缀,未传则默认年月分表
$this->suffix = $suffix ?: $this->ym;
}
//初始化分表表名并创建
$this->setSuffix();
}
/**
* 设置表后缀, 如果设置分表后缀,可在service层调用生成自定义后缀表名,
* 但每次操作表之前都需要调用该方法以保证数据表的准确性
* @param $suffix
*/
public function setSuffix($suffix = null): void
{
//isSplitTable参数为true时进行分表,否则不分表
if ($this->isSplitTable) {
//初始化后缀,未传则默认年月分表
$this->suffix = $suffix ?: $this->ym;
}
$this->endTable = $this->originTable . '_' . $this->suffix;
//调用时,创建分表,格式为 table_{$suffix}
//未传自定义后缀情况下,,默认按年月分表格式为:b_log_202101
//无论使用时是否自定义分表名,都会创建默认的分表,除非关闭该调用
$this->createTable();
}
/**
* 提供一个静态方法设置表后缀
* @param string $suffix
* @return Builder
*/
public static function suffix(string $suffix): Builder
{
$instance = new static;
$instance->setSuffix($suffix);
return $instance->newQuery();
}
/**
* 创建新的"table_{$suffix}"的模型实例并返回
* @param array $attributes
* @param bool $exists
*/
/**
* 创建分表,没有则创建,有则不处理
*/
protected function createTable(): void
{
//初始化分表,,按年月分表格式为:b_log_202101
if (!Schema::hasTable($this->endTable)) {
DB::update("create table $this->endTable like $this->table");
}
$this->table = $this->endTable;
}
/*
* 排序字段
*/
protected string $orderByField;
/**
* 排序类型,asc:正序,desc:倒序,默认倒序
* @var string
*/
protected string $orderBy = 'desc';
/**
* 执行union all对分表的最终扥分页查询
* @param $queries
* @param int $limit
* @return array
*/
public function dealListByUnionAllQuery($queries, int $limit = 10): array
{
//弹出一张表作为union的开始
$unionQuery = $queries->shift();
//循环剩下的表添加union
$queries->each(function ($item) use ($unionQuery) {
$unionQuery->unionAll($item);
});
//设置临时表的名称,添加临时表,顺序不能反过来,否则用关联约束会找不到表
$endQuery =
DB::table(DB::raw("({$unionQuery->toSql()}) as union_" . $this->originTable))
//合并查询条件
->mergeBindings($unionQuery);
if ($this->orderByField) {
$endQuery->orderBy($this->orderByField, $this->orderBy);
}
return $endQuery
//分页
->paginate($limit)
->toArray();
}
}
实际使用场景
<?php
namespace App\Models\Scrm;
use App\Models\BaseModel;
use App\Traits\SplitTableTrait;
use DateTimeInterface;
class MessageLogModel extends BaseModel
{
use SplitTableTrait;
protected $table = 'message_log';
protected $casts = [
'message' => 'array',
];
protected bool $isSplitTable = true;
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
// 初始化分表处理
$this->init();
}
}