LoginSignup
2
4

More than 3 years have passed since last update.

Rails フォロー機能まとめ

Last updated at Posted at 2021-03-06

記事作成理由

1:プログラミング初学者の私が見返すため。
2:私のような初学者の学習に少しでも役立てればと思ったため。

※誤った記述や知識があればお知らせいただけると幸いです。


開発環境

・ruby: 2.6.3
・rails: 5.2.4.5
・OS: macOS Catalina ver10.15.7
・Cloud9


前提条件

・Userモデル,usersテーブルがある

・Bootstrap導入済み
・deviseを使ってログイン機能実装済み

・CRUD機能の基礎について学習済み


実装の流れ

1:Relationshipモデルの作成
2:中間テーブル(relationshipsテーブル)の作成
3:アソシエーションの設定
4:アクションの定義
5:Viewファイルに記述


1:Relationshipモデルの作成

ターミナル.
rails g model Relationship


2:中間テーブル(relationshipsテーブル)の作成

マイグレーションファイルにカラムを追加

db/migrate/○○○○_create_relationships.rb
class CreateRelationships < ActiveRecord::Migration[5.2]
  def change
    create_table :relationships do |t|

      t.references :follower, foreign_key: {to_table: :users}
      t.references :followed, foreign_key: {to_table: :users}

      t.timestamps

      t.index [:follower_id, :followed_id], unique: true
    end
  end
end
ターミナル.
rails db:migrate

マイグレーションファイルの反映忘れずに!

ーー解説ーー

relationshipsテーブルの中身はこのようになります。

カラム タイプ オプション
follower_id integer foreign_key: {to_table: :users}
followed_id integer foreign_key: {to_table: :users}

relationshipsテーブルは中間テーブルであるため、
followerカラムとfollowedカラムはt.referencesをつけて生成します。


(ポイント!)
今回生成したカラム (follower_id と followed_id) は自由に命名することができます。
他記事ではfollowing_idやfollows_idなど様々な命名がされています。自分が1番理解しやすいように命名しましょう。
(色々な記事を調べて、様々な命名を見ると頭が混乱します。僕だけかもしれませんが・・・)


また、follower、followedどちらも外部キーとして設定します。 foregin_key(外部キー)
ここでforegin_key :trueとしてしまうと、followersテーブルやfollowedsテーブルなど存在しないテーブルを参照してしまいエラーになります。
それを避けるために{to_table: :users}として、usersテーブルのidを参照するよう紐づかせています。


t.index [:follower_id, :followed_id], unique: true
これにより、2つのカラムの中身が同じ組み合わせでデータを保存することを防ぎます。
=同じユーザーを2回フォローできないようにしています。

中間テーブル概念についてはこちら2つの記事がとても参考になりました。
【Railsでフォロー機能を作ろう】
【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル


3:アソシエーションの設定

relationship.rb

class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"

  validates :follower_id, presence: true
  validates :followed_id, presence: true
end


ーー解説ーー

■2,3行目
relationshipsテーブルでforeign_keyとしてfollower_id , followed_id を設定しているため、モデル名もFollower, Followedとします。

しかし、FollowerモデルもFollowedモデルも実在しない為、class_name: "User"とオプションを追記し、関連するモデル(本当のモデル名)を指定します。
このとき、"User"は文字列扱いになるため、ダブルクォーテーションで囲んでいます。

■4,5行目
validatesの説明は割愛します。
(わからない方は「バリデーション」)でお調べください。

user.rb
class User < ApplicationRecord

  # フォローするユーザーから見た中間テーブル
  has_many :active_relationships,
                      class_name: "Relationship",
                      foreign_key: "follower_id",
                      dependent: :destroy

  # フォローされているユーザーから見た中間テーブル
  has_many :passive_relationships,
                      class_name: "Relationship",
                      foreign_key: "followed_id",
                      dependent: :destroy

  # 中間テーブルactive_relationshipsを通って、フォローされる側(followed)を集める処理をfollowingsと命名
  # フォローしているユーザーの情報がわかるようになる
  has_many :followings, through: :active_relationships, source: :followed

  # 中間テーブルpassive_relationshipsを通って、フォローする側(follower)を集める処理をfollowingsと命名
  # フォローされているユーザーの情報がわかるようになる
  has_many :followers, through: :passive_relationships, source: :follower

end


ーー解説ーー

ここが鬼門です。がんばっていきましょう。
4つのhas_manyがありますが、上から2つずつに分けて説明します。

まず中間テーブルの見方を2つに分けます。
1:フォローするユーザーから見た視点
2:フォローされるユーザーから見た視点

本来であれば has_many :relationships とすればいいのですが、
見方が2つあるため、2通りの定義をしなければなりません。

そのため、ここでは
フォローする側active_relationships と命名し、(行目)
フォローされる側passive_relationships と命名しています。(行目)

※先ほどuser.rbで作成したモデル(Follower,Followed)と同様、これらも好きに命名することができます。
他記事を見て比較する時など、命名が異なるケースがあるため、注意しましょう。

そして、参照元のモデルはどちらもRelationshipモデルであるため、
class_name: "Relationship"とオプションを追加します。

しかし、このままではRelationshipsテーブルのどちら(follower , followed)を参照すればいいのかがわかりません。
そこで、下記のように指定してあげます
active_relationshipsは、foreign_key: "follower_id"を参照
passive_relationshipsは、foreign_key: "followed_id"を参照


dependent: :destroyは、とあるUserが削除された時にそれに紐づくrelationshipのデータも削除されるようにしています。

次に、こちらの文ですが、
has_many :followings, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower

これらはコードのコメントに記載の通り、定義したテーブルを通って(through)、sourceで指定したモデルの参照元からデータを集めることができます。

これにより
@user.followings (userがフォローしている人の情報)

@user.followers(userがフォローされている人の情報=userをフォローしている人の情報)
といった表記が可能になります。

※くどいようですが、、followingsfollowersも好きに命名することができます。
他記事を見て比較する時など、命名が異なるケースがあるため、注意しましょう。

これでアソシエーションは完成です。

4:アクション定義

user.rb と relationships.controller.rbにアクションを定義します。

user.rb
#下記3つのアクションを追記
class User < ApplicationRecord      

  # フォローする
  def follow(user_id)
    active_relationships.create(followed_id: user_id)
  end

  # フォローを外す
  def unfollow(user_id)
    active_relationships.find_by(followed_id: user_id).destroy
  end

  # すでにフォローしているのか確認
  def following?(user)
    followings.include?(user)
  end
end
relationships_controller.rb
class RelationshipsController < ApplicationController
  before_action :authenticate_user!

  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

  # フォローしている人一覧
  def follower
    user = User.find(params[:user_id])
    @users = user.followings
  end

  # フォローされている人一覧
  def followed
    user = User.find(params[:user_id])
    @users = user.followers
  end

end


5:Viewファイルに記述

ここからはあくまで例になります。
定義している変数などによって記述の仕方は変わります。
※一部抜粋

ーーフォロー数、フォロワー数表示、フォローボタンーー

users/index.html.erb
<tbody>
  <% @users.each do |user| %>
  <tr>
    <td><%= attachment_image_tag user, :profile_image, :fill, 40, 40, fallback: "no_image.jpg", size:"40x40" %></td>
    <td><%= user.name %></td>
    <td><%= "フォロー数: #{user.active_relationships.count}" %></td>
    <td><%= "フォロワー数: #{user.passive_relationships.count}" %></td>
    <td>
      <% if current_user.id != user.id %>
        <% if current_user.following?(user) %>
        <%= link_to 'フォローを外す', user_relationships_path(user.id), method: :DELETE %>
        <% else %>
        <%= link_to 'フォローする', user_relationships_path(user.id), method: :POST %>
        <% end %>
      <% end %>
    </td>
    <td><%= link_to "Show", user_path(user.id) %></td>
  </tr>
  <% end %>
</tbody>



ーーフォローしている人一覧ーー

relationships/follower.html.erb
<tbody>
  <% @users.each do |user| %>
    <tr>
      <td><%= attachment_image_tag user, :profile_image, :fill, 40, 40, fallback: "no_image.jpg" , size:"40x40" %></td>
      <td><%= user.name %></td>
      <td>フォロー数:<%= user.followings.count %></td>
      <td>フォロワー数:<%= user.followers.count %></td>
      <td><%= link_to "show", user_path(user.id) %> </td>
    </tr>
  <% end %>
</tbody>



ーーフォローされている人一覧ーー

relationships/followed.html.erb
<tbody>
  <% @users.each do |user| %>
    <tr>
      <td><%= attachment_image_tag user, :profile_image, :fill, 40, 40, fallback: "no_image.jpg" , size:"40x40" %></td>
      <td><%= user.name %></td>
      <td>フォロー数:<%= user.followings.count %></td>
      <td>フォロワー数:<%= user.followers.count %></td>
      <td><%= link_to "show", user_path(user.id) %> </td>
    </tr>
  <% end %>
</tbody>



以上です。
閲覧ありがとうございました!

誤字、脱字、誤り等ございましたら教えていただけると幸いです。

2
4
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
2
4