为什么在使用SQLite驱动程序时会收到此警告?我对MySQL驱动程序没有任何问题,但SQLite正在抛出此错误。
这对我来说没有意义,因为我理解在所有迁移完成后发生种子播放,为什么它会抱怨这个问题只有在数据库中已经存在数据时才会出现。
我的两次迁移是
第一次迁移
public function up() {
Schema::create('users', function($table) {
$table->increments('id');
$table->string('username');
$table->string('email');
$table->string('password');
});
}
第二次迁移
public function up() {
Schema::table('users', function(Blueprint $table) {
$table->date('birthday')->after('id');
$table->string('last_name')->after('id');
$table->string('first_name')->after('id');
});
}
错误
Exception: SQLSTATE[HY000]: General error: 1 Cannot add a NOT NULL column with default value NULL (SQL: alter table "users" add column "birthday" date not null)
答案 0 :(得分:16)
看起来这是一个SQLite的怪异。根据{{3}}:
从头开始添加表时,可以指定NOT NULL。但是,添加列时无法执行此操作。 SQLite的规范说你必须有一个默认值,这是一个糟糕的选择。
进一步了解Laracast forum thread about the same issue,我发现:
如果指定了NOT NULL约束,则该列必须具有NULL以外的默认值。
我认为在SQLite的世界中,不提供默认值与声明默认值应该为NULL是相同的(相反意味着此非可空列没有默认值,因此值必须在每个插页上提供它。)
如果您需要将一个不可为空的列添加到现有表中,SQLite似乎只会让您处于错误状态,该列也应该没有默认值。
答案 1 :(得分:6)
如果您不希望该列为nullable
,那么您需要让Laravel知道应该的默认值。
一个选项是像这样的空字符串""
public function up() {
Schema::create('users', function($table) {
$table->date('birthday')->after('id')->default('');
$table->string('last_name')->after('id')->default('');
$table->string('first_name')->after('id')->default('');
});
}
答案 2 :(得分:4)
我对Laravel并不熟悉,但显然使用after
方法来指定列的顺序似乎特别提到 ONLY MySQL
{{3}讨论(Laravel)似乎指出了它与SQLite一起使用的困难。它可能不兼容,因为它的功能:“...使用after方法to specify the order of columns
”设计符合SQLite文档(GitHub)中的限制,用于添加列...其中包含:{{1}我不能说使用"The new column is always appended to the end of the list of existing columns."
分配默认值是否可以让你接受。
答案 3 :(得分:3)
我成功使用的解决方法是检查正在使用的数据库驱动程序,并稍微修改SQLite的迁移。
例如:
class MyMigration extends Migration
{
public function up()
{
$driver = Schema::connection($this->getConnection())->getConnection()->getDriverName();
Schema::table('invoices', function (Blueprint $table) use ($driver) {
$table->string('stripe_invoice')->nullable()->change();
if ('sqlite' === $driver) {
$table->string('stripe_invoice_number')->default('');
} else {
$table->string('stripe_invoice_number')->after('stripe_invoice');
}
});
}
}
答案 4 :(得分:3)
所有的人解决方案都是好的,但是我想找到一种可重用且可读的方法来做到这一点,所以我做出了一个特征,希望它可以帮助您从迁移中删除一些样板代码。
方法是$this->databaseDriverIs("driver_name_here");
。
这是我在典型的表创建迁移中使用它的方式:
<?php
use App\Traits\WithDatabaseDriver;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCountryTable extends Migration
{
use WithDatabaseDriver;
public function up()
{
Schema::create("country", function (Blueprint $table) {
$table->increments("id");
$table->string("name");
$table->unique("name", "unique_country_name");
});
Schema::table("continent", function (Blueprint $table) {
$column = $table->unsignedInteger("countryId");
// This is where we apply the "fix" if
// the driver is SQLite
if ($this->databaseDriverIs("sqlite")) {
$column->nullable();
}
$table->foreign("countryId")->references("id")->on("country");
});
}
public function down()
{
Schema::dropIfExists("country");
}
}
这是完成所有工作的特征:
<?php
namespace App\Traits;
trait WithDatabaseDriver
{
/**
* @var string
*/
private $driver;
public function __construct()
{
$this->driver = config("database.default");
}
public function databaseDriverIs(string $driver): bool
{
return $this->driver === $driver;
}
}
答案 5 :(得分:1)
你必须添加
->nullable()
表示可能具有空值的列
答案 6 :(得分:0)
此问题的另一种解决方法是先将字段创建为可为空,然后再将其更改为非空。因此,例如在这种情况下,我们将执行以下操作:
public function up() {
// Create fields first as nullable
Schema::table('users', function(Blueprint $table) {
$table->date('birthday')->after('id')->nullable();
$table->string('last_name')->after('id')->nullable();
$table->string('first_name')->after('id')->nullable();
});
// Either truncate existing records or assign a value to new fields
if (true) {
DB::table('users')->truncate();
} else {
DB::table('users')->update([
'birthday' => '2019-05-01',
'last_name' => 'last name',
'first_name' => 'first name',
]);
}
// Change the fields to not be null
Schema::table('users', function(Blueprint $table) {
$table->date('birthday')->nullable(false)->change();
$table->string('last_name')->nullable(false)->change();
$table->string('first_name')->nullable(false)->change();
});
}
答案 7 :(得分:0)
正如人们所解释的那样,我做到了,而且确实有效,只是在迁移过程中使您的外键为空即可
public function up()
{
Schema::table('products', function (Blueprint $table) {
$table->foreignId('category_id')
->nullable()
->constrained();
});
}