なぜこの記事を書いたのか
Railsでのフォロー機能について説明している記事はたくさんあります。
しかし、「自分がフォローしているユーザーの一覧ページ」と「自分をフォロしているユーザーの一覧ページ」までの実装手順を説明している記事は少なく感じました。
この「フォロー一覧」と「フォロワー一覧」ですが、Relationshipテーブルのカラムをよく考えて命名しておかないと、ややこしくなることがあります。
following
, follower
, follow
, followed
など様々な命名方法が記事によって混在していますが、一番混乱しにくい命名方法で解説されている記事が少なかったため、改めて記事を作成することにしました。
まず結論から言います。
この記事では、フォローする側はfollowing
、**フォローされる側はfollower
**としておくとよく覚えておいてください。
普通の感覚だと、『フォローする側がfollower
じゃないの?』と感じるかもしれません。長くなるので詳しい説明は省きますが、仮にその命名方法を取った場合、あるユーザーの「フォロワー一覧」を表示する際に参照するカラムが、follower
カラムではなく、もう一方のカラムになってしまうため、色々と不都合が起きます。
現時点でこの意味が分からなくても全く問題ないので、ここではとにかくフォローする側はfollowing
、**フォローされる側はfollower
**だということを認識しておいてください。
前提
- MVCを理解している
- 基本的なアソシエーションを理解している
- Userモデルが作成してある
流れ
- Relationshipモデルを作る
- アソシエーションを組む
- ルーティングを組む
- コントローラーでアクションを定義
- ビューを編集
Relationshipモデルを作る
フォロー機能では、フォローする側もフォローされる側もUserであるため、Userテーブルのレコード同士で「多対多」の関係を作る必要があります。
これを実現するために、フォローするユーザーとフォローされるユーザーの情報を保存しておく中間テーブルを作る必要があります。
例えば、ユーザーAがユーザーBをフォローした場合は、以下のようにして情報を記録します。
Userテーブル
id | name |
---|---|
1 | ユーザーA |
2 | ユーザーB |
Relationshipテーブル
id | following | follower |
---|---|---|
1 | 2 |
以下をターミナルに打ち込んで、実際に中間テーブルであるRelationshipテーブルを作ってみましょう。
$ rails g model relationship following_id:integer follower_id:integer
$ rails db:migrate
これでRelationshipという中間テーブルができました!
このテーブルの存在意義は、以下の2つの情報を保存しておくことです。
- フォローするユーザーが誰なのかを示す
following
カラム - フォローされるユーザーが誰なのかを示す
follower
カラム
アソシエーション
今作成したRelationshipモデルにあるfollowing
とfollower
という情報は、どちらも親であるUserテーブルから情報を参照する必要があります。(フォローする側もされる側もどちらもUserであるため。)
FollowingモデルとFollowerモデルを擬似的に作成することで、その参照先をうまく分けることができます。
Relationshipモデルを以下のように変更してください。
class Relationship < ApplicationRecord
belongs_to :following, class_name: "User"
belongs_to :follower, class_name: 'User'
end
class_nameにUserと記述することで、それぞれの参照先がUserモデルであることを示しています。
次にUserモデル側にアソシエーションを記述しましょう。
まずは以下のコードをまとめて追加してください。
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
#以下を挿入
has_many :following, class_name: "Relationship", foreign_key: "following_id", dependent: :destroy
has_many :follower, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
has_many :following_user, through: :following, source: :follower
has_many :follower_user, through: :follower, source: :following
def follow(user_id)
following.create(follower_id: user_id)
end
def unfollow(user_id)
following.find_by(follower_id: user_id).destroy
end
def following?(user_id)
following_user.include?(user_id)
end
end
順番に説明していきます。
まずは以下の部分について。
has_many :following, class_name: "Relationship", foreign_key: "following_id", dependent: :destroy
has_many :follower, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
1つ目のhas_many
では、Relationshipモデルのfollowing_idにuser_idを格納する作業を行っています。その際、foreign_keyでfolowings_id
と明示的に格納したいカラムを指定しています。
2つ目のhas_many
では、Relationshipモデルのfollower_idにuser_idを格納する作業を行っています。その際、foreign_keyでfollower_id
と明示的に格納したいカラムを指定しています。
次に、以下の部分について。
has_many :following_user, through: :following, source: :follower
has_many :follower_user, through: :follower, source: :following
ここでは、自分がフォローしているユーザーと自分をフォローしているユーザーを簡単に取得するためにthroughを使った関連付けを行っているだけです。
こうすることで、例えば@user.following_user
などと記述することで自分がフォローしたユーザーの情報が取得できるようになります。
最後にこの部分について
def follow(user_id)
following.create(follower_id: user_id)
end
def unfollow(user_id)
following.find_by(follower_id: user_id).destroy
end
def following?(user_id)
following_user.include?(user_id)
end
end
ここではフォローするための関数,フォローを外すための関数,ユーザーを既にフォローしているかを調べる関数をそれぞれ作成しています。
これでフォロー機能で一番難しいアソシエーションの記述が終わりました!
ここまできたら、あとは基本的なMVCを理解していれば簡単です!
ルーティング
フォローしたり、フォローを外すためのアクションを定義する準備として、まずは以下のようにしてルーティングを行いましょう。
post 'follow/:id' => 'relationships#follow', as: 'follow'
delete 'unfollow/:id' => 'relationships#unfollow', as: 'unfollow'
続いて、「フォロー一覧」と「フォロワー一覧」のページを作るために、以下のルーティングも行っておきます。
resources :users, only: [:show] do
# 以下を挿入
get :following, :follower, on: :member
end
コントローラー
続いてrelationshipsコントローラーでのアクションの定義です。
まずはrelationshipsコントローラーを、ターミナルに以下のコードを打ち込んで作成しましょう。
$ rails g controller Relationships new
以下のようにして、ユーザーをフォローできるfollowアクションと、ユーザーへのフォローを外すことができるunfollowアクションを定義します。
class RelationshipsController < ApplicationController
def follow
current_user.follow(params[:id])
redirect_back(fallback_location: root_path)
end
def unfollow
current_user.unfollow(params[:id])
redirect_back(fallback_location: root_path)
end
end
続いて、usersコントローラーで「フォロー一覧」と「フォロワー一覧」を表示するために必要なアクションも以下のようにして記述しておきます。
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
# 以下を挿入
def following
@user = User.find(params[:id])
end
def follower
@user = User.find(params[:id])
end
end
あとはビューに記述していくだけです!!
ビュー
user#showページにフォローボタンやフォローを外すボタンを記述する時の例を載せておきます。
あくまで一例なので、自分のプロダクトに合うよう、適宜書き換えてください。
<% if user_signed_in? %>
<% unless @user == current_user %>
<% if current_user.following?(@user) %>
<%= link_to unfollow_path(@user.id), method: :delete, :style=>"color:#E0245E;" do %>
<i class="fas fa-heart"></i>
<% end %>
<% else %>
<%= link_to follow_path(@user.id), method: :post do %>
<i class="far fa-heart"></i>
<% end %>
<% end %>
<% end %>
<% end %>
以下は、「フォロー一覧」と「フォロワー一覧」に飛ぶためのリンクコードの例です。
<a href="<%= following_user_path(@user) %>">
<%= @user.following.count %> following
</a>
<a href="<%= follower_user_path(@user) %>">
<%= @user.follower.count %> followers
</a>
また、「フォロー一覧」と「フォロワー一覧」では以下を参考にして、適宜自分の表示させたい情報を記述していきましょう。
<% @user.following_user.each do |user| %>
<% user.name %>
<% end %>
<% @user.follower_user.each do |user| %>
<% user.name %>
<% end %>
これで完成です!お疲れ様でした!