[Rails]follow機能作成- association N:Nについて
RailsでこのようなFollow/follwer機能を作るにあたって必要な、
アソシエーションの復習と、N:Nについてからやっていきます!
ここで理解が必要な用語まとめ
これは簡単になので、以下で詳しくやっていきます!
用語 | 意味 | 補足 |
---|---|---|
リレーションシップ(relationship) | 繋がり | ここでは、テーブル同士の繋がり(関連)のこと |
主キー(PK:Praimary Key) | primary:最初の | テーブルの情報を一意に識別するためのもの |
外部キー(foreign key) | foreign: 外国にある | 関連したテーブル間を結ぶために設定する列のこと |
アソシエーション(association) | 繋がり、関連性 | 異なる 2 つのモデルの間に、1:N の関連性を持たせるもの |
association(アソシエーション)
異なる 2 つのモデルの間に、1:N の関連性を持たせるもの。
もっとわかりやすく言うと、テーブル同士のリレーションシップを作り、
それをモデル上の関係として操作できるようにする仕組み。
主キー(primary key)
テーブルの情報を一意に識別するためのもの
外部キー(foreign key)
関連したテーブル間を結ぶために設定する列のこと。
データの整合性をデータベースに保証させるために利用。
(belongs_toで参照する場合に必ず必要。)
別の言い方すると…
主キー(primary key)を参照するためのカラムのこと.
もっとわかりやすくいえば、他のテーブルのデータを参照する事が出来るように制約を付けたカラムのこと
associationを記述しよう
上記のイラストのように、アソシエーションは二種類ある!
どっちの視点から見るかで変わるよ!
(=> このどちらから、がよく説明で書かれている、[ 1:Nの関係 ]のことだよ!)
1. has_manyメソッド
他のモデルを複数持っている事を定義する
上記のイラストでいくと、
userモデルからarticleモデルへアソシエーションを結ぶ
User(1) has_many Articles(N)
2. belongs_toメソッド
他のモデルに属している事を定義する.
上記のイラストでいくと
articleモデルからUserモデルへのアソシエーションを結ぶ
article(N) belong_to user(1)**
Following機能の作成方法
今回はこのような感じのものを作成していきます。
<大まかな作成手順>
- relationshipsモデルを作る
- relationshipsのマイグレーションファイル作成
- userモデルとrelationshipsモデルをアソシエーションする
- userモデルにフォロー機能のメソッドを書く
- relationshipsコントローラを作成&編集
- viewの編集 (ボタン設置等)
- ルーティングの記述
Following機能は、N:Nだから中間モデルが必要
"Followするひと" と "followされる人"は、どちらも、 "User"であって、多:多なのだ。
多:多の関係性になった場合は、中間テーブルを作成して、1:多にする必要がある。
二つのテーブルの間に、双方のidをForeign_key としてもつ中間テーブルを作成。
<中間テーブルの特徴>
- 接続している2つのテーブルのForeign_keyを持っている。
- null(空)を出すことなく、レコードを追加することができる。
1.中間テーブル作成 (model/tableの作成)
- 今回の中間テーブルでのモデル名は "Relationship"とする
$ rails g model Relationship
そして今回のテーブルは、このようにする。
カラム名 | 型 | |
---|---|---|
id | integer | 主キー (PK) |
follower_id(class_name:"User") | integer | フォローするユーザのid |
followed_id(class_name:"User") | integer | フォローされるユーザのid |
Followする人、Followされる人ともに "User"なので、
ここでテーブルを作成するにあたり、それぞれにUserモデルに属する、という記述が必要。
<Relationshipモデルmigrationファイル>
t.integer:follower_id, class_name:"User"
t.integer:followed_id, class_name:"User"
class_name オプションについて
モデル名を直接指定するもの。
関連名と参照先のクラス名を異なるものに置き換えることができるオプション
(関連先のモデルを参照する際の、名前の変更ができるもの.)
このように、多:多になった場合などで、中間テーブルを作成する際など
関連付けの相手となるオブジェクト名を関連付け名から生成できない事情がある場合に役立つ.
<記述型>
class_name:"関連先モデル名"
- 記述できたらmigrationする
$ rails db:migrate
2. associationしていく
Userモデルと、Relationshipモデルの繋がりを記述していく。
まずはrelationshipモデルの記述
<relationship.rb>
class Relationship < ApplicationRecord
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
end
Userモデルにも記述
ここは少し多いので下で解説します。
< user.rb >
class User < ApplicationRecord
.
.
has_many :follower, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy # ① フォローしている人の取得
has_many :followed, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy # ② フォローされているの人取得
has_many :following_user, through: :follower, source: :followed # 自分がフォローしている人
has_many :follower_user, through: :followed, source: :follower # 自分をフォローしている人
# ユーザーをフォローする
def follow(user_id)
follower.create(followed_id: user_id)
end
# ユーザーのフォローを外す
def unfollow(user_id)
follower.find_by(followed_id: user_id).destroy
end
# フォローしていればtrueを返す
def following?(user)
following_user.include?(user)
end
end
<has_many 上から2行>
has_many :follower, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
-
has_many :follower, class_name: "Relationship"
この記述は、Userモデルでの説明と一緒!
(Userモデルでの表記と一緒で、relationshipモデルも分岐しているので、class_nameが必要です) -
foreign_key: "follower_id"
参照先外部キーのカラムを指定する必要があるため、foreign_keyの記述が必要。
これは以下の二つの記述()部分を成立するためにある。
⚪︎(フォローする側から、) 中間テーブルを通して、フォローされる側を取得する
⚪︎(フォローされる側から、)中間テーブルを通して、フォローしてくる側を取得する
<has_many 下の2行>
has_many :following_user, through: :follower, source: :followed
ここではforeign_keyで繋いだところの定義をおこなう。
- following_user:中間テーブルを通し、followedモデルのフォローされる側を取得
- follower_user:中間テーブルを通し、followerモデルのフォローする側を取得
Routingの設定
Userと、Relationshipは associationして繋げているため、userのidが必要。
relationshipsをネストさせる。
routing のネストとは
あるコントローラへのルーティングの記述の中に、別のコントローラへのルーティングを記述すること。
routingをネストすることのメリット
- URLの改装構造ができる。
- 関係性のあるもの同士をを紐づけることができる。
関係があるもの同士は、原則ルーティングをネストさせて関係性のあるURLを生成すること!!!!
今回はこのようになる。
:
resources :users, only: [:index, :show, :edit, :update] do
member do
get :follows, :followers
end
resource :relationships, only: [:create, :destroy]
end
rails routes
で確認するとこのようにidが入っていることが確認できる。
memberオプション
resources以外の自分で定義したアクションへのルーティングを設定する場合に使用する。
同じ場面で使用するものとしてcollection
があるが、アクションにidを渡したいときは、 memberを使用すること。
- member : アクションにidが渡されるため、id を使用した特定のデータに対するアクションの場合。
- collection :idを渡さない。id の必要ない全体のデータに対するアクションの場合。
上記でmemberを使用しているのは、
follow・followerアクションが必要かつ、それらにはidが必要だから。
Controllerの作成
今回はrelationships controller
$ rails g controller relationships
コントローラーにアクションの記述
< Relationships Controller >
class RelationshipsController < ApplicationController
def create
current_user.follow(params[:user_id])
redirect_to request.referer
end
def destroy
current_user.unfollow(params[:user_id])
redirect_to request.referer
end
end
< Users Controller >
def follows
user = User.find(params[:id])
@users = user.following_user.page(params[:page]).per(3).reverse_order
end
def followers
user = User.find(params[:id])
@users = user.follower_user.page(params[:page]).per(3).reverse_order
end
⚠️ ユーザー詳細ページで、フォロー数とフォロワー数を表示するための追記も行う。
< Users Controller >
:
def show
@user = User.find(params[:id])
# @posts = @user.posts.page(params[:page]).reverse_order
@following_users = @user.following_user
@follower_users = @user.follower_user
end
:
Viewの記述を行う。
- まずはこの部分。
<table>
:
省略
:
<tr>
<th>Follows</th>
<td>
<%= link_to follows_user_path(user) do %>
<h6><%= user.follower.count %></h6>
<% end %>
</td>
</tr>
<tr>
<th>Followers</th>
<td>
<%= link_to followers_user_path(user) do %>
<h6><%= user.followed.count %></h6>
<% end %>
</td>
</tr>
</table>
<!--ここからはボタンの記述-->
<div class='row mb-4'>
<% if current_user != user %>
<% if current_user.following?(user) %>
<%= link_to 'フォロー外す', user_relationships_path(user.id), method: :delete, class: "btn-sm btn-danger" %>
<% else %>
<%= link_to 'フォローする', user_relationships_path(user.id), method: :post, class:"btn-sm btn-success" %>
<% end %>
<% end %>
</div>
- Follow User-page/followers User-pageも作成する。
View>Userフォルダの下に、follows.html.erb と followers.html.erbファイルを作成すること。
View>Userフォルダの下に、follows.html.erb と followers.html.erbファイルを作成すること。
<follows.html.erb>
<table class='table'>
<thead>
<tr>
<th>Image</th>
<th>name</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<% @following_users.each do |user| %>
<tr>
<td>
<%= link_to user_path(user) do %>
<%= image_tag user.get_profile_image, size:'50x50' %>
<% end %>
</td>
<td><%= user.name %></td>
<td>
<%= link_to follows_user_path(user) do %>
<h6>フォロー<%= user.follower.count %></h6>
<% end %>
</td>
<td>
<%= link_to followers_user_path(user) do %>
<h6>フォロワー<%= user.followed.count %></h6>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
このように作成することができる!!!!
<ここで私の少し躓いたポイント>
follow usersページで、followしている人たちのfollow人数の表記.
<% @following_users.each do |user| %>
<h6>フォロー<%= user.follower.count %></h6>
<h6>フォロワー<%= user.followed.count %></h6>
<% end %>
user.follower.count
=> each文での変数 userにmodelで定義したfollowerで出力して.countオプション!
参照ページ
⏩ association公式ドキュメント
⏩Railsでサポートされている関連付け
⏩request.refererについて
⏩memberオプション
⏩routingのネスト
Discussion
わかりやすい!!!Rails勉強中なので助かっています〜:)
ジョンちゃんありがとう!
説明がとてもわかりやすくて理解しやすかったです!
こういう絵があると内容が入ってくるし、絵が上手!!
勉強になったし参考にさせてもらいました♪
ありがとうございました(^^)
参考にしていただきありがとうございます!
(今改めて見ると、不足が多いかなあと思いながら...笑)
がんもさんの図はとても見やすいし、動画もつけていて見やすかったです!
のちのDMM受講生は、同じように、がんもさんの記事でとても助かると思います!!
そう言っていただけると嬉しいです!ありがとうございます😄
あいりさんみたいな手書きの絵が描けるようになりたいなぁ...といつも読みながら思ってます笑
後から読み直して改善点が見つけられるって成長している証拠なのですごいと思います😊
先の記事がどんどんクオリティがあがっているので読んでいくのが楽しみです😌
このように当時を振り返ることができるのも毎日やってた醍醐味かなと思ったりします!
初心者の時期だからこその記事というか,考え方というか,視点というか...(まだ初心者ですが)笑
がんもさんのも楽しみです😉💖