3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails】フォロー機能

Last updated at Posted at 2020-03-11

#フォロー機能
##準備
・userテーブル
・relationshipsテーブル(usersテーブルとusersテーブルの間に入るテーブル)

##relationshipsのマイグレーションファイルを編集&実行

$ rails g model Relationship

relationshipsモデルを作成し、マイグレーションファイルを編集&実行します。

db/migrate/年月日時_create_relationships.rb
class CreateRelationships < ActiveRecord::Migration[5.0]
  def change
    create_table :relationships do |t|
      t.references :user, foreign_key: true
      t.references :follow, foreign_key: { to_table: :users }

      t.timestamps

      t.index [:user_id, :follow_id], unique: true
    end
  end
end

image.png
上記がrelationshipsテーブルのカラムになります。

user_idテーブルとfollow_idテーブルを関連づけるため、t.referencesと記載する必要があります。
外部キーを設定するためにforeign_key: trueを追記する。

followキーの参照テーブルとしては{ to_table: :users }を追記します。
foreign_key: tureだと無いはずのフォローテーブルを参照してしまいます。

t.index [:user_id, :follow_id], unique: trueはユーザIDとフォローIDのペアで重複して保存されるのを防いでいる。

rails db:migrateでマイグレーションファイルを実行する。

##relationshipsモデルとuserモデルのアソシエーションを書く

app/models/relationship.rb
class Relationship < ApplicationRecord
  belongs_to :user
  belongs_to :follow, class_name: 'User'

  validates :user_id, presence: true
  validates :follow_id, presence: true
end

class_name: 'User'と補足設定することで、Followクラスという本来無いクラスへの参照を防いでいます。
要は「followモデルは存在しないため、userモデルにbelongs_toをする」ということになります。

app/models/user.rb
class User < ApplicationRecord
  has_many :relationships
  has_many :followings, through: :relationships, source: :follow
  has_many :reverse_of_relationships, class_name: 'Relationship', foreign_key: 'follow_id'
  has_many :followers, through: :reverse_of_relationships, source: :user
end
has_many :followings, through: :relationships, source: :follow

has_many :followingsこの部分はfollowingクラス(モデル)を架空で作成しています。
そのあとにthrough :relationshipsと記載しrelationshipsテーブルを中間に設定しています。

source: :followは「relationshipsテーブルのfollow_IDを参考にしてfollowingsモデルにアクセス」を指定しています。

なので、user.followingsと打つだけでuserが中間テーブルrelationshipsを取得し、1つ1つのrelationshipsのfollow_idから「フォローしているユーザ達」を取得しています。

###次にフォロワー(フォローされているuser達)を取って来るための記述
3行目のhas_many :reverse_of_relationshipshas_many :relationshipsの「逆方向」の意味になります。
user_idを入り口にfollow_idという出口から出てfollowingsテーブルからフォローしてくれる人のデータを取ってくる。

foregin_key = 入口
source = 出口

この概念を持っておくと理解しやくなります。

has_many :followersと命名していますがfollowersクラスはありません。

through: :reverse_of_relationshipで「中間テーブルはreverses_of_relationshipにしてね」と設定し、source: :userで「出口はuser_id、userテーブルから自分をフォローしているuserをとってくる」と設定している。

###userモデルにフォロー機能のメソッドを記載する
userモデルにフォロー機能メソッドを記載する。

app/models/user.rb
class User < ApplicationRecord
  has_many :relationships
  has_many :followings, through: :relationships, source: :follow
  has_many :reverse_of_relationships, class_name: 'Relationship', foreign_key: 'follow_id'
  has_many :followers, through: :reverse_of_relationships, source: :user

  def follow(other_user)
    unless self == other_user
      self.relationships.find_or_create_by(follow_id: other_user.id)
    end
  end

  def unfollow(other_user)
    relationship = self.relationships.find_by(follow_id: other_user.id)
    relationship.destroy if relationship
  end

  def following?(other_user)
    self.followings.include?(other_user)
  end

end
  def follow(other_user)
    unless self == other_user
self.relationships.find_or_create_by(follow_id: other_user.id)
    end
  end

unless self == other_userによってフォローしようとしているother_userが自分自身では無いかを検証している。selfにはuser.follow(other)を実行したときuserが代入されます。つまり実行したUserのインスタンスがselfになります。

def following?ではself.followingsによりフォローしているUser達を取得し、include?(other_user)によってother_userが含まれていないかを確認している。含まれている場合はtrue、含まれていなければfalseを返す。

def followではunless self == other_userによってフォローしようとしている。ohter_userが自分自身では無いかを検証しています。
selfにはuser.follow(other)を実行したときuserが代入されます。実行したUserのインスタンスがselfになります。

self.relationships.find_or_create_by(follow_id: other_user.id)は見つかればrelationを返し、見つからなければself.relationships.find_or_create_by(follow_id: other_user.id)としてフォロー関係をself.relationships.create(follow_id: other_user.id) としてフォロー関係を保存することができる。これによってフォローされている場合にフォローが重複して保存されることがなくなります。

def unfollowingではフォローがあればアンフォローしてくれます。またrelationship.destroy if relationshipはrelationshipが存在すればdestroyする。

def following?ではself.followingsによりフォローしているUser達を取得し、include?(other_user)によってother_userが含まれていないかを確認している。

#relationshipsコントローラを作成&編集

$ rails g controller relationships

まずはrelationshipsコントローラを作成しています。

app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
  before_action :set_user

  def create
    user = User.find(params[:relationship][:follow_id])
    following = current_user.follow(user)
    if following.save
      flash[:success] = 'ユーザーをフォローしました'
      redirect_to user
    else
      flash.now[:alert] = 'ユーザーのフォローに失敗しました'
      redirect_to user
    end
  end

  def destroy
    user = User.find(params[:relationship][:follow_id])
    following = current_user.unfollow(user)
    if following.destroy
      flash[:success] = 'ユーザーのフォローを解除しました'
      redirect_to user
    else
      flash.now[:alert] = 'ユーザーのフォロー解除に失敗しました'
      redirect_to user
    end
  end

  private

  def set_user
    user = User.find(params[:relationship][:follow_id])
  end

end

コントローラに上記を記載します。

#フォローボタン(form_for)をviewに設置

app/views/relationships/_follow_button.html.erb
<% unless current_user == user %>
  <% if current_user.following?(user) %>
    <%= form_for(current_user.relationships.find_by(follow_id: user.id), html: { method: :delete }) do |f| %>
      <%= hidden_field_tag :follow_id, user.id %>
      <%= f.submit 'Unfollow', class: 'btn btn-danger btn-block' %>
    <% end %>
  <% else %>
    <%= form_for(current_user.relationships.build) do |f| %>
      <%= hidden_field_tag :follow_id, user.id %>
      <%= f.submit 'Follow', class: 'btn btn-primary btn-block' %>
    <% end %>
  <% end %>
<% end %>

'''
Rails.application.routes.draw do
resources :relationships, only: [:create, :destroy]
end
'''
あとはルーティングを記載して完了です。

#参考URL
https://qiita.com/MitsuguSueyoshi/items/e41e2ff37f143db81897

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?