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 迁移 - 在表中添加检查约束的主要内容,如果未能解决你的问题,请参考以下文章
SQLSTATE[HY000]:一般错误:1215 无法在表用户中添加外键约束 Laravel