7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Laravel]ポリモーフィックリレーション

Posted at

[Laravel]ポリモーフィックリレーション

とは???

超端的に言うと複数テーブルと紐付けることが出来るリレーション

経緯

PL「この設計は多対1のポリモーフィックを表していると思うので、設計とそれに伴った実装修正しておいて。」
自分「確かにそうですよね!(は???ポリモーフィックとはなんぞや???)」
ということで何もわからない状態から1日かけてマイグレーション、モデル、取得、作成処理を書き直しました。
このあたり公式ドキュメントも意外と詳しく書いてくれていなくて、中々苦労したので備忘録とします。

実装方法

テーブル定義

users
id
admin_users
id
comments
id
user_id
admin_user_id

みたいな感じでusersとadmin_userとcommentsテーブルがあるとします。
この際ユーザーも管理者もコメントをかくことが出来るとします。
commentsテーブルのuser_idはusers、admin_user_idはadmin_user_idの外部キーです。
この外部キーを1つに出来るのがポリモーフィックです。
まず結論からこのcommentsテーブルをどう変えるかというと

comments
id
commentable_id
commentable_type
このcommentable_idとcommentable_typeがミソで、comment_typeにどちらのテーブルかを記載し(少し語弊がありますが後で詳しく書きます)、commentable_idにusers、admin_usersのidが入ります。

マイグレーション

$table->morphs('commentable');

この1行足すだけで勝手にidとtype両方作ってくれます。

Model

// Userクラス
public function comments()
{
    return $this->morphMany(
                  Comment::class,
                  'commentable',
                  )
}
// AdminUserクラス
public function comments()
{
    return $this->morphMany(
                  Comment::class,
                  'commentable',
                  )
}
// Commentクラス
public function commentable()
{
    return $this->morphTo();
}

こちらで下処理完璧です。
ちなみに自分がハマった原因として、commentableの部分をマイグレーション時点でスネークケースで000_000_idと000_000_typeで表現していたことが原因でこのモデルにカスタム設定を使用しないとリレーションが取れませんでした。(ちなみに取得はできるけど作成ができなくなる)
もし自分と同じ方法でやるなら

// Userクラス、AdminUserクラス
public function comments()
{
    return $this->morphMany(
                  Comment::class,
                  null,
                  'commentable_type',
                  'commentable_id',
                  'id'
                  )
}

こちらならスネークケース2区切り以上の名前ものも可能です。

取得

クエリを使うために、whereHasMorph()を使用します

Comment::whereHasMorph('commentable', 
    [User::clss, AdminUser::class], 
     function ($query, $type) {
         $query->~~~~~
     }
     )->get();

作成

リレーションを繋げて作成処理するという、あまり経験したことのない処理をすることで新規作成出来ます。リレーション先の主キーのidから自動で作成してくれます。
以下が参考コードになります

User::find($userId)->comments()->create([~~~]);

最後に

自分が詰まった部分が調べても出てこないことが多かったので、この記事で同じことに悩んでいる方の一助になれば幸いです。
ちなみに調べていると出てきたと思いますが、ポリモーフィックリレーションはSQLのアンチパターンみたいなので鵜呑みにせず一度は他の方法も考えたみたほうがいいかもしれません。。。

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?