5
1

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フォローフォロワー機能の実装、一覧表示、seedに初期データ投入

Last updated at Posted at 2021-03-17

#前提

ログイン機能はDeviseで作成済み(user)

seeds.rbにuserのデータ先に入れておきます。

db/seeds.rb
user1 = User.create({ nickname: '田中', email: 'test1@test.com', password: '********' })
user2 = User.create({ nickname: '森山', email: 'test2@test.com', password: '********' })
user3 = User.create({ nickname: '坂下', email: 'test3@test.com', password: '********' })
id nickname email password
1 田中 test1@test.com ********
2 森山 test2@test.com ********
3 坂下 test3@test.com ********

#モデル作成

$ rails g model Relationship

relationshipテーブルにuserモデルの外部キー(user_id)を追加

db/migrate/000000000000_create_relationships.rb

class CreateRelationships < ActiveRecord::Migration[5.0]
  def change
    create_table :relationships do |t|
      t.references :user, type: :bigint, foreign_key: true
      t.references :follow, foreign_key: { to_table: :users }

      t.timestamps

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

・references 外部キーの追加
(参照モデル名_idというカラム名で参照モデルのidと紐付け。これだけだと参照にないidでも登録できてしまう)

・foreign_key: true
外部キー制約(親レコードにない値は参照カラムに登録できない、has_manyやbelongs_toなどを考える上で、「関連付けできないような値は絶対に来ない」という意味。user_idにはusers.idにある値しか入れられない)

・foreign_key: { to_table: :users }
外部キー制約【応用編】(user_id以外の名前(follow_id)でusers_idへの外部キー制約をはる。foreign_key: trueにすると存在しないfollowsテーブルを参照してしまう)

・unique: true
(user_idとfollow_id のペアで重複するものが保存されないようにするデータベースの設定)

$ rails db migrate

foreign_keyを記述してrails db:migrateをすると、
foreign_keyを記述した側のモデル(relationship.rb)に自動的にbelongs_toが記述されます。
(has_manyに関してはもう片方のモデル.rbに手で記述しなければなりません)

#関連付け(アソシエーション)多対多

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モデルなんて存在しない→userモデルに紐付け)

app/models/user.rb
class User < ApplicationRecord
  has_many :relationships, dependent: :destroy
  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 :relationships
(relationshipsモデルと関連付け)

・has_many :followings, through: :relationships, source: :follow
(relationshipsテーブルのfollow_idを参照して、followingsモデル(架空)にアクセス)

・has_many :reverse_of_relationships
(has_many :relationshipsの逆方向。フォロワー(フォローされているuser))

・class_name: 'Relationship'
(relationshipsモデルに紐付け)

・foreign_key: 'follow_id'
外部キー(relationshipsテーブルにアクセスする時、follow_idを入口とする)

・has_many :followers, through: :reverse_of_relationships, source: :user
(followersモデル(架空)。中間テーブルはreverses_of_relationshipで持って帰ってくる情報はuser_id)

#userモデルにフォローメソッドの追加

app/models/user.rb
...
# 自分以外だったらフォロー
 def follow(other_user)
    unless self == other_user
      relationships.find_or_create_by(follow_id: other_user.id)
    end
  end

 # フォローがあればアンフォロー
  def unfollow(other_user)
    relationship = relationships.find_by(follow_id: other_user.id)
    relationship&.destroy
  end

# フォローしているユーザー
  def following?(other_user)
    followings.include?(other_user) 
  end

・other_user
(他ユーザーのページでフォロー/アンフォローボタンを押した人)

・unless self == other_user
(フォローしようとしている(other_user)が自分自身(self)ではないか)

・find_or_create_by
(findが先に実行、探してみて無ければ作成。同じデータは2つ作られることはない)

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

・include?
(followingsの中にother_userオブジェクトが含まれていればtrueを返す)

#コントローラー作成

$ rails g controller relationships
app/controller/relationships_controller.rb
class RelationshipsController < ApplicationController
  before_action :set_user

  def create
    following = current_user.follow(@user)
    if following.save
      @user.create_notification_follow!(current_user)
      flash[:success] = 'ユーザーをフォローしました'
      redirect_back(fallback_location: root_path)
    else
      flash.now[:alert] = 'ユーザーのフォローに失敗しました'
      redirect_back(fallback_location: root_path)
    end
  end

  def destroy
    following = current_user.unfollow(@user)
    if following.destroy
      flash[:success] = 'ユーザーのフォローを解除しました'
      redirect_back(fallback_location: root_path)
    else
      flash.now[:alert] = 'ユーザーのフォロー解除に失敗しました'
      redirect_back(fallback_location: root_path)
    end
  end

  private

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

#ルーティング

config/routes.rb
 resources :relationships, only: [:create, :destroy]

#部分テンプレート作成

app/view/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-dark 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-dark btn-block w-100' %>
    <% end %>
  <% end %>
<% end %>

フォローボタンを表示させたいところで呼び出す

<%= render relationships/follow_button, user: @user %> 

#一覧表示(フォローフォロワー人数、フォローボタン付き)

followers.html.erb
<% if @user.followers.present? %>
  <% @users.each do |user| %>
    <%= link_to user_path(user) do %>
      <%= attachment_image_tag user, :image, fallback: "logo.png" %>
    <% end %>
    <%= link_to user.nickname, user_path(user) %>
    フォロー(<%= user.followings.count %>人)<br>
    フォロワー(<%= user.followers.count %>人)
    <%= render 'relationships/follow_button', user: user %>
  <% end %>
<% else %>
    フォロワーがいません
<% end %>
app/controller/users_controller.rb
  def followers
    @user = User.find(params[:id])
    @users = @user.followers.all
  end

※followingsも同様。

#データ投入

db/seeds.rb
# user1をuser2,3がフォロー
user1.followers << user2
user1.followers << user3
user1.save

# user2をuser1,3がフォロー
user2.followers << user1
user2.followers << user3
user2.save

備忘録、復習です。

5
1
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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?