php的seeder是什么,Laravel学习笔记之Seeder填充数据小技巧
说明:本文主要聊一聊Laravel测试数据填充器Seeder的小技巧,同时介绍下Laravel开发插件三件套,这三个插件挺好用哦。同时,作者会将开发过程中的一些截图和代码黏上去,提高阅读效率。
备注:在设计个人博客软件时,总会碰到有分类Category、博客Post、给博客贴的标签Tag、博客内容的评论Comment。
而且,Category与Post是一对多关系One-Many:一个分类下有很多Post,一个Post只能归属于一个Category;Post与Comment是一对多关系One-Many:一篇博客Post下有很多Comment,一条Comment只能归属于一篇Post;Post与Tag是多对多关系Many-Many:一篇Post有很多Tag,一个Tag下有很多Post。
开发环境:Laravel5.2 + MAMP + PHP7 + MySQL5.5
开发插件三件套
在先聊测试数据填充器seeder之前,先装上开发插件三件套,开发神器。先不管这能干些啥,装上再说。
1、barryvdh/laravel-debugbar
composer require barryvdh/laravel-debugbar --dev
2、barryvdh/laravel-ide-helper
composer require barryvdh/laravel-ide-helper --dev
3、mpociot/laravel-test-factory-helper
composer require mpociot/laravel-test-factory-helper --dev
然后在config/app.php文件中填上:
/**
*Develop Plugin
*/
Barryvdh\Debugbar\ServiceProvider::class,
Mpociot\LaravelTestFactoryHelper\TestFactoryHelperServiceProvider::class,
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
设计表的字段和关联
设计字段
按照上文提到的Category、Post、Comment和Tag之间的关系创建迁移Migration和模型Model,在项目根目录输入:
php artisan make:model Category -m
php artisan make:model Post -m
php artisan make:model Comment -m
php artisan make:model Tag -m
在各个表的迁移migrations文件中根据表的功能设计字段:
//Category表
class CreateCategoriesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->comment('分类名称');
$table->integer('hot')->comment('分类热度');
$table->string('image')->comment('分类图片');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('categories');
}
}
//Post表
class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->integer('category_id')->unsigned()->comment('外键');
$table->string('title')->comment('标题');
$table->string('slug')->unique()->index()->comment('锚点');
$table->string('summary')->comment('概要');
$table->text('content')->comment('内容');
$table->text('origin')->comment('文章来源');
$table->integer('comment_count')->unsigned()->comment('评论次数');
$table->integer('view_count')->unsigned()->comment('浏览次数');
$table->integer('favorite_count')->unsigned()->comment('点赞次数');
$table->boolean('published')->comment('文章是否发布');
$table->timestamps();
//Post表中category_id字段作为外键,与Category一对多关系
$table->foreign('category_id')
->references('id')
->on('categories')
->onUpdate('cascade')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//删除表时要删除外键约束,参数为外键名称
Schema::table('posts', function(Blueprint $tabel){
$tabel->dropForeign('posts_category_id_foreign');
});
Schema::drop('posts');
}
}
//Comment表
class CreateCommentsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->integer('post_id')->unsigned()->comment('外键');
$table->integer('parent_id')->comment('父评论id');
$table->string('parent_name')->comment('父评论标题');
$table->string('username')->comment('评论者用户名');
$table->string('email')->comment('评论者邮箱');
$table->string('blog')->comment('评论者博客地址');
$table->text('content')->comment('评论内容');
$table->timestamps();
//Comment表中post_id字段作为外键,与Post一对多关系
$table->foreign('post_id')
->references('id')
->on('posts')
->onUpdate('cascade')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//删除表时要删除外键约束,参数为外键名称
Schema::table('comments', function(Blueprint $tabel){
$tabel->dropForeign('comments_post_id_foreign');
});
Schema::drop('comments');
}
}
//Tag表
class CreateTagsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tags', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->comment('标签名称');
$table->integer('hot')->unsigned()->comment('标签热度');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('tags');
}
}
由于Post表与Tag表是多对多关系,还需要一张存放两者关系的表:
//多对多关系,中间表的命名laravel默认按照两张表字母排序来的,写成tag_post会找不到中间表
php artisan make:migration create_post_tag_table --create=post_tag
然后填上中间表的字段:
class CreatePostTagTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('post_tag', function (Blueprint $table) {
$table->increments('id');
$table->integer('post_id')->unsigned();
$table->integer('tag_id')->unsigned();
$table->timestamps();
//post_id字段作为外键
$table->foreign('post_id')
->references('id')
->on('posts')
->onUpdate('cascade')
->onDelete('cascade');
//tag_id字段作为外键
$table->foreign('tag_id')
->references('id')
->on('tags')
->onUpdate('cascade')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('post_tag', function(Blueprint $tabel){
$tabel->dropForeign('post_tag_post_id_foreign');
$tabel->dropForeign('post_tag_tag_id_foreign');
});
Schema::drop('post_tag');
}
}
设计关联
写上Migration后,还得在Model里写上关联:
class Category extends Model
{
//Category-Post:One-Many
public function posts()
{
return $this->hasMany(Post::class);
}
}
class Post extends Model
{
//Post-Category:Many-One
public function category()
{
return $this->belongsTo(Category::class);
}
//Post-Comment:One-Many
public function comments()
{
return $this->hasMany(Comment::class);
}
//Post-Tag:Many-Many
public function tags()
{
return $this->belongsToMany(Tag::class)->withTimestamps();
}
}
class Comment extends Model
{
//Comment-Post:Many-One
public function post()
{
return $this->belongsTo(Post::class);
}
}
class Tag extends Model
{
//Tag-Post:Many-Many
public function posts()
{
return $this->belongsToMany(Post::class)->withTimestamps();
}
}
然后执行迁移:
php artisan migrate
数据库中会生成新建表,表的关系如下:
Seeder填充测试数据
好,在聊到seeder测试数据填充之前,看下开发插件三件套能干些啥,下文中命令可在项目根目录输入php artisan指令列表中查看。
1、barryvdh/laravel-ide-helper
执行php artisan ide-helper:generate指令前:
执行php artisan ide-helper:generate指令后:
不仅Facade模式的Route由之前的反白了变为可以定位到源码了,而且输入Config Facade时还方法自动补全auto complete,这个很方便啊。
输入指令php artisan ide-helper:models后,看看各个Model,如Post这个Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\Post
*
* @property integer $id
* @property integer $category_id 外键
* @property string $title 标题
* @property string $slug 锚点
* @property string $summary 概要
* @property string $content 内容
* @property string $origin 文章来源
* @property integer $comment_count 评论次数
* @property integer $view_count 浏览次数
* @property integer $favorite_count 点赞次数
* @property boolean $published 文章是否发布
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property-read \App\Category $category
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Comment[] $comments
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Tag[] $tags
* @method static \Illuminate\Database\Query\Builder|\App\Post whereId($value)
* @method static \Illuminate\Database\Query\Builder|\App\Post whereCategoryId($value)
* @method static \Illuminate\Database\Query\Builder|\App\Post whereTitle($value)
* @method static \Illuminate\Database\Query\Builder|\App\Post whereSlug($value)
* @method static \Illuminate\Database\Query\Builder|\App\Post whereSummary($value)
* @method static \Illuminate\Database\Query\Builder|\App\Post whereContent($value)
* @method static \Illuminate\Database\Query\Builder|\App\Post whereOrigin($value)
* @method static \Illuminate\Database\Query\Builder|\App\Post whereCommentCount($value)
* @method static \Illuminate\Database\Query\Builder|\App\Post whereViewCount($value)
* @method static \Illuminate\Database\Query\Builder|\App\Post whereFavoriteCount($value)
* @method static \Illuminate\Database\Query\Builder|\App\Post wherePublished($value)
* @method static \Illuminate\Database\Query\Builder|\App\Post whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\App\Post whereUpdatedAt($value)
* @mixin \Eloquent
*/
class Post extends Model
{
//Post-Category:Many-One
public function category()
{
return $this->belongsTo(Category::class);
}
//Post-Comment:One-Many
public function comments()
{
return $this->hasMany(Comment::class);
}
//Post-Tag:Many-Many
public function tags()
{
return $this->belongsToMany(Tag::class)->withTimestamps();
}
}
根据迁移到库里的表生成字段属性和对应的方法提示,在控制器里输入方法时会自动补全auto complete字段属性的方法:
2、mpociot/laravel-test-factory-helper
输入指令php artisan test-factory-helper:generate后,database/factory/ModelFactory.php模型工厂文件会自动生成各个模型对应字段数据。Faker是一个好用的生成假数据的第三方库,而这个开发插件会自动帮你生成这些属性,不用自己写了。
/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| Here you may define all of your model factories. Model factories give
| you a convenient way to create models for testing and seeding your
| database. Just tell the factory how a default model should look.
|
*/
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'email' => $faker->safeEmail,
'password' => bcrypt(str_random(10)),
'remember_token' => str_random(10),
];
});
$factory->define(App\Category::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name ,
'hot' => $faker->randomNumber() ,
'image' => $faker->word ,
];
});
$factory->define(App\Comment::class, function (Faker\Generator $faker) {
return [
'post_id' => function () {
return factory(App\Post::class)->create()->id;
} ,
'parent_id' => $faker->randomNumber() ,
'parent_name' => $faker->word ,
'username' => $faker->userName ,
'email' => $faker->safeEmail ,
'blog' => $faker->word ,
'content' => $faker->text ,
];
});
$factory->define(App\Post::class, function (Faker\Generator $faker) {
return [
'category_id' => function () {
return factory(App\Category::class)->create()->id;
} ,
'title' => $faker->word ,
'slug' => $faker->slug ,//修改为slug
'summary' => $faker->word ,
'content' => $faker->text ,
'origin' => $faker->text ,
'comment_count' => $faker->randomNumber() ,
'view_count' => $faker->randomNumber() ,
'favorite_count' => $faker->randomNumber() ,
'published' => $faker->boolean ,
];
});
$factory->define(App\Tag::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name ,
'hot' => $faker->randomNumber() ,
];
});
在聊第三个debugbar插件前先聊下seeder小技巧,用debugbar来帮助查看。Laravel官方推荐使用模型工厂自动生成测试数据,推荐这么写的:
//先输入指令生成database/seeds/CategoryTableSeeder.php文件: php artisan make:seeder CategoryTableSeeder
use Illuminate\Database\Seeder;
class CategoryTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
factory(\App\Category::class, 5)->create()->each(function($category){
$category->posts()->save(factory(\App\Post::class)->make());
});
}
}
//然后php artisan db:seed执行数据填充
但是这种方式效率并不高,因为每一次create()都是一次query,而且每生成一个Category也就对应生成一个Post,当然可以在each()里每一次Category继续foreach()生成几个Post,但每一次foreach也是一次query,效率更差。可以用debugbar小能手看看。先在DatabaseSeeder.php文件中填上这次要填充的Seeder:
public function run()
{
// $this->call(UsersTableSeeder::class);
$this->call(CategoryTableSeeder::class);
}
在路由文件中写上:
Route::get('/artisan', function () {
$exitCode = Artisan::call('db:seed');
return $exitCode;
});
输入路由/artisan后用debugbar查看执行了15次query,耗时7.11ms:
实际上才刚刚输入几个数据呢,Category插入了10个,Post插入了5个。
可以用DB::table()->insert()批量插入,拷贝ModelFactory.php中表的字段定义放入每一个表对应Seeder,当然可以有些字段为便利也适当修改对应假数据。
class CategoryTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// factory(\App\Category::class, 20)->create()->each(function($category){
// $category->posts()->save(factory(\App\Post::class)->make());
// });
$faker = Faker\Factory::create();
$datas = [];
foreach (range(1, 10) as $key => $value) {
$datas[] = [
'name' => 'category'.$faker->randomNumber() ,
'hot' => $faker->randomNumber() ,
'image' => $faker->url ,
'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
'updated_at' => \Carbon\Carbon::now()->toDateTimeString()
];
}
DB::table('categories')->insert($datas);
}
}
class PostTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$faker = Faker\Factory::create();
$category_ids = \App\Category::lists('id')->toArray();
$datas = [];
foreach (range(1, 10) as $key => $value) {
$datas[] = [
'category_id' => $faker->randomElement($category_ids),
'title' => $faker->word ,
'slug' => $faker->slug ,
'summary' => $faker->word ,
'content' => $faker->text ,
'origin' => $faker->text ,
'comment_count' => $faker->randomNumber() ,
'view_count' => $faker->randomNumber() ,
'favorite_count' => $faker->randomNumber() ,
'published' => $faker->boolean ,
'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
'updated_at' => \Carbon\Carbon::now()->toDateTimeString()
];
}
DB::table('posts')->insert($datas);
}
}
class CommentTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$faker = Faker\Factory::create();
$post_ids = \App\Post::lists('id')->toArray();
$datas = [];
foreach (range(1, 50) as $key => $value) {
$datas[] = [
'post_id' => $faker->randomElement($post_ids),
'parent_id' => $faker->randomNumber() ,
'parent_name' => $faker->word ,
'username' => $faker->userName ,
'email' => $faker->safeEmail ,
'blog' => $faker->word ,
'content' => $faker->text ,
'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
'updated_at' => \Carbon\Carbon::now()->toDateTimeString()
];
}
DB::table('comments')->insert($datas);
}
}
class TagTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$faker = Faker\Factory::create();
$datas = [];
foreach (range(1, 10) as $key => $value) {
$datas[] = [
'name' => 'tag'.$faker->randomNumber() ,
'hot' => $faker->randomNumber() ,
'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
'updated_at' => \Carbon\Carbon::now()->toDateTimeString()
];
}
DB::table('tags')->insert($datas);
}
}
class PostTagTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$faker = Faker\Factory::create();
$post_ids = \App\Post::lists('id')->toArray();
$tag_ids = \App\Tag::lists('id')->toArray();
$datas = [];
foreach (range(1, 20) as $key => $value) {
$datas[] = [
'post_id' => $faker->randomElement($post_ids) ,
'tag_id' => $faker->randomElement($tag_ids) ,
'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
'updated_at' => \Carbon\Carbon::now()->toDateTimeString()
];
}
DB::table('post_tag')->insert($datas);
}
}
在DatabaseSeeder.php中按照顺序依次填上Seeder,顺序不能颠倒,尤其有关联关系的表:
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// $this->call(UsersTableSeeder::class);
$this->call(CategoryTableSeeder::class);
$this->call(PostTableSeeder::class);
$this->call(CommentTableSeeder::class);
$this->call(TagTableSeeder::class);
$this->call(PostTagTableSeeder::class);
}
}
输入路由/artisan后,生成了10个Category、10个Post、50个Comments、10个Tag和PostTag表中多对多关系,共有9个Query耗时13.52ms:
It is working!!!
表的迁移Migration和关联Relationship都已设计好,测试数据也已经Seeder好了,就可以根据Repository模式来设计一些数据库逻辑了。准备趁着端午节研究下Repository模式的测试,PHPUnit结合Mockery包来TDD测试也是一种不错的玩法。
M(Model)-V(View)-C(Controller)模式去组织代码,很多时候也未必指导性很强,给Model加一个Repository,给Controller加一个Service,给View加一个Presenter,或许代码结构更清晰。具体可看下面分享的一篇文章。
最近一直在给自己充电,研究MySQL,PHPUnit,Laravel,上班并按时打卡,看博客文章,每天喝红牛。很多不会,有些之前没咋学过,哎,头疼。后悔以前读书太少,书到用时方恨少,人丑还需多读书。
研究生学习机器人的,本打算以后读博搞搞机器人的(研一时真是这么想真是这么准备的,too young too simple)。现在做PHP小码农了,只因当时看到智能机就激动得不行,决定以后做个码农试试吧,搞不好是条生路,哈哈。读书时觉悟太晚,耗费了青春,其实我早该踏入这条路的嘛,呵呵。Follow My Heart!
不扯了,在凌晨两点边听音乐边写博客,就容易瞎感慨吧。。
分享下最近发现的一张好图和一篇极赞的文章:
php的seeder是什么,Laravel学习笔记之Seeder填充数据小技巧相关推荐
- Laravel学习笔记汇总——Collection方法详解
## Laravel学习笔记汇总--Collection方法详解 本文参考:https:// laravel.com/docs/8.x/collections // 返回整个底层的数组 collect ...
- JavaWeb黑马旅游网-学习笔记05【分类数据展示功能】
Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...
- 学习笔记(五)——数据适配器、数据表、数据网格视图控件的综合应用。
学习笔记(五)--数据适配器.数据表.数据网格视图控件的综合应用. 1. 批量修改 修改包括增加,删除以及更新3个操作,所以声明实力化3个SQL命令分别应用于插入,删除以及修改 将声明的SQL命令连 ...
- HALCON 20.11:深度学习笔记(3)---Data(数据)
HALCON 20.11:深度学习笔记(3)---Data(数据) HALCON 20.11.0.0中,实现了深度学习方法.其中,关于术语"数据"的介绍如下: 术语"数据 ...
- R学习笔记(4): 使用外部数据
来源于:R学习笔记(4): 使用外部数据 博客:心内求法 鉴于内存的非持久性和容量限制,一个有效的数据处理工具必须能够使用外部数据:能够从外部获取大量的数据,也能够将处理结果保存.R中提供了一系列的函 ...
- SDN软件定义网络 学习笔记(4)--数据平面
SDN软件定义网络 学习笔记(4)--数据平面 1. 简介 2. SDN数据平面架构 2.1 传统网络交换设备架构 2.2 SDN交换设备架构 2.3 数据平面架构图 3. SDN芯片与交换机 3.1 ...
- 【Vue】学习笔记-组件传值的数据累加器
[Vue]学习笔记-组件传值的数据累加器 前言 父级组件 购物车组件 计数器组件 常见错误总结 前言 组件传值的数据累加器可以分为三个部分 App.vue为父级组件 Carts.vue表示购物车 Co ...
- SQLite学习笔记(七)-- 数据插入、更新和删除(C++实现)
1.代码实例 代码说明 本例主要说明如何对数据表进行插入.更新和删除操作. 测试平台 1.开发语言:C++ 2.开发工具:VS2015 3.操作系统:Win7 X64 测试数据说明 测试表为Stude ...
- Python学习笔记---merge和concat数据合并(1)
Python学习笔记-merge和concat数据合并(1) Python学习笔记-merge和concat数据合并(2) 文章目录 Python学习笔记---merge和concat数据合并(1) ...
- 数据库学习笔记(一) | 数据(Data)的定义
数据库学习笔记(一) | 数据(Data)的定义和种类 什么是数据(Data) 结构化数据(Structured Data) 半结构化数据(Semi-structured Data) 非结构化数据(U ...
最新文章
- 【Java】IO Stream详细解读
- 利用DBMS_ADVISOR.TUNE_MVIEW包生成物化视图创建语句
- Modelsim-altera 仿真 顶层原理图的解决办法
- iText关于中文的解决方案
- C语言中常见的内存相关的Bugs
- hadoop22---wait,notify
- 【计算机组成与设计学习笔记】(一)
- 陕西省计算机分数线,2019陕西省各大学录取分数线最新汇总
- V4L2 pixel format 格式参考
- Alertmanager 通知模板
- open3d使用知识拾遗
- 腾讯企业邮箱 POP3/SMTP 设置
- Excel条形图也可以变身高大上
- http://ai.taobao.com/?pid=mm_40428920_1105750338_109783200329
- android 序列化存储对象,android中对象序列化存储
- 解决 Agent JAR loaded but agent failed to initialize
- L160. 相交链表
- Python开发的Web在线学习教育培训网课系统
- 聊聊Monolisa和JetBrains Mono字体在编码中的体验
- 选择护肤品时应该注意的功效成分!