Devise+フォロー、アンフォロー機能の実装をしていこう!
遅かれ早かれ、やっとフォロー、アンフォローまでの実装が終わりました。
今回Deviseを使って実装するのが初めてだったのでまとめておきます!
これを見た人は参考までにどうぞ。
こちらの記事を参考にしほとんど一緒の内容になってしまいますが、若干違う場所もあります。
それではレッツラゴー
モデルの作成
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
$ rails generate model Relationship follower_id:integer followed_id:integer
def changecreate_table :relationships do |t|t.integer :follower_idt.integer :followed_idt.timestamps null: falseendadd_index :relationships, :follower_idadd_index :relationships, :followed_idadd_index :relationships, [:follower_id, :followed_id], unique: trueendend
マイグレーションファイルが作成されるので黄色の箇所の複合キーインデックスを付け足す。ここでは、同じユーザーが2回以上同じ人をフォロー出来ないように防いでいる。
そしていつも通り
$ rails db:migrate
UserとRelationshiipの関連付け
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
app/models/rerationship.rb
class Relationship < ApplicationRecordbelongs_to :follower, class_name: "User"belongs_to :followed, class_name: "User"validates :follower_id, presence: truevalidates :followed_id, presence: trueend
下の2行でついでにバリデーションも設定
app/models/user.rb
has_many :active_relationships, class_name: "Relationship",foreign_key: "follower_id",dependent: :destroyhas_many :passive_relationships, class_name: "Relationship",foreign_key: "followed_id",dependent: :destroyhas_many :following, through: :active_relationships, source: :followedhas_many :followers, through: :passive_relationships, source: :follower# ユーザーをフォローするdef follow(other_user)active_relationships.create(followed_id: other_user.id)end# ユーザーをアンフォローするdef unfollow(other_user)active_relationships.find_by(followed_id: other_user.id).destroyend# 現在のユーザーがフォローしてたらtrueを返すdef following?(other_user)following.include?(other_user)endend
仮装的にUserモデルを2つに分けるためにactive_rerationshipsとpassive_rerationshipsという別名をつけます。
followiing,followersはそれぞれsourceから名前を上書きしています。followingによる関連付けを使ってfollow,unfollow,following?メソッドを作成していきます。ここではUserは2つとなるのでselfではなくother_userを使います。
ルーティング
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
resources :users do
member do
get :following, :followers
end
end
とすることで
このようなURLが使えるようになりましたが、今回はDeviseを使ってるので
devise_scope :user doget 'users/:id/' => 'users/registrations#show', as: 'show'get 'users/:id/following', to: 'users/registrations#following', as: 'following'get 'users/:id/followers', to: 'users/registrations#followers', as: 'followers'endresources :relationships, only: [:create, :destroy]end
このように自分で2つのgetリクエストを作ってあげます。こうすることでチュートリアルと同じものがDevise上でも出来上がります。
あとはrelationshipのresourcesもお忘れなく!
必要なテンプレートの作成
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
今回はajaxでの実装をしていく形になります。
まずは、フォロワーの統計情報を表示するパーシャルを作成します。
app/views/users/shared/_stats.html.erb
<% @user ||= current_user %><div class="stats"><a href="<%= following_path(@user) %>">フォロー<strong id="following" class="stat"><%= @user.following.count %></strong></a><a href="<%= followers_path(@user) %>">フォロワー<strong id="followers" class="stat"><%= @user.followers.count %></strong></a></div>
次は先ほどの統計情報とフォローボタンを表示させるためのViewを追加します。
また、ログインしている人だけに表示させます。
app/views/registrations/show.html.erb
<section class="stats"><%= render 'users/shared/stats' %></section><section><%= render 'users/shared/follow_form' if user_signed_in? %></section>
次にfollow,unfollowのパーシャルを作成
app/views/users/shared/_follow_form.html.erb
<% unless @user == current_user %><div id="follow_form"><% if current_user.following?(@user) %><%= render 'users/shared/unfollow' %><% else %><%= render 'users/shared/follow' %><% end %></div><% end %>
次にfollow_form.html.erbで用意したパーシャルを作成するのですが、ajaxにするためremote:trueにします。
app/views/users/shared/_follow.html.erb
<%= form_for(current_user.active_relationships.build, remote: true) do |f| %><div><%= hidden_field_tag :followed_id, @user.id %></div><%= f.submit "フォローする", class: "follow-btn" %><% end %>
app/views/users/shared/_unfollow.html.erb
<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),html: { method: :delete }, remote:true) do |f| %><%= f.submit "フォロー中", class: "unfollow-btn" %><% end %>
followingアクションとfollowersアクション
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
次の2つのアクションをshow_followページ1つで読み込むようにさせる。
app/views/users/registrations_controller.rb
def following@title = "フォロー"@user = User.find(params[:id])@users = @user.following.paginate(page: params[:page])render 'show_follow'enddef followers@title = "フォロワー"@user = User.find(params[:id])@users = @user.followers.paginate()page:params[:page]render 'show_follow'end
このアクションに対応するビューを作成
app/views/users/registrations/show_follow.html.erb
<div><h1><%= @user.name %></h1><section class="stats"><%= render 'users/shared/stats' %></section><div><h3><%= @title %></h3><% if @users.any? %><ul class="users follow"><% @users.each do |user| %><li><%= link_to user.name, "#" %></li><% end %></ul><% end %></div></div>
RelationshipコントローラでAjaxリクエストに対応
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
まずは、Relationshipコントローラ作成
$ rails g controller Relationships
app/controller/relationships_controller.rb
class RelationshipsController < ApplicationControllerbefore_action :authenticate_user!def create@user = User.find(params[:followed_id])current_user.follow(@user)respond_to do |format|format.html { redirect_to @user }format.jsendenddef destroy@user = Relationship.find(params[:id]).followedcurrent_user.unfollow(@user)respond_to do |format|format.html { redirect_to @user }format.jsendendend
これらのアクションが呼び出すjs.erbファイルを作成
app/views/relastionships/create.js.erb
$("#followers").html('<%= @user.followers.count %>');
app/views/relationships/destroy.js.erb
$("#followers").html('<%= @user.followers.count %>');
以上!!!
これでできるはず!
人によってはDeviseの部分が違ったりしてくるのでそこは対応させてください。
また、レイアウトもしていないのでとても見栄えはよくないので自分なりにアレンジしてみてください。
ばいちゃ