Laravel 迁移 - 在表中添加检查约束

Posted

技术标签:

【中文标题】Laravel 迁移 - 在表中添加检查约束【英文标题】:Laravel Migration - Adding Check Constraints In Table 【发布时间】:2016-11-08 10:55:14 【问题描述】:

我想像这样在 Laravel Migration 中创建一个表-

CREATE TABLE Payroll
(
 ID int PRIMARY KEY, 
 PositionID INT,
 Salary decimal(9,2) 
 CHECK (Salary < 150000.00)
);

我所做的是-

Schema::create('Payroll', function (Blueprint $table)

    $table->increments('id');
    $table->integer('PositionID ');
    $table->decimal('Salary',9,2);
    //$table->timestamps();
);

但我无法创建这个-

 CHECK (Salary < 150000.00)

谁能告诉我,如何在Laravel Migration 中实现这个CHECK 约束?

【问题讨论】:

对于我们当中的 Google 员工来说,Kael Watts-Deuchar 的回答需要更新。截至MariaDB 10.2.1 MariaDB 现在支持检查约束。 (我无法直接评论 Kael,因为我缺乏 50 所需的声望)。 【参考方案1】:

这个答案已经过时了,MariaDB 10.2.1 和 mysql 8.0.16 都支持正确的检查约束。


MySQL/Mariadb 忽略CHECK 约束,因此您必须改用触发器。触发器只能设置为在 INSERT/UPDATE/DELETE 之一上运行,这意味着如果我们希望它同时在 INSERT 和 UPDATE 上运行,我们必须创建一个过程,然后从两个单独的触发器中调用它。

DB::statement() 不支持这种语法,所以我们必须使用 PDO::exec() 代替。

这是 Michael 示例的 TRIGGER 语法:

public function up()

    Schema::create('Payroll', function (Blueprint $table) 
        $table->increments('id');
        $table->integer('position_id');
        $table->decimal('salary', 9, 2);
    );

    DB::connection()->getPdo()->exec("
        -- Create the procedure
        CREATE PROCEDURE payroll_check_salary_amount (salary INT)
        BEGIN
            IF NOT (salary < 150000.00) THEN
                SIGNAL SQLSTATE '45000' SET message_text = 'salary must be less than 150000.00';
            END IF;
        END;

        -- Create the INSERT trigger
        CREATE TRIGGER payroll_check_salary_amount_insert BEFORE INSERT ON Payroll
        FOR EACH ROW
        BEGIN
            CALL payroll_check_salary_amount(NEW.salary);
        END;

        -- Create the UPDATE trigger
        CREATE TRIGGER payroll_check_salary_amount_update BEFORE UPDATE ON Payroll
        FOR EACH ROW
        BEGIN
            CALL payroll_check_salary_amount(NEW.salary);
        END;
    ");


public function down()

    Schema::dropIfExists('Payroll');
    DB::statement('DROP PROCEDURE IF EXISTS payroll_check_salary_amount');

【讨论】:

在 MySQL 8.0.16 之前,CREATE TABLE 只允许以下有限版本的 table CHECK 约束语法,它被解析和忽略:从 MySQL 8.0.16 开始,CREATE TABLE 允许 table 的核心特性和列 CHECK 约束,适用于所有存储引擎。 CREATE TABLE 允许以下 CHECK 约束语法,用于表约束和列约束:dev.mysql.com/doc/refman/8.0/en/…【参考方案2】:
public function up ()

    Schema::create('payroll', function (Blueprint $table) 
        $table->increments('id');
        $table->integer('p_id');
        $table->decimal('payment',9,2);
    );

    // Add the constraint`enter code here`
    DB::statement('ALTER TABLE payroll ADD CONSTRAINT check_salary_amount CHECK (payment < 2000.00);');

【讨论】:

请编辑您的答案并解释您的代码为何有效!【参考方案3】:

要添加约束,您可以编写以下代码

$table->enum('choices', ['foo', 'bar']);

但是如果你想用算术运算来做,解决办法是用 sql 语句来做,就像**@Michael** 提到的那样

// Add the constraint
DB::statement('ALTER TABLE payroll ADD CONSTRAINT chk_salary_amount CHECK (salary < 150000.00);');

查看以下link 了解更多信息

【讨论】:

【参考方案4】:

Blueprint 类不支持添加约束(至少从 Laravel 5.3 开始),但是可以通过使用数据库语句直接从迁移中为表添加约束。 p>

在您的迁移文件中,

public function up ()

    Schema::create('payroll', function (Blueprint $table) 
        $table->increments('id');
        $table->integer('position_id');
        $table->decimal('salary',9,2);
    );

    // Add the constraint
    DB::statement('ALTER TABLE payroll ADD CONSTRAINT chk_salary_amount CHECK (salary < 150000.00);');

【讨论】:

此方法有效,但请注意它不适用于 SQLite(例如,当用作内存数据库进行测试时)。 SQLite 不支持在创建表后更改约束。要解决这个问题,您需要确保只在 SQLite 之外的任何内容上添加约束。【参考方案5】:

Blueprint 类中不包含此功能,因此您无法在迁移文件中执行此操作。

但在您的薪资模型中,您可以创建一个修改器:

class Payroll extends Model

    public function setSalaryAttribute($value)
        $this->attributes['Salary'] = $value < 150000.00 ? $value : 150000.00;
    


所以当一个 payroll Salary 属性被创建或更新时,这个方法会被自动触发并且会检查新的值不超过 150000.00

编辑:您应该查看 Laravel 文档中的 mutators documentation。

【讨论】:

可以在迁移文件中添加约束,只是不使用 Blueprint 类。详情请见my answer。 这将默默地限制数量,这与 CHECK CONSTRAINT 所做的完全相反 - 抛出一个错误。如果你想在某种程度上模仿 CHECK CONSTRAINT 的行为,我建议你抛出一个错误【参考方案6】:

我不认为这是 Laravel 迁移中的一个功能。我认为这必须在您的模型或验证逻辑中,除非您手动将其添加到 MYSQL

这就是我要做的

$this->validate($request, [
    'Salary' => 'max:150000.00',
]);

【讨论】:

这也是我解决这个问题的方法。在您保存或更新数据之前,在您获取请求的控制器中使用此功能。 我不是在问它,我是在要求使用 MySQL 端进行自动验证,所以它对我没有帮助。但是感谢您的帮助:)

以上是关于Laravel 迁移 - 在表中添加检查约束的主要内容,如果未能解决你的问题,请参考以下文章

如何编写迁移来撤消Laravel中的唯一约束?

SQLSTATE[HY000]:一般错误:1215 无法在表用户中添加外键约束 Laravel

SQLSTATE [23000]:完整性约束违规:1452 无法在 laravel 迁移中添加或更新子行

如何在 Rails 迁移中添加检查约束?

Laravel 迁移:无法添加外键约束

外键 Laravel 8 foreignId + 约束 - 无法添加或更新子行:外键约束失败