見出し画像

フォロー機能の実装

1.目的

自分の気に入った人をフォローしたりフォローされたりできるようにする。

2.扱うgem・API

なし

3.ER図

スクリーンショット 2021-07-25 12.02.30

4.手順

①relationshipモデルの作成を行います。
terminalで以下のコマンドを実行します。

% rails g model Relationship

②作成されたマイグレーションファイルを以下のように編集します。

class CreateRelationships < ActiveRecord::Migration[6.0]
 def change
   create_table :relationships do |t|
     t.integer :follower_id, null: false
     t.integer :following_id, null: false
     t.timestamps
   end
   add_index :relationships, :follower_id
   add_index :relationships, :following_id
   add_index :relationships, %i[follower_id following_id], unique: true
 end
end

add_index :relationships, %i[follower_id following_id], unique: true
は、一度フォローしたユーザーを二度フォローしてしまわないようにするための一意の設定です。

relationshipsテーブルのfollower_id、following_idカラムにindexを貼ることでアルファベット順にfollower_id、following_idを並べ替え検索しやすいようにしてくれます。

③アソシエーションを定義します。
フォローするのとフォローされるのとで能動的関係と受動的関係で分けることをします。
ここでは、能動的関係をfollwerと呼び、受動的関係をfollowingと呼ぶことにしています。

○フォローすることを考えてやっていきます。
能動的関係であるfollwerからやっていきます。
まずあるユーザーA がユーザーBをフォローすることを考えます。
ユーザーAからフォローはたくさんでき、フォローされた側からはAさんしかいないので1対多の関係です。
ユーザーAは、follwersモデルを通してフォローを行います。
user.rbに以下を追記します。

  has_many :followers,
          class_name: 'Relationship',
          foreign_key: 'follower_id',
          dependent: :destroy,
          inverse_of: :follower

今回はAさんがfollwingしようとしているので、Aさんがrelationshipsテーブルにfollwing_idを登録しています。

has_many :followersだと、railsが外部キーにfollers_idを探してしまい、できないので外部キーを明示的に示す必要があります。Bさんが今回は、follwer_idを登録する役割を持っています。

relationsip.rbに以下を記します。

class Relationship < ApplicationRecord
 belongs_to :follower, class_name: 'User'
end

○フォローされることのみを考えてやっていきます。
この際、relationshipモデルをfollwingモデルとして置き換えます。

ユーザーAをフォローしている人は、たくさんいるのでhas_many
ユーザーAをフォローしている人にとってfollwingの対象は1人であるのでbelongs_to です。
relationship.rbに以下を追記します。

belongs_to :following, class_name: "User"

user.rbに以下を追記します。

  has_many :followings,
          class_name: 'Relationship',
          foreign_key: 'following_id',
          dependent: :destroy,
          inverse_of: :following

フォローされているユーザーAをfollwing_idを外部キーとして特定しています。

○フォローしているユーザーの集団を取り出します。
1人のユーザーにはたくさんの「フォローする/フォローされる」の多対多のリレーションがあります。
user.rbに以下を追記します。

has_many :following_users, through: :followers, source: :following

中間テーブルにfollwersテーブルを指定しています。その結果、user.following_users と打つだけで、userが中間テーブルfollers を取得し、その1つ1つのfollowing_idから、「フォローしているUser達」を取得できるようになります。

source: :followingはわかりやすくするためのものであるのでなくてもOK!!

○あるユーザーをフォローしているフォロワーを取り出します。
user.rbに以下の内容を追記します。

  has_many :follower_users, through: :followings, source: :follower

これでuser.follower_usersメソッドが使えるようになります。

○取得したデータを簡単に扱うためのメソッドを追記していきます。
user.rbに以下の内容を追記します。

 ##フォローするときのメソッド
 def follow(other_user_id)
   followers.create(following_id: other_user_id)
 end
 ##フォローしているのか確認のメソッド
 def following?(other_user)
   following_users.include?(other_user)
 end
 ##フォローを外すときのメソッド
 def unfollow(other_user_id)
   followers.destroy(following_id: other_user_id)
 end

④app/views/users/show.html.erbに以下の内容を追記します。

        <div class="follow">
          <%= render "follow_form" %>
         </div>

⑤app/views/usersディレクトリに_follw_form.html.erbファイルを作成します。
次に_follw_form.html.erbに以下のように編集します。

<%if user_signed_in?%>
<%unless @user == current_user%>
 <% if current_user.following?(@user)%>
   <%=button_to "フォロー中",relationship_path,method: :delete ,class: "fllowig-btn" %>
 <%else%>
   <%= form_for(@user.followers.build) do |f| %>
     <%= hidden_field_tag :follow_id, @user.id %>
     <%= f.submit "フォロー" ,class: "follow-btn" %>
   <% end %>
 <%end%>
<%end%>
<%end%>

⑥ルーティングの設定を行います。
routes.rbを以下のように追記します。

 resources :users, only: [:show, :index, :edit, :update, :destroy] do
   member do
     get :followings, :followers, :follow_review, :good_review
   end
 end

どの人をフォローしているのか、誰にフォローされているのか、フォローした人のレビュー表示、いいねしたものの表示のアクションを加える。

⑦users_controller.rbに以下を追記します。

class UsersController < ApplicationController
 before_action :set_user, except: [:index]
 def followings
   @followings = @user.following_users
 end
 def followers
   @followers = @user.follower_users
 end
 def follow_review
   @followings = @user.following_users
 end
 def good_review
 end
   private

 def set_user
   @user = User.find(params[:id])
 end
 end

⑧app/views/users/show.html.erbに以下の内容を追記します。

        <div class="user-item">
         <div class="user-nick">
           <%="#{@user.nickname}のホーム" %> 
         </div>  
         <div class="follow">
           <%= render "follow_form" %>
            
           <p class="follow-count">
             <%= link_to "#{@user.followers.count} #{User.human_attribute_name("フォロー")}", followings_user_path %> <%= link_to "#{@user.followings.count} #{User.human_attribute_name("フォロワー")}", followers_user_path %>
           </p> 
           
         </div>     
       </div>
       <div class="user_matome">
           <%=link_to "フォローユーザーレビュー",follow_review_user_path ,class:"follow"  %>
           <%=link_to "いいねしたレビュー", good_review_user_path, class:"good-r" %>
       </div>

フォローした人へ遷移、フォローされた人へ遷移、フォローしたユーザーレビューへの遷移、いいねしたレビューへの遷移。

⑨app/views/usersディレクトリ以下にfolloers.html.erb、followings.html.erb、follow_review.html.erb、good_review.html.erbファイルを作成します。

○folloers.html.erbを以下のように編集します。

<%=render "shared/header"%>
<div class='main'>
       <div class="follower-list">
         <h1>フォロワー</h1>   
       </div>
       <div class='item-contents'>
         <h2 class='title'>Pick Up</h2>
         <div class="subtitle" >
           user list
         </div>
      
         <ul class='item-lists'>
     
           <% @followers.each do |user| %>
           <div class="comment-profile">
               <div class="profile">
                 <% if user.image.attached? %>
                   <%= image_tag user.image, class: "user-icon3" %>
                 <% else %>
                   <%= image_tag "willy.png", alt: "user-icon", class: "user-icon3" %>
                 <% end %>
               </div>
               <div  class="name-file">
                 <%= link_to user.nickname, user_path(user.id) ,class: "message-nickname" %>
                 
               </div>
           </div>
           <%end%>
         </ul>
         
         <%= link_to t("button.back"), user_path %>
         
       </div> 
         
</div>
<%=render "shared/footer"%>

○followings.html.erbを以下のように編集します。

<%=render "shared/header"%>
<div class='main'>
       <div class="follow-list">
         <h1>フォロー中</h1>   
       </div>
       <div class='item-contents'>
         <h2 class='title'>Pick Up</h2>
         <div class="subtitle" >
           user list
         </div>
      
         <ul class='item-lists'>
     
           <% @followings.each do |user| %>
           <div class="comment-profile">
               <div class="profile">
                 <% if user.image.attached? %>
                   <%= image_tag user.image, class: "user-icon3" %>
                 <% else %>
                   <%= image_tag "willy.png", alt: "user-icon", class: "user-icon3" %>
                 <% end %>
               </div>
               <div  class="name-file">
                 <%= link_to user.nickname, user_path(user.id) ,class: "message-nickname" %>
                 
               </div>
           </div>
           <%end%>
         </ul>
         
         <%= link_to t("button.back"), user_path %>
         
       </div> 
         
</div>
<%=render "shared/footer"%>

○follow_review.html.erbを以下のように編集します。

<%=render "shared/header"%>
<div class='main'>
       <div class="user-item">
         <div class="user-nick">
           <h1>フォローユーザーレビュー一覧</h1> 
         </div>      
       </div>
       <div class="user_matome">
           <%=link_to "いいねしたレビュー",good_review_user_path ,class:"follow"  %>
           <%=link_to "#{@user.nickname}のホームへ", user_path, class:"good-r" %>
         </div>
       <%if user_signed_in? && current_user.id == @user.id%>
       <div class="myrank">
          
       </div> 
       <%end%>  
       <div class='item-contents'>
         <h2 class='title'>Pick Up</h2>
         <div class="subtitle" >
           follow user review
         </div>
         <ul class='item-lists'>
     
           <% @followings.each do |user| %>
           <%user.items.page(params[:page]).per(16).each do |item|%>
           <li class='list'>
             <%= link_to item_path(item.id),class:"link" do %>
             <div class='item-info'>
               
               <%= item.name%>
               
             </div>
             <div class='item-img-content'>
               
               <%if item.image.attached? %>
                 <%= image_tag item.image, class: "item-img"  %>
               <%else%>
                 <%= image_tag 'soundreview2.png', class: "item-img"  %>
               <%end%>  
             </div>
             <div class="profile-all">
             <div class="profile2">
                   <% if item.user.image.attached? %>
                     <%=link_to user_path(item.user.id),class:"user-icon4" do %>
                       <%= image_tag item.user.image, class: "user-icon4" %>
                     <%end%>  
                   <% else %>
                     <%=link_to user_path(item.user.id) ,class:"user-icon4" do %>
                       <%= image_tag "willy.png", alt: "user-icon", class: "user-icon4" %>
                     <%end%>
                   <% end %>
                 </div>
             <div class="item-create">
               <div class="item-user-name">
                 <%= link_to item.user.nickname, user_path(item.user.id) %>
               </div> 
               <div class="created_at" >
                   <%=item.created_at.strftime('%Y/%m/%d')%>
               </div>
             
             </div>
             <div class="good-county">
                   <i class="fas fa-thumbs-up"></i>      <%=item.likes.count%>
             </div> 
             </div>
             
             
             
             <% end %>
             
           </li>
           <%end%>
           <% end %>
         </ul>
         
        
       </div>
       <%= link_to new_item_path, class: 'item-btn' do %>
       <span class='item-btn-text'>Post Review</span>
       <%= image_tag 'icon_camera.png' , size: '79x50' ,class: "item-btn-icon" %>
       <% end %>  
         
</div>

<%= render "shared/footer" %>

○good_review.html.erbを以下のように編集します。

<%=render "shared/header"%>
<div class='main'>
       <div class="user-item">
         <div class="user-nick">
           <h1>マイグッドレビュー一覧</h1> 
         </div>      
       </div>
       <div class="user_matome">
           <%=link_to "フォローユーザーレビュー",follow_review_user_path ,class:"follow"  %>
           <%=link_to "#{@user.nickname}のホームへ", user_path, class:"good-r" %>
         </div>
       <%if user_signed_in? && current_user.id == @user.id%>
       <div class="myrank">
          
       </div> 
       <%end%>  
       <div class='item-contents'>
         
         <h2 class='title'>Pick Up</h2>
         <div class="subtitle" >
           my good review
         </div>
         
         <ul class='item-lists'>
     
           <%@user.like_items.each do |post|%>
           
           <li class='list'>
             <%= link_to item_path(post.id),class:"link" do %>
             <div class='item-info'>
               
               <%= post.name%>
               
           </div>
             <div class='item-img-content'>
               
               <%if post.image.attached? %>
                 <%= image_tag post.image, class: "item-img"  %>
               <%else%>
                 <%= image_tag 'soundreview2.png', class: "item-img"  %>
               <%end%>  
             </div>
             <div class="profile-all">
             <div class="profile2">
                   <% if post.user.image.attached? %>
                     <%=link_to user_path(post.user.id),class:"user-icon4" do %>
                       <%= image_tag post.user.image, class: "user-icon4" %>
                     <%end%>  
                   <% else %>
                     <%=link_to user_path(post.user.id) ,class:"user-icon4" do %>
                       <%= image_tag "willy.png", alt: "user-icon", class: "user-icon4" %>
                     <%end%>
                   <% end %>
                 </div>
             <div class="item-create">
               <div class="item-user-name">
                 <%= link_to post.user.nickname, user_path(post.user.id) %>
               </div> 
               <div class="created_at" >
                   <%=post.created_at.strftime('%Y/%m/%d')%>
               </div>
             
             </div>
             <div class="good-county">
                   <i class="fas fa-thumbs-up"></i>      <%=post.likes.count%>
             </div> 
             </div>
             
             
             <% end %>
             
           </li>
         
           <% end %>
           
         </ul>
         
         
       </div> 
       <%= link_to new_item_path, class: 'item-btn' do %>
       <span class='item-btn-text'>Post Review</span>
       <%= image_tag 'icon_camera.png' , size: '79x50' ,class: "item-btn-icon" %>
       <% end %> 
         
</div>

<%= render "shared/footer" %>

5.まとめ

以上でフォロー機能の実装は完成でーす!
以下のように表示されたらOK!です!
ありがとうございました。

画像2


この記事が気に入ったらサポートをしてみませんか?