- User모델은 Article 모델과 일대다 관계를 가진다.
- Comment 모델은 Article 모델과 일대다 다형성 관계를 가진다.
- Article은 제한이 없는 계층적인 댓글을 가진다.
0.테이블 스키마생성
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->string('title');
$table->text('content');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->unsignedBigInteger('parent_id')->nullable();
$table->text('content');
$table->string('commentable_type');
$table->unsignedBigInteger('commentable_id');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('parent_id')->references('id')->on('comments');
});
1. 모델간 관계 정의
User 모델 :
public function articles()
{
return $this->hasMany(Article::class);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
Article 모델 :
protected $fillable = ['user_id', 'title', 'content'];
public function user()
{
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
Comment 모델:
protected $fillable = ['content', 'user_id', 'commentable_type', 'commentable_id', 'parent_id'];
public function commentable()
{
return $this->morphTo();
}
public function parent()
{
return $this->belongsTo(Comment::class, 'parent_id');
}
public function replies()
{
return $this->hasMany(Comment::class, 'parent_id');
}
public function article()
{
return $this->belongsTo(Article::class, 'commentable_id');
}
public function user()
{
return $this->belongsTo(User::class);
}
엘로퀀트는 comments 테이블의 외래키를 comment_id로 자동으로 추정하기 때문에 메서드 인자에 ‘parent_id’를 전달해서 외래키를 지정했다.
2.시더에 사용할 팩토리 작성
UserFactory :
$factory->define(User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
});
ArticleFactory :
$factory->define(Article::class, function (Faker $faker) {
return [
'title' => $faker->sentence,
'content' => $faker->paragraph,
];
});
Article은 User와 관계가 정의 되어있기 때문에 user_id를 따로 지정하지 않아도 자동으로 시더를 통해 입력된다.
CommentFactory :
$factory->define(Comment::class, function (Faker $faker) {
$userIds = App\User::all()->pluck('id')->toArray();
return [
'content' => $faker->paragraph(),
'user_id' => $faker->randomElement($userIds),
];
});
마찬가지로 ‘commentable_type’, ‘commentabe_id’를 지정하지 않는 이유는 위와 같다.
‘paretn_id’ 는 null을 허용한다. 이 값이 지정되어 있지 않으면 최상위 댓글로 간주한다. ‘CommentFactory’ 에는 이 값을 비워두어 최상위 댓글을 생성하게 한다. 후에 시더에서 make 메서드에 ‘parent_id’ 를 전달해 계층적인 댓글 구조를 만들어 준다.
3.시더작성
UsersTableSeeder :
public function run()
{
factory(App\User::class, 10)->create()->each(function($user){
$user->articles()->createMany(factory(App\Article::class, 3)->make()->toArray());
});
}
CommentsTableSeeder :
public function run()
{
$faker = Faker\Factory::create();
$articles = App\Article::all();
//최상위 댓글 생성
$articles->each(function($article){
$article->comments()->createMany(factory(App\Comment::class, 3)->make()->toArray());
});
//계층적 댓글 생성
$articles->each(function($article) use ($faker){
for($i=0; $i<10; $i++)
{
$commentIds = $article->comments()->pluck('id')->toArray();
$article->comments()->create(factory(App\Comment::class)->make(
['parent_id' => $faker->randomElement($commentIds)]
)->toArray());
}
});
}
계층적 댓글 생성 부분에서 make 메서드를 보면 ‘CommentFactory’에서 지정하지 않은 ‘parent_id’값을 전달해 줌으로써 재귀적인 댓글 구조를 가질수 있게 한다.