Laravel 数据表按月份水平分表,表不存在,自动创建生成

说明:创建表的时候需要进行判断,如果表存在,则不需要创建。这个代码会被多次使用并可以重复使用,选择写成trait,实现分表后的增删改查操作和分表前一样,但默认是对当前月份进行增删改查。

trait:

<?php

namespace App\Models\Traits;

use Carbon\Carbon;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

trait SplitTableTrait
{
    /**
     * 是否分表,默认false,即不分表
     * @var bool
     */
    protected $isSplitTable = true;

    /**
     * 最终生成表
     * @var
     */
    protected $endTable;

    /**
     * 后缀参数
     * @var null
     */
    protected $suffix = null;

    /**
     * 初始化
     * @param  array  $attributes
     * @param $suffix
     * @return void
     */
    public function init(array $attributes = [], $suffix = null)
    {
        $this->endTable = $this->table;

        // isSplitTable参数为true时进行分表,否则不分表
        if ($this->isSplitTable) {
            // 初始化后缀,未传则默认年月分表
            $this->suffix = $suffix ?: Carbon::now()->format('Ym');
        }
        //初始化分表表名并创建
        $this->setSuffix($suffix);
    }

    /**
     * 设置表后缀, 如果设置分表后缀,可在service层调用生成自定义后缀表名,
     * 但每次操作表之前都需要调用该方法以保证数据表的准确性
     * @param $suffix
     * @return void
     */
    public function setSuffix($suffix = null)
    {
        // isSplitTable参数为true时进行分表,否则不分表
        if ($this->isSplitTable) {
            //初始化后缀,未传则默认年月分表
            $this->suffix = $suffix ?: Carbon::now()->format('Ym');
        }

        if ($this->suffix !== null) {
            // 最终表替换模型中声明的表作为分表使用的表
            $this->table = $this->endTable.'_'.$this->suffix;
        }

        // 调用时,创建分表,格式为 table_{$suffix}
        // 未传自定义后缀情况下,,默认按年月分表格式为:orders_202205
        // 无论使用时是否自定义分表名,都会创建默认的分表,除非关闭该调用
        $this->createTable();
    }

    /**
     * 提供一个静态方法设置表后缀
     * @param $suffix
     * @return mixed
     */
    public static function suffix($suffix = null)
    {
        $instance = new static;
        $instance->setSuffix($suffix);

        return $instance->newQuery();
    }

    /**
     * 创建新的"table_{$suffix}"的模型实例并返回
     * @param  array  $attributes
     * @return object $model
     */
    public function newInstance($attributes = [], $exists = false): object
    {
        $model = parent::newInstance($attributes, $exists);
        $model->setSuffix($this->suffix);

        return $model;
    }

    /**
     * 创建分表,没有则创建,有则不处理
     * @return void
     */
    protected function createTable()
    {
        $connectName = $this->getConnectionName();

        // 初始化分表,,按年月分表格式为:orders_202205
        if (!Schema::connection($connectName)->hasTable($this->table)) {
            Schema::connection($connectName)->create($this->table, function (Blueprint $table) {
                $table->id();
                $table->dateTime('pay_at')->comment('交易时间');
                $table->string('appid')->comment('公众账号ID');
                $table->string('mch_id')->comment('商户号');
                $table->string('sub_mch_id')->nullable()->comment('特约商户号');
                $table->string('device_info')->nullable()->comment('设备号');
                $table->string('transaction_id')->comment('微信订单号');
                $table->string('out_trade_no')->comment('商户订单号');
                $table->string('openid')->comment('用户标识');
                $table->string('trade_type')->comment('交易类型');
                $table->string('pay_status')->comment('交易状态');
                $table->string('bank_type')->comment('付款银行');
                $table->string('fee_type')->comment('货币种类');
                $table->decimal('settlement_total_fee', 10, 2)->comment('应结订单金额');
                $table->decimal('coupon_fee', 10, 2)->comment('代金券金额');
                $table->string('refund_id')->comment('微信退款单号');
                $table->string('out_refund_no')->comment('商户退款单号');
                $table->decimal('refund_fee', 10, 2)->comment('退款金额');
                $table->decimal('coupon_refund_fee', 10, 2)->comment('充值券退款金额');
                $table->string('refund_type')->nullable()->comment('退款类型');
                $table->string('refund_status')->nullable()->comment('退款状态');
                $table->string('body')->comment('商品名称');
                $table->string('attach', 1000)->nullable()->comment('商户数据包');
                $table->decimal('service_charge', 10, 2)->comment('手续费');
                $table->string('rate')->comment('费率');
                $table->decimal('total_fee', 10, 2)->comment('订单金额');
                $table->decimal('apply_refund_fee', 10, 2)->comment('申请退款金额');
                $table->string('rate_remark')->nullable()->comment('费率备注');
                $table->timestamps();
            });
        }
    }
}

模型使用trait:(任何模型都可使用这个trait进行按月分表)

<?php

namespace App\Models;

use App\Models\Traits\SplitTableTrait;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class WechatBill extends Model
{
    use HasFactory;

    use SplitTableTrait;

    protected $table = 'wechat_bill';

    protected $guarded = [];

    public function __construct(array $attributes = [], $suffix = null)
    {
        // 初始化分表处理
        $this->init($attributes, $suffix);

        parent::__construct($attributes);
    }

}

效果:

Laravel  数据表按月份水平分表,表不存在,自动创建生成

分表查询示例

$wechatBill = new WechatBill();
$wechatBill->setSuffix(202303);
return $wechatBill->newQuery()->get();

分表写入

  return (new WechatBill([], 202303))->newInstance()->create([]);
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 5

建表这种操作,最好还是放到任务计划里面去,提前创建。这样直接在查询中,如果遇到刚好有事务,DDL 操作直接把事务给你自动提交了,就麻烦了。

1年前 评论
gyp719 (楼主) 1年前
gyp719 (楼主) 1年前
sanders

如果查询条件里面没有日期,或查整年的数据,想听听你是如何处理的。

1年前 评论
gyp719 (楼主) 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!