[laravel] 다형성관계 시더작성하기

  • 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’값을 전달해 줌으로써 재귀적인 댓글 구조를 가질수 있게 한다.