Ruby on Rails tutorial +alpha(4)フォローを承認制にする
注意:この記事はメモなので、この記事に書いてあることをやっただけでは同じものができません。(大体の雰囲気は書いてあると思います。)
注意:全くのweb開発初心者なので、間違っているところなどがあるかと思いますが、教えていただけると嬉しいです。
フォローを承認制にしていきます。
フォロー関係を作った時と同様に FollowRequestモデルを作り、以下の機構を実装していきます。
- follow_requestsテーブルにfollower_idとfollowed_idを追加、usersと紐づける
- follower_idの人がフォローリクエスト申請ボタンを押したら テーブルにデータ作成 *followed_idの人がフォロー許可を押したらrelationshipsテーブルにデータを作成してfollow_requestsのデータは削除
以下の動作が保証されなければいけないでしょう。
- follow_requestsをみられるのは関係する2人のみ
- follow_requestsを送信できるのはfollower_idだけ
- follow_requestsを承認できるのはfollowed_idだけ
- 存在しないフォローリクエストは承認できない
モデルの作成
モデルを作ります。
rails generate model FollowRequest follower_id:integer followed_id:integer
migration
class CreateFollowRequests < ActiveRecord::Migration[6.0] def change create_table :follow_requests do |t| t.integer :follower_id t.integer :followed_id t.timestamps end add_index :follow_requests, :follower_id add_index :follow_requests, :followed_id add_index :follow_requests, [:follower_id, :followed_id], unique: true end end
model/follow_request.rb
class FollowRequest < ApplicationRecord belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" validates :follower_id, presence: true validates :followed_id, presence: true end
model/user.rb
has_many :active_requests, class_name: "FollowRequest", foreign_key: "follower_id", dependent: :destroy has_many :passive_requests, class_name: "FollowRequest", foreign_key: "followed_id", dependent: :destroy has_many :request_following, through: :active_requests, source: :followed has_many :request_followers, through: :passive_requests, source: :follower
rails db:migrate:reset
テストを書いてフォローリクエストが送れるようにします。 test/models/follow_request_test.rb
test "フォローリクエストを送る" do michael = users(:michael) archer = users(:archer) assert_not michael.request_following?(archer) michael.request_follow(archer) assert michael.request_following?(archer) assert archer.request_followers.include?(michael) michael.request_unfollow(archer) assert_not michael.request_following?(archer) end
models/user.rb
# ユーザーをフォロー申請する def request_follow(other_user) request_following << other_user end # ユーザーをフォロー申請解除する def request_unfollow(other_user) active_requests.find_by(followed_id: other_user.id).destroy end # 現在のユーザーがフォローしてたらtrueを返す def request_following?(other_user) request_following.include?(other_user) end
まずフォローリクエストの数をホームに表示することにして、そこをクリックするとフォローリクエストが見られるようにします。 routes.rb
resources :users do member do get :following, :followers get :request_following, :request_followers get :events end end
だいたい同じように作ります。
コントローラとビューの作成
ここまではRelationshipsを作る時と同じでした。ここから、フォローボタンを押すとフォローリクエストが送られるようにします。 (フォローの時のコードと同じようにやる)
次に、ユーザの下のボタンでフォローを承認できるようにします
class RelationshipsController < ApplicationController before_action :logged_in_user def create @user = User.find(params[:follower_id]) if @user.request_unfollow(current_user) @user.follow(current_user) respond_to do |format| format.js {render inline: "location.reload();" } end else flash[:danger]="エラーが発生しました。フォローリクエストが存在しません。" redirect_to :back end end def destroy @user = Relationship.find(params[:id]).followed current_user.unfollow(@user) respond_to do |format| format.html { redirect_to @user } format.js end end end
<li> <%= image_tag user.icon_image(size:50), :size=>"50x50"%> <%= link_to user.name, user %> <% if current_user.admin? && !current_user?(user) %> | <%= link_to "delete", user, method: :delete, data: { confirm: "You sure?" } %> <% end %> <% if current_user.request_follower?(user) %> <%= form_with(model: current_user.active_relationships.build, remote: true) do |f| %> <div><%= hidden_field_tag :follower_id, user.id %></div> <%= f.submit "フォロー承認", class: "btn btn-primary" %> <% end %> <%end%> </li>
できました。
権限設定・テストなど
あとはテストなどを書いていきます。createを使って直接フォローができないようになっているか注意しましょう。
自分以外は/users/1/request_followersと/users/1/request_folloingを見られない
before_actionを指定すれば良いです。画面上に存在するボタンを押したらルートに飛ばさせるのも変なので、ビューの方でcurrent_user=@user以外の時はリンクを消しておきました。
フォロー関係のテスト
folloing_testを変更すればいいです。
test "should send and accept follow request a user the standard way" do assert_difference '@user.request_following.count', 1 do post follow_requests_path, params: { followed_id: @other.id } end log_in_as(@other) assert_difference '@user.following.count', 1 do post relationships_path, params: { follower_id: @user.id } end end test "should accept follow request a user with Ajax" do assert_difference '@user.request_following.count', 1 do post follow_requests_path, xhr: true, params: { followed_id: @other.id } end log_in_as(@other) assert_difference '@user.following.count', 1 do post relationships_path,xhr:true, params: { follower_id: @user.id } end end test "should not accept not existing follow request" do log_in_as(@other) assert_no_difference '@user.following.count' do post relationships_path, params: { follower_id: @user.id } end end
これだとajaxではなくフォローを承認したときにテストが失敗してしまいます。これはformat.htmlの方を返していなかったからです。
def create @user = User.find(params[:follower_id]) if @user.request_unfollow(current_user) @user.follow(current_user) respond_to do |format| format.html {redirect_back(fallback_location: root_path) } format.js {render inline: "location.reload();" } end else flash[:danger]="エラーが発生しました。フォローリクエストが存在しません。" redirect_back(fallback_location: root_path) end end
にしたら通りました。(あとrails5以上ではredirect_to :backが消えていたみたいです。redirect_back(fallback_location: root_path)にしました。) あとは細かい訂正をすれば(省略)テストに全て通過します。