Rails フォロー機能


①Model(3ステップ)

○必要なモデル(2つ)を作成

 1. USER (deviseで作成)
 2. Relationships
  rails g model Relationship follower_id:integer followed_id:integer

 create_table "relationships", force: :cascade do |t|
   t.integer  "follower_id"
   t.integer  "followed_id"
   t.datetime "created_at",  null: false
   t.datetime "updated_at",  null: false
 end

○アソシエーション

各モデルに追記。

Relationshipモデル [ Relationships.rb ]

belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"

*class_name: "モデル名" で、指定したモデルを参照した際のモデル名を変更できる。
Userモデルを「Follower」と「Followed」に分けるイメージ。

Userモデル [ User.rb ]

    # フォローしている
    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 :follower_user, through: :followed, source: :follower
    #フォローされている人
    has_many :following_user, through: :follower, source: :followed

Relationshipモデルも「follower」と「followed」に分けられる
foreign_keyを追加することで、それぞれRelationshipモデルを通して取得可能となる

それぞれsourceにしているモデルを通して
*through: :モデル名 指定したモデルを通して取得する
*source: :モデル名  関連するモデルを指定する

○メソッド定義(3つ) [ User.rb ]

  # 1. followメソッド = フォローする
  def follow(user_id)
   follower.create(followed_id: user_id)
  end

  # 2. unfollowメソッド = フォローを外す
  def unfollow(user_id)
   follower.find_by(followed_id: user_id).destroy
  end

  # 3. followingメソッド = 既にフォローしているかの確認
  def following?(user)
   following_user.include?(user)
  end

htmlで表示を条件分岐する際に使います。
 引数にuserが含まれていた場合、trueを返す=trueのときは「フォローを外す」を表示
*.include?(引数)メソッド: 対象の文字列に引数で指定した文字列が含まれているか検索して真偽値を返す

②Controller

○メソッド定義: Relationships Controller

def create #フォローする Userモデルで定義したfollowメソッド
    current_user.follow(params[:user_id])
    redirect_to request.referer #遷移前のURLを取得してリダイレクト
end

def destroy #フォローを外す Userモデルで定義したunfollowメソッド
    current_user.unfollow(params[:user_id])
    redirect_back(fallback_location: root_path)
end

def follower #follower一覧
    user = User.find(params[:user_id])
    @users = user.following_user
    # .follower_userメソッド :Userモデルで定義済
end

def followed #followed一覧
    user = User.find(params[:user_id])
    @users = user.follower_user
    # .follower_userメソッド :Userモデルで定義済
end

③Routing

create(follow)、destroy(unfollow)とfollows,followersの一覧用を追加

resources :users
 resource :relationships, only:[:create, :destroy]

 get 'follows' => 'relationships#follower'
 get 'followers' => 'relationships#followed'
end

④View

・フォローボタン(follow/unfollow)

フォローボタンを表示させたい部分にリンクを追加

 <% if current_user != user %>
  <% if current_user.following?(user) %>
    <%= link_to 'フォロー外す', user_relationships_path(user.id), method: :delete, class: "btn btn-default" %>
   <% else %>
    <%= link_to 'フォローする', user_relationships_path(user.id), method: :POST , class: "btn btn-primary"%>
   <% end %>
 <% end %>

・follower.follows一覧

relationshipsのなかに、follower.html.erb と follows.html.erbファイルを作成し、
各ファイルに下記の通り記載

<% if @users.count > 0 %> の条件分岐でUserがいる場合はeach文で表示し、
いない場合は ユーザーはいません を表示

 <% if @users.count > 0 %>
   <table class="table">
    <thead>
      <tr>
        <th>name</th>
       </tr>
     </thead>
     <tbody>
      <% @users.each do |user| %>
        <tr>
           <td><%= @user.name %></td>
           <td>フォロー数:<%= @user.follower.count %></td>
           <td>フォロワー数:<%= @user.followed.count %></td>
           <td>
            <% if current_user != @user %>
              <% if current_user.following?(@user) %>
                <%= link_to 'フォロー外す', user_relationships_path(@user.id), method: :delete, class: "btn btn-default" %>
               <% else %>
                <%= link_to 'フォローする', user_relationships_path(@user.id), method: :POST , class: "btn btn-primary"%>
               <% end %>
            <% end %>
          </td>
          <td><%= link_to "Show", @user %></td>
        </tr>
      <% end %>
    </tbody>
  </table>
<% else %>
  <p>ユーザーはいません</p>
<% end %>