フォロー/フォロワー機能
実装機能
・relationshipsコントローラ(create, destroy)
・relationshipsモデル
・フォロー数 / フォロワー数の表示(ビュー)
・フォローボタン(ビュー)
・フォロー一覧 / フォロワー一覧(ビュー)
前提
・deviseを使用
・userモデルを作成済み
step1 モデルの作成
relationshipsモデル
カラム | データ型 | 詳細 |
---|---|---|
id | 初期カラム | 主キー |
follower_id | integer | フォローするユーザーのid |
followed_id | integer | フォローされるユーザーのid |
・フォロー vs フォロワーは多対多の関係なので、中間テーブルの作成が必要になる。
・relationshipsモデルは、中間テーブル。
フォロー(ユーザー) vs relationship
フォロワー(ユーザー) vs relationship
は、1対多の関係になる。
$ ralis g model relationship follower_id:integer followed_id:integer
また、migrationファイルを開き、timestampの下に
index :relationships, [:follower_id, :followed_id], unique: true
を追記する。
※これは同一のユーザーに対して複数のフォローを作成しないための制約です。
$ rails db:migrate
step2 モデル間のアソシエーションを作成
フォロー(ユーザー) vs relationship
フォロワー(ユーザー) vs relationship
は、1対多の関係。
そのため、relationshipモデルには以下のように記載する。
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
・follower や、followed は、勝手に便宜上命名したもの。
本来はuserモデルなので、class_name: "User" の記述を忘れずに。
userモデルには以下のように記載する。
has_many :relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
has_many :reverse_of_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy
has_many :followings, through: :relationships, source: :followed
has_many :followers, through: :reverse_of_relationships, source: :follower
以下、解説。
has_many :relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
この部分は、図中の青部分と黄色部分の関係性を表しています。
方向性は赤い矢印の向きです。
user は多くのrelationshipsを持っています。
user と relationshipsを紐づける外部キー(foreign_key)は follower_idです。
has_many :reverse_of_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy
この部分は、図中の緑部分と黄色部分の関係性を表しています。
方向性は赤い矢印の向きです。
user は多くのrelationshipsを持っています。
ただ、先に出たrelationshipsとは矢印の向きが逆なので、便宜上、reverse_of_relationshipsと名づけました。
user と reverse_of_relationshipsを紐づける外部キー(foreign_key)は followed_idです。
has_many :followings, through: :relationships, source: :followed
# has_many :自身がフォローしている全ユーザー, through: :スルーするモデル, source: :参照元となるモデル
この部分は、has_manyとは少し違います。
has_many throughという関連付けです。
*この部分のおかげで、 user.following_users で「自身がフォローしている全ユーザー」を取得できます。
has_many :followers, throuhg: :reverse_of_relationships, source: :follower
*この部分のおかげで、 user.followers_users で「自身の全フォロワーユーザー」を取得できます。
step3 メソッドの作成
ユーザーモデルにメソッドを作成します。
ここで作成したメソッドは、後にrelationshipsコントローラやビューで使用します。
def follow(user)
relationships.create(followed_id: user.id)
end
def unfollow(user)
relationships.find_by(followed_id: user.id).destroy
end
def following?(user)
followings.include?(user)
end
以下、解説です。
def follow(user)
relationships.create(followed_id: user.id)
end
この部分は、引数のuserをフォローする時のメソッドです。
relationshipsモデルでデータをcreateしています。
followed_idは user.idです。
follower_idは この followメソッドを呼び出し元のuser.idです。
def unfollow(user)
relationships.find_by(followed_id: user.id).destroy
end
この部分は、引数のuserのフォローを外す時のメソッドです。
relationshipsモデルでデータをdestroyしています。
def following?(user)
followings.include?(user)
end
この部分は、引数のuserをフォローしているかどうか?を確認するためのメソッドです。
このメソッドのおかげで、ビューで「このuserをフォローしている場合は、『フォロー解除ボタン』を表示」みたいな記述ができます。
step4 ルーティングの設定
resources :users, only: [:index,:show,:edit,:update] do
resource :relationships, only: [:create, :destroy]
get 'followings' => 'relationships#followings', as: 'followings'
get 'followers' => 'relationships#followers', as: 'followers'
end
relationshipsは userモデルに紐づいています。
そのため、userにネストします。
relationshipsの idを urlに使ったビューは必要ないので、resource にします。
step5 relationshipsコントローラの作成
$ rails g controller relationships
上記コマンドを実行後、以下のようにコントローラを記述
def create
user = User.find(params[:user_id])
current_user.follow(user) #userモデルに記述したメソッド
redirect_to request.referer
end
def destroy
user = User.find(params[:user_id])
current_user.unfollow(user) #userモデルに記述したメソッド
redirect_to request.referer
end
def followings
user = User.find(params[:user_id])
@users = user.followings #userモデルに記述したアソシエーション
end
def followers
user = User.find(params[:user_id])
@users = user.followers #userモデルに記述したアソシエーション
end
step6 ビューの作成 (User info部分)
次に、この部分のビューを作成する
<table class='table'>
<tr><%= image_tag user.get_profile_image, size:'100x100' %></tr>
<tr>
<th>name</th>
<th><%= user.name %></th>
</tr>
<tr>
<th>introduction</th>
<th><%= user.introduction %></th>
</tr>
<tr>
<th>Follows</th>
<th>
<%= link_to user.followings.count, user_followings_path(user) %></th>
</tr>
<tr>
<th>Followers</th>
<th><%= link_to user.followers.count, user_followers_path(user) %></th>
</tr>
</table>
<div class='row'>
<% if user != current_user %>
<%= render 'relationships/btn', user: user %>
<% else %>
<%= link_to edit_user_path(user), class: "btn btn-outline-secondary btn-block edit_user_#{user.id}" do %>
<i class="fas fa-user-cog"></i>
<% end %>
<% end %>
</div>
render部分
<% if current_user != user %>
<% if current_user.following?(user) %>
<%= link_to "フォローを外す", user_relationships_path(user.id), method: :delete, class: "btn btn-danger" %>
<% else %>
<%= link_to "フォローする", user_relationships_path(user.id), method: :post, class: "btn btn-success" %>
<% end %>
<% end %>
step7 ビューの作成 (Users部分)
次に、このビューを作成する
<table class='table'>
<thead>
<tr>
<th>image</th>
<th>name</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% users.each do |user| %>
<tr>
<td><%= image_tag user.get_profile_image, size: '50x50' %></td>
<td><%= user.name %></td>
<td><%= render "relationships/btn", user: user %></td>
<td><%= link_to 'Show', user, class: "user_#{user.id}" %></td>
</tr>
<% end %>
</tbody>
</table>
step8 ビューの作成 (Follow Users部分)
次に、フォロー中のユーザー一覧を作成します。
<h2>Follow Users</h2>
<% if @users.exists? %>
<%= render 'users/index', users: @users %>
<% else %>
<p>ユーザーはいません</p>
<% end %>
step9 ビューの作成 (Follower Users部分)
次に、フォロワー一覧を作成します。
<h2>Follow Users</h2>
<% if @users.exists? %>
<%= render 'users/index', users: @users %>
<% else %>
<p>ユーザーはいません</p>
<% end %>
完成です!
追記
relationshipモデルに
validates :follower_id, uniqueness: { scope: :followed_id }
こちらのバリデーションを追加しました。
※同一のユーザーに対し複数のフォローを作成しないようにするためです。
参照
Discussion