LoginSignup
2
2

More than 1 year has passed since last update.

【Rails】フォロー機能を小5でもわかる様に、やさしく解説します!

Posted at

フォロー機能を実装しよう

自分のWEBアプリケーションにフォロー機能を
付けたい人多いのではないでしょうか?

ただ、フォロー機能は中間テーブルを用いて多対多を行っているため
初めての人にとっては分かりづらいのも事実。

特にアソシエーションの部分についてを詳しく説明していくので
初学者でも分かりやすく理解できるよう解説していきます。

多対多とは?

まず実装する前に多対多についてを説明していこうと思います!
この概念を理解していないと???が浮かんでくるので
しっかりとおさえてくださいませ!

通常であれば1対多、多対1のような状態であることが多いです。

例) 1対多(投稿機能)
ー--------------ー-----

・ユーザー >> 投稿 = 1対多の関係です。
 1人のユーザーに投稿はたくさんつきます。

・投稿 >> ユーザー = 多対1の関係です
 その一方一つの投稿は一人のユーザーに決まります。

ー--------------ー-----

まずここまで理解できますでしょうか?
次に多対多の関係を見ていきます。



例) 多対多(フォロー機能)
ー--------------ー-----

・ユーザーA >> ユーザーB
        ユーザーc
        ユーザーd
ユーザーAはたくさんの人をフォローしてます。

ー--------------ー-----

・ユーザーB >> ユーザーA
        ユーザーC
        ユーザーd
ユーザーBはたくさんの人をフォローしています。

ー--------------ー-----

このように片方が1対多にならない関係を
多対多といいます。

フォロー機能がその良い例ですね!
ここまで簡単に多対多について説明をしました。

それでは次から実装にはいっていきましょう!

実装

⓵ マイグレーションファイル
⓶ モデル
➂ ルーティング
⓸ コントローラー
⓹ ビューファイル

それでは、いってみよー!

ステップ➀:マイグレーションファイル

一番名グレーションファイルからコードを見ていきましょう!

まずはコードをお見せしますね♪

php/db/migrate/...create_user.rb

class CreateFavorites < ActiveRecord::Migration[5.2]
  def change
    create_table :favorites do |t|
      t.integer :follow_id, null: false
      t.integer :follower_id, null: false
      t.timestamps
    end
  end
end

※ userテーブルは既に作成済みとして進みます。

ちなみにnull falseというのは空欄である場合に
登録をさせないようにするための記述となりますよ。

空欄で保存させないためにも記載をしておきましょう!

できたらrails db migrateを実行です!

ステップ➁:モデルファイル

次にモデルファイルにコードを記載していきます。
これが少し難しいので理解をしていく必要があります。

php/app/models/user.rb
class User < ApplicationRecord

  #フォロー機能のアソシエーション
  #follower_id=自分
  #follow_id=相手

上パート
----------------
 #自分がフォローしたり、アンフォローしたりするための記述
  has_many :favorites, class_name: "Favorite", foreign_key: "follower_id", dependent: :destroy
  #sourceは本当はfollow_idとなっていてカラム名を示している。
 #フォロー一覧を表示するための記述
  has_many :followings, through: :favorites, source: :follow
----------------

下パート
----------------  
  has_many :reverse_favorites, class_name: "Favorite", foreign_key: "follow_id", dependent: :destroy
  #フォロワー一覧を表示するための記述
  has_many :followers, through: :reverse_favorites, source: :follower
----------------

分かりやすくするために点線と上パートと下パートと記述をしました。
二つのパートを用意してはいますが、フォローとアンフォローだけであれば
上パートの➀だけあれば実はフォローとアンフォローの機能はできてしまいます。

しかしなぜ、⓶⓷⓸はあるの?というお話になると思いますが
これはフォローとフォロワーの一覧を表示するために必要な機能なんですね。

フォローとアンフォローだけであれば➀だけつくればいいですが
フォロー機能をつくったらフォロー数とフォロワー数や一覧なども
みたくなりますよね?

なので、⓶⓷⓸を記述しているわけです。

さて、本題にもどりますが、これらがどんな役割かというと

➀has_many :favorites, class_name: "Favorite", foreign_key: "follower_id", dependent: :destroy
⓷has_many :reverse_favorites, class_name: "Favorite", foreign_key: "follow_id", dependent: :destroy

➀と➂の最初のfavoritesやreverse_favoritesというのは
仮の名前をつけているわけなんですね。

なぜかというと、1対多であればhas_manyというのは、
上パートと下パートと分ける必要がないので、
一つのカラムに指定をすればいいのですが
多対多という場合は中間テーブルというのを用いて
無理やり?テーブル同士の繋がりを実現することになります。

つまり疑似的にuserテーブルを二つあるかのような設定を作り上げます。

一覧表示を表示する際は、
自分がフォローしている人の一覧を表示するためと
自分のことをフォローしてくれている人の一覧を表示するために
上パートと下パートを用意することでそれを実現できます。

しかし現実的にはfavoriteテーブルというのは
一つしかないので、仮の名前を1つ目で作成しているわけなんですね。

次にclass_nameというのは、これが本当のテーブルの名前です。

foreign_keyというのは、外部キー指定というもので
このカラムに保存してくださいとアソシエーションで指定できます。

dependent destroyというのは、
userが削除されたらそれに紐づいているアソシエーションもすべて削除されます。

次にこちらの解説をします。
これらは一覧表示をするために必要になります。
⓶has_many :followings, through: :favorites, source: :follow
⓷has_many :reverse_favorites, class_name: "Favorite", foreign_key: "follow_id", dependent: :destroy

ここでのポイントはthroughとsourceという記載があることです。
throughはfavoritesテーブルを通してという意味になります。
sourceというのは実際のカラムになります。
sourceであればfollowカラムを参照するということなので
favoritesテーブルを通じて自分がフォローしている人すべての人を取得できます。

ちなみにですが、sourceは出口と覚えていただくと分かりやすいと思います。
➀has_many :favorites, class_name: "Favorite", foreign_key: "follower_id", dependent: :destroy
⓶has_many :followings, through: :favorites, source: :follow

この例で行くと、foreign_keyがfollower_idで入口で、source: :followが出口です。
従いまして、自分がフォローしている人一覧を表示する際は
まず自分のidなので、follower_idを探してきて、それに紐づくsourceのfollowキーを
railsが引っ張ってくれるわけですね。

自分のことをフォローしてくれている人を表示したい時は
逆の場合も同じように考えればいいわけです!

php/app/models/favorite.rb

class Favorite < ApplicationRecord
  # userへのアソシエーション
  # class_nameがuserモデルとリンクしている(class_nameは大文字でなければNG)
  belongs_to :follow, class_name: "User"
  belongs_to :follower, class_name: "User"

1対多であれば、上記の場合であれば
belongs_to: userという記述になりますが
favoriteモデルの場合は多対多になるため
belongs_toが二つ必要になります。

なぜかというと、userモデルでsourceを指定しているので
書かないと繋がりをつくれなくなってしまうからですね。

class_nameはユーザーモデルを示しています。

さて、フォロー機能で一番難しいといっても過言ではない
モデル間のアソシエーション機能については完了となります。

次はルーティングをみていくとしましょう。

ステップ⓷:ルーティングファイル

それでは、ステップ⓷はルーティングファイルを記載していきます。

php/config/routes.rb
Rails.application.routes.draw do

    # users用のURL用に設定
    resources :users, only: [:edit, :update] do
      # フォロー機能はuserにネストさせている
      resource :favorites, only: [:create, :destroy]
      get 'followings' => 'favorites#followings', as: 'followings'
      get 'followers' => 'favorites#followers', as: 'followers'
    end

resource :favorites, only: [:create, :destroy]は、
フォローとアンフォローをするだけあればOKなのでcreateとdestroyだけを記述しています。

そしてその下は、一覧表示するための記述です。

get 'followings' => 'favorites#followings', as: 'followings'
get 'followers' => 'favorites#followers', as: 'followers'

user_idに基づいて、フォローしたりアンフォローしたりするので
user_idネストをすることでurlにidを含めることができます。

ステップ⓸:コントローラー

さて、マイグレーションファイル、モデル、ルーティングが完了したら
次はコントローラーを記述していきます。

php/app/controllers/favorites_controller.rb
class FavoritesController < ApplicationController

  # フォローアンフォロー処理
  def create
    # params[:user_id]これはリンクから送られてきたuser_idをparamsで受け取っている
    # そして受け取った値をモデルのメソッドに受け渡している
    @user = User.find(params[:user_id])
    current_user.follow(params[:user_id])
    #フォローの通知機能
    @user.create_notification_follow!(current_user)
    redirect_to request.referer
  end

  def destroy
    current_user.unfollow(params[:user_id])
    redirect_to request.referer
  end

  # フォローフォロワー一覧処理
  def followings
    user = User.find(params[:user_id])
    @users = user.followings
  end

  def followers
    user = User.find(params[:user_id])
    @users = user.followers
  end
end

まずは、フォローアンフォローの機能からみていきましょう。

def create
current_user.follow(params[:user_id])
end

def destroy
current_user.unfollow(params[:user_id])
redirect_to request.referer
end

フォロー機能はこれだけでできます。
crateとdestroyで何をやっているかというと
current_userでfollowメソッドを走らせてと行っています。
そしてfollowにはuser_idをひきわたしてあげます。

このfollowというのは何かというと
モデルに自作することができるメソッドのことです。

モデルにメソッドを書くことでコントローラー内の
記述がスッキリするのでモデルにメソッドをかけるのであれば
モデルにメソッドを記述していきましょう!

モデル内に書いたメソッドはこれです。

php/app/controllers/favorites_controller.rb
class User < ApplicationRecord

  def follow(user_id)
    favorites.create(follow_id: user_id)
  end

  def unfollow(user_id)
    favorites.find_by(follow_id: user_id).destroy
  end

さて、コントローラーの記述に戻りますが
下記のfollowというのと、モデルのfollowが一致していますね。

current_user.follow(params[:user_id])

def follow(user_id)
favorites.create(follow_id: user_id)
end

つまり、コントローラーのfollowはモデルの自作したfollowメソッドを
引っ張ってきているわけなんですね。

通常であればコントローラーに.updateや.createと
記載をすると思うのですがモデルにモデルにかけるので
モデルに.createを記載していきます。

コントローラーでcurrent_userという記述をしているので
現在ログインしているユーザーの情報をもったまま
モデルに書いたメソッドにログインユーザーの情報を引き渡して
モデルに書いた処理を実行することができます。

フォローボタンを押した際にフォローした相手のuser_idが
follow_idカラムに保存され、follower_idにはcurrent_userのidが
自動で入るようにrailsがおこなってくれるわけですね。

favorites.create(follow_id: user_id)

逆も同じような考えて削除するときも同じように削除を行います。

# フォローフォロワー一覧処理
def followings
user = User.find(params[:user_id])
@users = user.followings
end

def followers
user = User.find(params[:user_id])
@users = user.followers
end

フォロー一覧をする際にはUser.findをまずして
誰がuser_idなのかを突き止めてから
user.followingsでアソシエーションをして
user_idがフォローしている人一覧を取得します。

これにてコントローラーの解説は完了となります。
最後はviewファイルをみていきますよ!

ステップ⑤:viewファイル

最後はいよいよビューファイルをみていきます!

まずは、フォロー機能とフォロー一覧機能で
分けてみていきましょう!

php/app/views/users/friends.html.erb

<% @friends.each do |friend| %>
    <tr>
        <td class="text-center"><%= link_to friend_posts_path(friend) do %>
          <%= attachment_image_tag friend, :profile_image, fallback: "no_image.jpg", size: '70x70', class:"mt-3 rounded-circle"; %>
        <% end %>
        </td>
        <td class="text-center align-middle"><%= friend.nickname %></td>
        <td class="text-center align-middle"><%= friend.height %></td>
        <td class="text-center align-middle"><%= friend.weight %></td>                
        <td class="text-center align-middle"><%= friend.introduction %></td>  
        <!--ログインユーザーでない場合にフォローボタン表示-->
        <% if friend != current_user %>
            <td class="text-center align-middle"><% if current_user.following?(friend) %>
            <%= link_to "Unfollow", user_favorites_path(friend), method: :delete %>
        <% else %>
            <%= link_to "Follow", user_favorites_path(friend), method: :post %>
        <% end %>
            </td>
        <% end %>
        </td>
    </tr>
<% end %>

eachの@friendsは、userコントローラー内のfriendsコントローラーで
index(または自作)アクション内に指定をしたインスタンス変数を指定してあげます。

php/app/controllers/users_controller.rb

  def friends
    @friends = User.all
  end

eachではUserの情報が入っているのでブロック変数のfriendを
link_toで当ててあげましょう。

<% if friend != current_user %>

<% if current_user.following?(friend) %>
<%= link_to "Unfollow", user_favorites_path(friend), method: :delete %>
<% else %>
<%= link_to "Follow", user_favorites_path(friend), method: :post %>
<% end %>

フォローアンフォロー機能を作成するには、link_toを記載して
methodでdeleteかpostを指定してあげることで、
favoritesコントローラーのfollow、unfollowアクションを
走らせることができます。

そして、もう一つの肝となるのが、
フォロー機能を判定するメソッドです。

<% if current_user.following?(friend) %>

これはuserモデルにこのメソッドを記載しています。
followingsは、アソシエーションになるので
followingsはfollowカラムを参照するので
自分がフォローしているユーザーを探すことができます。

(user)の引数には相手のidが入ってくるため
<% if current_user.following?(friend) %>
でcurrent_userと指定していることで
自分のidがフォローしているかincludeで判定をします。

# 引数そのものを検索する
# フォローを判定しているメソッド
def following?(user)
followings.include?(user)
end

自分が相手をフォロしていればunfollowを表示できるようになり
そうでなければfollowと表示をviewの分岐で行います。

これにて、フォロー機能は完成です!

まとめ

いかがでしたでしょうか?

フォロー機能は少し複雑にはなりますが
ポイントはアソシエーションの理解になります。

ここさえ掴めれば自分でも理解に落とし込めるはずです。

何度も復習してみてくださいね!

それでは、ちゃお!

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