Migration Blueprint 继承及其相关问题
分割线下面是原问题细节,实际上想问的问题可以归结为:(看代码)
Schema::create('Person',function(Blueprint $table){
$table->string('id')->comment('身份证号');
$table->string('id')->comment('学号')->change();//为什么此句不起作用?
});
上述代码中的第二句为什么不起作用呢?
解决方案
目前的理解是:
change()函数的调用实际上只是将['change'=>true]
添加到ColumnDefinition
,然后最终在toSql()
中进行语法解析,而change
操作最终会转变为ChangeColumn::compile()
交给doctrine/dbal
处理,这里推测doctrine/dbal
的操作是在表格已经存在的情况下,所以如果表格不存在,change()
没有任何操作。
而如果依旧想实现后面的定义会覆盖前面的,那么根据Blueprint的执行过程,可以重写addColumn()
函数,如下:
public function addColumn($type, $name, array $parameters = [])
{
foreach($this->columns as $k=>$v)
{
if($v->name==$name)//如果之前有定义
{
$this->columns=array_except($this->columns,$k);//把之前的定义删除
break;
}
}
return parent::addColumn($type,$name,$parameters);
}
实际上,Blueprint中允许定义两个相同名字的字段,只不过后面的Grammar会进行检测。上面的代码实现了自己的目的不过在定义字段的时候需要记着后面的定义会覆盖前面的!
问题背景
最初通过laravel
的Migration
来管理数据库表的迁移时,都是一个文件对应一个表格。即使表格可能会存在继承关系,还是会写很多重复的代码定义。如下:
M1 extends Migration
{
public function up
{
Schema::create('Person',function(Blueprint $table){
$table->string('name')->comment('name');
$table->string('sex')->comment('sex');
$table->string('id')->comment('身份证号');
....
});
}
......
}
M2 extends Migration
{
public function up
{
Schema::create('Student',function(Blueprint $table){
$table->string('name')->comment('name');
$table->string('sex')->comment('sex');
$table->string('id')->comment('学号');
....
});
}
......
}
如上所示,倘若能够实现Student继承自Person,那么M2中就可以少写很多重复代码
问题分析
由于Schema::create()
第二个参数为闭包且传入的是Blueprint对象,因此如果能够继承Blueprint则能够达到上述目的。怀着这个想法查到了下面的问答:Extend Blueprint class?
继承
Blueprint
的主要步骤举例就是:(代码在下面)
- 新建类
Person
直接继承系统Blueprint
类,在构造函数中完成基本的字段定义Student
然后继承Person
,其构造函数中增加其独特的字段定义,如:班级
Person extends Blueprint
{
public function __construct($table,$callback=null,$prefix='')
{
parent::__construct($table,$callback,$prefix');
$table->string('name')->comment('name');
$table->string('sex')->comment('sex');
$table->string('id')->comment('身份证号');
}
}
Student extends Person
{
public function __construct($table,$callback=null,$prefix='')
{
parent::__construct($table,$callback,$prefix');
//下面的方式1和2是为了在Student中修改Person中的已有定义,后面有用!
//$table->string('id')->comment('学号')->change();//方式1
//$table->removeColumn();//方式2
//$table->string('id')->comment('学号')->change();
}
}
那么再通过
Schema
来建表的主要步骤举例就是:(代码在下面)
- 获取
Schema
的实例$schema
- 修改
$schema
的blueprintResolver
以返回Person/Student
实例- 将
$schema->create()
的闭包函数function(){}
的参数类型修改为Person/Student
M1 extends Migration
{
public function up
{
$schema = Schema::getFacadeRoot();
$schema->blueprintResolver(function($table, $callback,$prefix) {
return new Person($table, $callback,$prefix);
});
$schema->('Person',function(Person $table){
**这里什么都没有**
});
}
......
}
M2 extends Migration
{
public function up
{
$schema = Schema::getFacadeRoot();
$schema->blueprintResolver(function($table, $callback,$prefix) {
return new Student($table, $callback,$prefix);
});
$schema->('Student',function(Student $table){
**这里什么都没有**
});
}
......
}
解决了基本的继承问题,不过,如上述代码所示:Person
的id
字段comment
值为身份证号
,那么显然Student
中的id
字段comment值
继承自Person
。
但是,如果想要Student
中id
的comment
更改为学号
应该怎么实现呢?
上述代码中注释部分写了2种方法,不过没有达到预期效果!
Blueprint继承无法覆盖已定义字段?
如代码中所示,change()
以及removeColumn()
都没有达到子类覆盖父类的字段定义
的目的
change()
指的是,如:$student->string('id')->comment('学号')->change()
removeColumn()
指的是:$table->removeColumn(‘id’)
经查阅资料发现:包括官网指导在内的很多教程都只是举例如何在php artisan migrate
之后,建立新的Migration
再调用相应的方法,并且这些Modify Column
操作需要额外的依赖composer require doctrine/dbal
,从这里揣测,似乎doctrine/dbal
所做的工作都是在表格存在的情况下!
那么,在继承的基础上实现上述字段重定义应该怎么做呢?
推荐文章: