概要
ブロック機能を実装しようとしたところ、記事としてまとめられているものがなかったのでアドベントカレンダーの企画に便乗して記事の執筆に挑戦しました!!
初めて記事を書くので拙いコード、文章になってしまっていたらごめんなさい
この記事を参考にしてブロック機能を実装して頂けたらうれしいです
改善点などあればぜひコメントお願いします
具体的にできること
ユーザーをブロックした場合そのユーザーから
- マイページを見られない
- フォローされない
- DMが送られてこない
- 投稿一覧、いいねした投稿一覧を見られない
マイページのみにフォローボタン、DMのリンク、投稿一覧、いいねした投稿一覧を置く
➡ブロックされている場合は通常のマイページを見られなくする
➡不審なユーザーからアクセスされないっていう感じです
完成図
そのユーザーにブロックされていた場合の表示
前提
- deviseでユーザーモデル作成、マイページ実装済み
- フォロー、DMも実装すればブロック機能に対応できます
筆者が参考にさせていただいた記事を以下に置いときます
railsでフォロー機能をつける。 #Ruby - Qiit
railsでDM機能を作成する
実装スタート!!
①モデル
基本的にフォロー機能と同じように進めていきます
Blockモデル作成
$ rails generate model Block
マイグレーションファイルを編集
classCreateBlocks < ActiveRecord::Migration[6.1]
def change
create_table :blocks do |t|
t.integer :blocker_id # 追加
t.integer :blocking_id # 追加
t.timestamps
end
add_index :blocks, :blocker_id # 追加
add_index :blocks, :blocking_id # 追加
add_index :blocks, [:blocker_id, :blocking_id], unique: true # 追加
end
end
$ rails db:migrate
Blockモデルに追記
class Block < ApplicationRecord
validates :blocker_id, presence: true
validates :blocking_id, presence: true
belongs_to :blocker, class_name: 'User'
belongs_to :blocking, class_name: 'User'
validate :self_block
private
# 自分自身をブロックしていないか検証する
def self_block
return unless blocker_id && blocking_id
errors.add(:blocking_id, '自分自身をブロックすることはできません') if blocker_id == blocking_id
end
end
Userモデルはブロック機能で新しく追加するやつだけ書いてます
has_many :active_blocks, class_name: "Block",
foreign_key: "blocker_id",
dependent: :destroy
has_many :blockings, through: :active_blocks, source: :blocking
has_many :passive_blocks, class_name: "Block",
foreign_key: "blocking_id",
dependent: :destroy
has_many :blockers, through: :passive_blocks, source: :blocker
def block!(other_user)
active_blocks.create!(blocking_id: other_user.id)
end
def unblock!(other_user)
active_blocks.find_by(blocking_id: other_user.id).destroy
end
def blocking?(other_user)
active_blocks.find_by(blocking_id: other_user.id)
end
def blocked?(other_user)
passive_blocks.find_by(blocker_id: other_user.id)
end
②コントローラー
blocksコントローラー作成
$ rails g controller blocks
class BlocksController < ApplicationController
before_action :authenticate_user!
def create
@user = User.find(params[:block][:blocking_id])
current_user.block!(@user)
@user.unfollow!(current_user) if @user.following?(current_user)
current_user.unfollow!(@user) if current_user.following?(@user)
redirect_to @user, notice: "ユーザーをブロックしました。"
end
def destroy
user = Block.find(params[:id]).blocking
current_user.unblock!(user)
redirect_to user, notice: "ユーザーのブロックを解除しました。"
end
end
usersコントローラーは追加するやつだけ書いてます
def blocking
@title = 'blocking'
@user = User.find(params[:id])
@users = @user.blockings
render 'show_blocking'
end
③ルーティング
これも追加するやつだけ書いてます
resources :users do
member do
get :following, :followers
get :blocking, :blockers
end
end
resources :blocks, only: [:create, :destroy], controller: 'blocks'
④helper
def current_user?(user)
user == current_user
end
⑤view
マイページは最初のif文とそれに対応するelseとendさえあればこの記事でやりたいことは実装できるので自由にカスタマイズしてみてください
<% if current_user.blocked?(@user) %>
<h3>あなたはこのユーザーにブロックされています</h3>
<%= link_to "Tweet一覧に戻る", tweets_path %>
<%= link_to "ユーザー一覧に戻る", users_path %>
<% else %>
<h1>マイページ</h1>
<p>名前 : <%= @user.name %></p>
<p>メールアドレス : <%= @user.email %></p>
<p>プロフィール : <%= @user.profile %></p>
<% if user_signed_in? %>
<%= render 'stats' %>
<%= render 'follow_form' %> # フォロー機能を実装する場合は必要
<%= render 'block_form' %>
<% end %>
<% if current_user.id == @user.id %>
<%= link_to "編集する", edit_user_registration_path %>
<% else %> # 以下DM機能を実装する場合は必要
<% if @isRoom == true %>
<p><%= link_to 'DMへ', room_path(@roomId) %></p>
<% else %>
<%= form_for @room do |f| %>
<%= fields_for @entry do |e|%>
<% e.hidden_field :user_id, value: @user.id %>
<% end %>
<%= f.submit "DMを開始する"%>
<% end %>
<% end %> # DM機能ここまで
<% end %>
<h2>ユーザーの投稿一覧</h2>
<% @user.tweets.each do |t| %>
<%= t.user.name %>
<%= t.body %>
<% end %>
<br>
<%= link_to "Tweet一覧に戻る", tweets_path %>
<%= link_to "ユーザー一覧に戻る", users_path %>
<% end %>
このページは何人ブロックしてるかっていう情報を表示するファイルです
フォローとフォロワーの記述はフォロー機能つけてない場合は必要ないです
<% @user ||= current_user %>
<div class="stats">
<a href="<%= following_user_path(@user) %>">
フォロー
<strong id="following" class="stat">
<%= @user.followings.count %>
</strong>
</a>
<a href="<%= followers_user_path(@user) %>">
フォロワー
<strong id="followers" class="stat">
<%= @user.followers.count %>
</strong>
</a>
<% if @user == current_user %>
<p>
<a href="<%= blocking_user_path(@user) %>">
<strong id="blocking" class="stat">
<%= @user.blockings.count %>
</strong>
人ブロック中
</a>
</p>
<% end %>
</div>
自分が誰をブロックしてるか表示するページです
<h1>ブロックしているユーザー</h1>
<% if @user.blockings.any? %>
<ol>
<% @user.blockings.each do |user| %>
<li>
<%= "#{user.name} - " %>
<%= link_to '詳細', user_path(user) %>
</li>
<% end %>
</ol>
<% else %>
<p>ブロックしているユーザーはいません。</p>
<% end %>
今見てるユーザーをブロックしてるかどうかで表示するボタンを変えるためのファイルです
<% unless current_user?(@user) %>
<div id="block_form">
<% if current_user.blocking?(@user) %>
<%= render "unblock" %>
<% else %>
<%= render "block" %>
<% end %>
</div>
<% end %>
ブロックボタンを表示するファイル
<%= form_for(current_user.active_blocks.build(blocking_id: @user.id)) do |f| %>
<div><%= f.hidden_field :blocking_id %></div>
<%= f.submit "Block" %>
<% end %>
ブロック解除ボタンを表示するファイル
<% block = current_user.active_blocks.find_by(blocking_id: @user.id) %>
<% if block %>
<%= form_for(block, html: { method: :delete }) do |f| %>
<%= f.submit "Unblock" %>
<% end %>
<% end %>
参考記事
服選びの時間短縮‼︎ スピードウェア #Ruby - Qiita
railsでフォロー機能をつける。 #Ruby - Qiit
railsでDM機能を作成する
まとめ
この記事を参考にしていただける方が少しでもいらっしゃったら幸いです。
ご覧いただきありがとうございました。