質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.20%

多対多リレーションと中間テーブルの考え方について

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,969

miyu94

score 18

多対多リレーションの認識で曖昧な部分があります。

「モデルAとモデルBが、多対多関係である」とは「あるAは複数のBを持つことができ、あるBは複数のAを持つことができる」関係だと認識しています。

この認識は正しいでしょうか?

よくSNSにある以下の機能は多対多ですか?

・ユーザー(User)が投稿(Post)に「いいね」する機能
・ユーザー(User)が他のユーザー(User)を「フォロー」する機能

これらの機能をLaravel標準の多対多リレーションを使わずに実装したら、いろんな問題が発生したので、多対多で実装すべきだと思いました。

フォロー機能は、両モデルが同じUserですが多対多になりますか。

これらの機能を実装するスタンダードなやり方を知りたいです。
以下のやり方は正しいでしょうか?

「いいね」機能

中間テーブルのカラムは user_id, post_id です。

//Userクラス
public function posts()
{
   return $this->belongsToMany('App\Post');
}

//Postクラス
public function users()
{
   return $this->belongsToMany('App\User');
}

「フォロー」機能

ユーザーのフォロー一覧、フォロワー一覧、相互フォロー一覧などを取得できることが前提です。

中間テーブル follows のカラムは user_id, follow_user_id です。

//Userクラス

//自分がフォローしているユーザーを取得
public function my_follow_users()
{
   return $this->belongsToMany('App\User', 'follows', 'user_id', 'follow_user_id');
}
//自分のフォロワーを取得
public function my_follower_users()
{
   return $this->belongsToMany('App\User', 'follows', 'follow_user_id', 'user_id');
}


相互フォローの取得は複雑になりそうです。

その他疑問

Laravelでいいね機能を作ってみた
この記事のように「いいね」実装に多対多を使わない例を見るのですが、どちらが良いでしょうか?
中間テーブルのモデルクラス内にもリレーションメソッドを記述しているようです。

中間テーブルのモデルLikeに、postメソッドとuserメソッドをbelongsToで定義。
Userモデルに、likesメソッドをhasManyで定義。
Postモデルに、userメソッドをbelongsTo、likesメソッドをhasManyで定義。

・中間テーブルの2カラム(例 user_id, post_id)は外部キー制約を付け、かつユニーク制約をつけるべきですか?

質問が多くてすみません。ご回答いただけると幸いです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

「モデルAとモデルBが、多対多関係である」とは「あるAは複数のBを持つことができ、あるBは複数のAを持つことができる」関係だと認識しています。
この認識は正しいでしょうか?

正しいです。

よくSNSにある以下の機能は多対多ですか?

はい。

この記事のように「いいね」実装に多対多を使わない例を見るのですが、どちらが良いでしょうか?

ケースバイケースですが、基本的にIDカラムしかない場合はBelongsToManyを使えば良いでしょう。
この前同じような質問がありました。
https://teratail.com/questions/169935

・中間テーブルの2カラム(例 user_id, post_id)は外部キー制約を付け、かつユニーク制約をつけるべきですか?

ユニーク制約は不要です。
例えば、例に上げてらっしゃる「いいね」機能で考えると、同じユーザーが複数の投稿にいいねする(中間テーブルにuser_idが複数回挿入される)可能性があるからです。
外部キー制約は基本的につけたほうが良いでしょう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/27 00:24

    ありがとうございます。

    > 基本的にIDカラムしかない場合はBelongsToManyを使えば良いでしょう。

    中間テーブルのことですよね?いいねの例だと user_id と post_id の2カラムのことですね。
    IDの2カラム + created_at の3カラムだとどうでしょうか?

    > ユニーク制約は不要です。
    説明不足で申し訳ございません。「組み合わせ」のユニーク制約のことです。

    キャンセル

  • 2019/02/27 00:30

    BelongsToManyでないと実現するのが難しい場面が多くて、
    できればBelongsToMany使いたいですね。

    キャンセル

  • 2019/02/27 06:27

    > 中間テーブルのことですよね?
    はい、IDカラムしかない中間テーブルのことです。

    > IDの2カラム + created_at の3カラムだとどうでしょうか?
    created_atのようなWHOカラムがあったとしてもBelongsToManyでよろしいかと思います。
    IDおよびWHOカラムしかない中間テーブルは作成、削除がほとんどで更新はほぼないと考えられるためです。
    非正規化された中間テーブルやステータスカラムを持つ中間テーブル(例:友達申請に使用するテーブル)等に関しては柔軟性や拡張性が求められたりするので、hasMany, belongsToを使うほうがよろしいかと思います。

    > 説明不足で申し訳ございません。「組み合わせ」のユニーク制約のことです。
    あーなるほど。個人的にはアプリの実装に頼るだけでなくDBレベルでも制約(外部キー制約、ユニーク制約等)をつけることをおすすめします。
    ただ、プロジェクト規約・性質によりDBによる制約はつけないこともありますので、必須ではないという回答になります。

    キャンセル

  • 2019/02/28 21:34

    BelongsToManyで注意するのは、中間テーブルの更新処理が発生する場合なのですね。

    制約に関しては、必須ではないが付けるのが無難ということですね。

    ありがとうございました!

    キャンセル

0

この記事のように「いいね」実装に多対多を使わない例を見るのですが、どちらが良いでしょうか?

多対多は柔軟に対応できる反面、実装が複雑になったり整合性を取るのが大変になりがちです。
もし多対多を使わないで済むなら使わないにこしたことはないと思います。

以下の記事を読ませていただきましたが、特に違和感もありませんし、この実装を参考にして良いと思います。

Laravelでいいね機能を作ってみた

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/27 00:35

    多対多は便利な反面、複雑になりがちなのですね。
    ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.20%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる