使用SQLite迁移Laravel '无法添加具有默认值NULL的NOT NULL列'

k5ifujac  于 2023-10-22  发布在  SQLite
关注(0)|答案(8)|浏览(135)

为什么我在使用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)
zpjtge22

zpjtge221#

看起来这是一个SQLite古怪。根据Laracast forum thread about the same issue
从头开始添加表时,可以指定NOT NULL。但是,在添加列时不能执行此操作。SQLite的规范要求必须有一个默认值,这是一个糟糕的选择。
再看看SQLite ALTER TABLE docs,我发现:
如果指定了NOT NULL约束,则列必须具有NULL以外的默认值。
我想在SQLite的世界里,不提供默认值就等于说默认值应该是NULL(而不是意味着这个不可空的列没有默认值,所以每次插入时都必须为它提供一个值)。
看起来SQLite只是让你处于一个糟糕的状态,如果你需要添加一个不可空的列到一个现有的表,该列也不应该有一个默认值。

3hvapo4f

3hvapo4f2#

我成功使用的一个变通方法是检查正在使用哪个数据库驱动程序,并稍微修改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 ($driver === 'sqlite') {
                $table->string('stripe_invoice_number')->default('');
            } else {
                $table->string('stripe_invoice_number')->after('stripe_invoice');
            }
        });
    }
}
vktxenjb

vktxenjb3#

如果你不希望列是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('');

    });
  }
a8jjtwal

a8jjtwal4#

所有的民间解决方案都很好,但我想找到一种可重用和可读的方法来做到这一点,所以我做了一个trait,希望它能帮助您从迁移中删除一些样板代码。
方法是$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;
  }
}
shstlldc

shstlldc5#

我不熟悉Laravel,但显然使用after方法来指定列的顺序似乎特别提到了 ONLYMySQL(Laravel),并且讨论(GitHub)似乎指出了它与SQLite一起使用的困难。它可能不兼容,因为它的功能:“. use the after method to specify the order of columns“设计遇到了SQLite文档(SQLite)中关于添加列的限制.其中规定:"The new column is always appended to the end of the list of existing columns."我不能说用->default($value)分配默认值是否能让你通过。

aoyhnmkz

aoyhnmkz6#

你必须添加

->nullable()

对于可能具有空值的列,

ffx8fchx

ffx8fchx7#

解决此问题的另一种方法是首先将字段创建为可空,然后将字段更改为非空。例如,在这种情况下,我们将做如下事情:

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();
    });
}
5us2dqdw

5us2dqdw8#

我做的人解释,它的工作,只是使您的外键为空在您的迁移

public function up()
{
    Schema::table('products', function (Blueprint $table) {
        $table->foreignId('category_id')
            ->nullable()
            ->constrained();
    });
}

相关问题