【问题标题】:Laravel Migration - Adding Check Constraints In TableLaravel 迁移 - 在表中添加检查约束
【发布时间】: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 所需的声望)。

标签: php mysql laravel migration database-migration


【解决方案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 端进行自动验证,所以它对我没有帮助。但是感谢您的帮助:)
    猜你喜欢
    • 2011-06-07
    • 1970-01-01
    • 2021-08-30
    • 1970-01-01
    • 2020-03-12
    • 2019-03-24
    • 2017-03-27
    • 2018-04-01
    相关资源
    最近更新 更多