Php 如何在Laravel迁移中安全地将不可为null的列添加到现有表中?
我想在Laravel迁移中向现有表中添加一个不可为空的列,其中包含一些行。 在SQL中,我理解这样的操作应该在事务内部按照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
- 在不破坏DB完整性的情况下进行初始化,以及
不违反ALTER TABLE
约束notnull
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();
});
});