如何优雅的处理表结构变更导致的数据拆分需求

业务需求

  • 1.0版系统的微信模块只有一个用户表 wechat_users ,里面包含了微信用户信息,主要字段是 mini_open_id, mp_open_id, user_name 。
  • 随着业务需求的更新与升级,微信公众号与小程序的一对一关系已经满足不了。需要升级为一对多的关系了,也就是一个公众号信息对应多个小程序。
  • 所以现在需要新增一个小程序用户表 mini_program_users ,包含小程序用户的信息,主要字段是 mini_open_id。原来微信用户表 wechat_users 的 mini_open_id 字段删除。
  • 因为已经是线上的系统,所以 wechat_users 表中已经有很多用户数据了。

目前的方案

前提:使用 Migration 进行所有表结构的管理。

  • 创建 CreateMiniProgramsTable 类新建 mini_program_users 表。
  • 创建 MoveWechatUserData 类将 wechat_users 表的 mini_open_id 等字段迁移至 mini_program_users 表。
  • 创建 EditWechatUsersTable 类删除 wechat_users 表的 mini_open_id 等字段。

目前的问题

  • 看了 migrate 和 seeder 文档,最后决定用 migrate 进行处理,不知 Laravel 是否还有其他更好的方案?
  • 因为 wechat_users 和 mini_program_users 表有关联关系,所以在 MoveWechatUserData 类中有一些必要的业务逻辑处理,然后在做新增操作,这样是否符合规范呢?

希望听到大家更好的解决方案,你们是如何处理这样的问题呢?

《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 10

我这两天刚写过 一对多 变 多对多 的数据处理,跟你的需求大概类似
我的做法是,
第一步:migration写新建表,和表新增的字段,
第二步:app/Console/Commands/目录下新建一个命令脚本,在这个脚本中跑数据处理(数据批量插入、原有字段删除、多余表删除、表字段名称修改等等)
第三步:命令行执行php artisan laravel-project:update-wechat-user (就是第二步命令的名称)

抛砖引玉啦,同求更优实现方法~

namespace App\Console\Commands;

use App\Models\A;
use App\Models\B;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

class UpdateWechatUser extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'laravel-project:update-wechat-user';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '批量更新微信用户相关数据';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {

        if (Schema::hasTable('a_table')) {
            $this->info('处理a_table表');
            DB::statement('alter table `a_table` drop column a_column');
            DB::statement('drop table c_table');
            DB::statement(' alter table a_table rename c_table');
        }

        if (Schema::hasColumn('b_table', 'b_column')) {
            $this->info('处理b_table表');
            DB::statement('truncate table d_table');
            $data = A::selectRaw('id as e_table_id,else_id,created_at,updated_at,deleted_at')
                ->withTrashed()->get();
            B::insert($data->toArray());
            DB::statement('alter table `d_table` drop column d_column');
        }
        $this->info('ok!');

        return true;
    }
}
4年前 评论

使用 command 确实也是一种方式,可以研究一下。

有个疑问,用这种方式,支持 rollback 这种降级操作吗?也是通过在写一个 command 来实现吧。
@aen233

4年前 评论

:see_no_evil:写的时候就没考虑回滚,本地用备份的数据库测试,随时跑命令,随时恢复数据库

4年前 评论

:ghost: 嗯 简单研究了一下 如果不考虑降级操作 command 会更灵活 :+1:

4年前 评论

这种操作一般不考虑降级,因为会导致信息丢失,旧表字段可以延后删除,比如1.1版本上线,等1.2 1.3以后再考虑把废弃的字段删除

4年前 评论

@Kamicloud 嗯 在降级的时候 确实会存在数据丢失的问题

因为我用 Migration 方案做,所以也定义了 down 方法,如果降级会丢失新增字段的数据。降级也是仅限于上线后短时间发现问题,能够及时回滚操作。

如果是您遇到这种需求,要如何优雅的处理呢?

4年前 评论

@24K大白羊 production禁用rollback,只允许测试和开发使用。回滚只做代码上的回滚,因为蓝绿部署的时候也需要保证旧代码可以跑,所以不太容易出现逻辑回滚后和数据库不兼容的情况

4年前 评论

@Kamicloud 你头像的猫猫好萌哦

4年前 评论

@Kamicloud 有道理 感谢分享 学习到了很多 :grin:

4年前 评论

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