在日常开发中,经常需要往数据库里填充数据,以便调试接口发现bug。Laravel自带的数据填充器seeder也十分好用,纯自动化填充,还支持一键回滚,只需在使用之前定义些方法即可,下面就介绍一下seeder普遍的用法。

版本

Laravel 5.2

流程介绍

  1. 生成Seeder填充类
  2. 按实际情况定义模型工厂
  3. 执行artisan命令自动化填充数据到数据库
  4. 回滚,恢复到实际生产环境

起步

首先利用artisan命令创建一个seeder类

1
$ php artisan make:seeder TestSeeder

在/database/seeds/中,编写run方法

1
2
3
4
5
6
7
8
9
class TestSeeder extends Seeder
{
public function run()
{
DB::table('YourTable')->insert([
'name' => str_random(10)
]);
}
}

如上,只需把插入语句写进seeder类的run方法即可。上面的例子是往YourTable表里插入一条记录,但是这是我们日常都是大批量填充数据的,此时就需要用到模型工厂。

一个模型单角色定义

模型工厂的定义脚本在/database/factories/ModelFactory.php,我们只需脚本里定义好模型,然后就可以在seeder类里使用了。

1
2
3
4
5
6
7
8
9
$factory->define(App\YourModel::class,function (Faker\Generator $faker){
return [
'name' => $faker->name,
'email' => $faker->email,
'password' => str_random(10),
'remember_token' => str_random(10),
'admin' => true,
];
});

Faker是专门生成假数据的包(GITHUB地址),这样至少生成的假数据也好看点而不是一堆乱码数字对吧。

一个模型多角色定义

除了用define字段定义模型外,我们常常还用到defineAs,因为通常一个模型(表)里面,存在这不同角色,比如User表里可以存在普通用户或者管理员两种角色,这时可以用defineAs

1
2
3
4
5
6
7
8
9
10
$factory->defineAs(App\YourModel::class,'admin',function (Faker\Generator $faker){
return [
// blabla 按实际情况编写
'name' => $faker->name,
'email' => $faker->email,
'password' => str_random(10),
'remember_token' => str_random(10),
'is_admin' => true,
];
});

生产

这样我们就定义好了一个模型工厂,要工厂生产产品就需要用到Laravel的全局函数factory.生产产品的工厂一定要是定义过的才能用factory函数

1
2
3
4
5
6
7
// 以下写在seeder类的run方法里
// 生产1个模型实例
$obj=$factory->(App\YourModel::class)->make();
// 生产5个模型实例 返回类型为collection
$collection=$factory->(App\YourModel::class,5)->make();
// 生产5个admin身份的模型实例
$collection=$factory->(App\YourModel::class,'admin',5)->make();

处理关联

数据库插入时也经常涉及关联,比如有两张表父表与子表,由于存在关联关系,设置了外键约束时,填充数据会很蛋疼,不过利用Laravel的eloquent使得填充数据十分方便,下面就讲一下如何操作

首先假设有两张表father和son,其中son的主键参照father表中的一个字段id

father的模型:

1
2
3
4
5
6
7
class father extends Model
{
public function test(){
// Laravel 的模型关联方法 不清楚的可以补一下文档
return $this->hasMany(\App\son:class,'id');
}
}

son的模型细节我就忽略了,因为不是重点。接下来就要定义两个表的模型工厂

1
2
3
4
5
6
7
8
9
10
11
$factory->define(App\father::class,function (){
return [
//blabla
] ;
});
$factory->define(App\father::class,function (){
return [
//blabla
] ;
});

然后就可以在seeder类的run方法里使用了

1
2
3
4
5
factory(App\father::class,3)
->create()
->each(function ($obj){
$obj->test()->save(factory(App\son::class,2)->make());
});

就可以在你填充3个父亲的数据的同时,也为每个父亲填充2个与之关联的儿子


填充器模块化

一个填充器写N多个factory来填充数据,难免会看晕,因此我们需要将负责不同模型的生产分模块,划分成N多个子填充器

1
2
3
4
5
6
7
public function run(){
Model::unguard();
$this->call(Test1Seeder::class);
$this->call(Test2Seeder::class);
// .......
Model::reguard();
}

unguard函数貌似是关闭自动填充的限制,reguard是恢复限制.还有一个函数
Model::truncate();用于放在成员函数里可以清空成员表数据,用来初始化填充还可以,不过有重要数据请备份。


最后一步

在编写好seeder之后,运行artisan即可进行填充

1
2
php artisan db:seed
php artisan db:seed --class=xxxxxSeeder // 你的填充类

一键回滚,重建数据库

1
php artisan migrate:refresh --seed

坑点

因为运行artisan命令时本质上是用CLI,因此请开启proc_open,proc_get_status等系统调用函数,留意你的CLI所用的拓展是否有对应数据库的拓展,如果遇到CLI的坑可以去翻一些我前几天些的关于CLI的坑,遇到什么permission denied的给对应文件加上权限就好。