Laravel 中解决分表问题
在开发中发现 composer 的分表扩展,packages 中的并不好用,以下是我在x项目中的一个分表 实践
<?phpnamespace 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_202101if (!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();}}
实际使用场景
<?phpnamespace 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();}}