Php 如何在Laravel迁移中安全地将不可为null的列添加到现有表中?

Php 如何在Laravel迁移中安全地将不可为null的列添加到现有表中?,php,sql,laravel,postgresql,migration,Php,Sql,Laravel,Postgresql,Migration,我想在Laravel迁移中向现有表中添加一个不可为空的列,其中包含一些行。 在SQL中,我理解这样的操作应该在事务内部按照 添加列 初始化列 使其不可为空 以保证 在不破坏DB完整性的情况下进行初始化,以及 ALTER TABLE不违反notnull约束 以下是PostgreSQL代码示例(假设users表有一列old\u col),涉及: 像这样的普通Laravel迁移文件不起作用 public function up() { Schema::table('users', func

我想在Laravel迁移中向现有表中添加一个不可为空的列,其中包含一些行。 在SQL中,我理解这样的操作应该在事务内部按照

  • 添加列
  • 初始化列
  • 使其不可为空
  • 以保证

    • 在不破坏DB完整性的情况下进行初始化,以及
    • ALTER TABLE
      不违反
      notnull
      约束
    以下是PostgreSQL代码示例(假设
    users
    表有一列
    old\u col
    ),涉及:

    像这样的普通Laravel迁移文件不起作用

    public function up()
    {
        Schema::table('users', function($table) {
            $table->integer('new_col');  // ->nullable(false) // later?
        });
    }
    
    如何在Laravel迁移中实现SQL事务或其等价物

    注释(已编辑)

    如果您想设置默认值,并且不需要(绝对同时)根据每行的某些值更新现有行的列,那么您可以简单地在迁移文件中指定
    ->default(0)
    或类似的内容(并避免所有技巧!)。我的问题不是为要添加的列设置默认值。

    您需要将
    默认值设置为您想要的任何值:

    public function up()
    {
        Schema::table('users', function($table) {
            $table->integer('new_col')->default(0); 
        });
    }
    

    解决方案包含三个查询:

    DB::transaction(function () {
        Schema::table('users', function (Blueprint $table) {
            $table->integer('id_cloned')->nullable();
        });
    
        App\Models\User::query()->update([
            'id_cloned' => DB::raw('id + 1'),
            'updated_at' => DB::raw('now()') // if you want to touch the timestamp
        ]);
    
        Schema::table('users', function (Blueprint $table) {
            $table->integer('id_cloned')->nullable(false)->change();
        });
    });
    
    没有DB::raw parts的替代解决方案,但将为每个记录生成单独的更新查询:

    DB::transaction(function () {
        Schema::table('users', function (Blueprint $table) {
            $table->integer('id_cloned')->nullable();
        });
    
        foreach (App\Models\User::all() as $row) {
            $row->id_cloned = $row->id + 1;
            $row->save();
        }
    
        Schema::table('users', function (Blueprint $table) {
            $table->integer('id_cloned')->nullable(false)->change();
        });
    });
    

    您可以在foreach中编写代码,如$methodName='item->get'.$method'.()'

    类项目{

    getFoo()

    getBar()

    }

    $methods=['Foo','Bar']

    foreach($methods作为$method){

    $methodName='item->get'.$method'.()'

    echo$methodName


    }

    至少在Postgres上,列的默认值不能从另一列派生:对不起,我应该说我不想设置默认值……此外,正如@AndriusRimkus正确指出的,您不能使用另一列设置默认值。话虽如此,如果您100%确定迁移期间没有conpeting DB update请求,那么可以在Schema语句之后立即编写语句来更新现有行。我明白了!我已确认
    DB::transaction()
    肯定有效,并且独家工作流程得到保证!但也有一些问题。首先,在最后一条语句中需要
    ->nullable(false)
    。其次,
    处的
    updated\u列不会更新(尽管这取决于您是否要更新它们)。第三,必须安装
    doctrine/dbal。第四,模型应该是
    App\User
    。让我更新并改进你的答案。谢谢@MasaSakano,谢谢你的评论。我同意
    ->nullable(false)
    部分,抱歉,没有正确测试
    updated_at
    是一个不错的选择,但正如您所说的,这取决于具体情况。我无法理解
    原则/dbal
    部分。Laravel对这个包有依赖关系,我们不能假设它默认存在吗?对于
    App\User
    部分,我导入了正确的名称空间,但在示例中显式可能更好。不确定这是否是对您提出的更改进行评论的正确位置。我只想指出,虽然使用循环/更新的解决方案很好,但它对数据库的影响也很大。谢谢您的回复和改进的答案,Andrius!事实上,为了测试这一点,我使用
    composer-create项目--preference dist-Laravel/Laravel
    对最新的稳定的Laravel(5.7.3版)进行了一次干净的安装,发现
    doctrine/dbal
    没有默认安装,还发现
    App\User
    是默认路径。你最后的评论绝对正确!让我们希望拉威尔将来能提供一种更好、更轻松的方式来处理此类案件。
    DB::transaction(function () {
        Schema::table('users', function (Blueprint $table) {
            $table->integer('id_cloned')->nullable();
        });
    
        foreach (App\Models\User::all() as $row) {
            $row->id_cloned = $row->id + 1;
            $row->save();
        }
    
        Schema::table('users', function (Blueprint $table) {
            $table->integer('id_cloned')->nullable(false)->change();
        });
    });