LoginSignup
2
0

railsでブロック機能実装してみた

Last updated at Posted at 2023-12-16

概要

ブロック機能を実装しようとしたところ、記事としてまとめられているものがなかったのでアドベントカレンダーの企画に便乗して記事の執筆に挑戦しました!!

初めて記事を書くので拙いコード、文章になってしまっていたらごめんなさい
この記事を参考にしてブロック機能を実装して頂けたらうれしいです
改善点などあればぜひコメントお願いします

具体的にできること

ユーザーをブロックした場合そのユーザーから

  • マイページを見られない
  • フォローされない
  • DMが送られてこない
  • 投稿一覧、いいねした投稿一覧を見られない

マイページのみにフォローボタン、DMのリンク、投稿一覧、いいねした投稿一覧を置く
➡ブロックされている場合は通常のマイページを見られなくする
➡不審なユーザーからアクセスされないっていう感じです

完成図

Geektwitter12 - Google Chrome 2023-12-10 16-38-32.gif

そのユーザーにブロックされていた場合の表示

スクリーンショット (443).png

前提

実装スタート!!

①モデル

基本的にフォロー機能と同じように進めていきます

Blockモデル作成

コマンドプロンプト
$ rails generate model Block

マイグレーションファイルを編集

db/migrate/xxxxxxxxxxxxxx_create_blocks
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モデルに追記

app/models/block.rb
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モデルはブロック機能で新しく追加するやつだけ書いてます

app/models/user.rb
  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
app/controllers/blocks_controller.rb
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コントローラーは追加するやつだけ書いてます

app/controllers/users_controller.rb
def blocking
    @title = 'blocking'
    @user = User.find(params[:id])
    @users = @user.blockings
    render 'show_blocking'
end

③ルーティング

これも追加するやつだけ書いてます

config/routes.rb
  resources :users do
    member do
      get :following, :followers
      get :blocking, :blockers
    end
  end  
  resources :blocks, only: [:create, :destroy], controller: 'blocks'

④helper

app/helpers/users_helper.rb
def current_user?(user)
    user == current_user
end

⑤view

マイページは最初のif文とそれに対応するelseとendさえあればこの記事でやりたいことは実装できるので自由にカスタマイズしてみてください

app/view/users/show.html.erb
<% 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 %>

このページは何人ブロックしてるかっていう情報を表示するファイルです
フォローとフォロワーの記述はフォロー機能つけてない場合は必要ないです

app/view/users/_stats.html.erb
<% @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>

自分が誰をブロックしてるか表示するページです

app/view/users/show_blocking.html.erb
<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 %>

今見てるユーザーをブロックしてるかどうかで表示するボタンを変えるためのファイルです

app/view/users/_block_form.html.erb
<% unless current_user?(@user) %>
<div id="block_form">
    <% if current_user.blocking?(@user) %>
        <%= render "unblock" %>
    <% else %>
        <%= render "block" %>
    <% end %>
</div>
<% end %>

ブロックボタンを表示するファイル

app/view/users/_block.html.erb
<%= form_for(current_user.active_blocks.build(blocking_id: @user.id)) do |f| %>
    <div><%= f.hidden_field :blocking_id %></div>
    <%= f.submit "Block" %>
<% end %>

ブロック解除ボタンを表示するファイル

app/view/users/_unblock.html.erb
<% 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機能を作成する

まとめ

この記事を参考にしていただける方が少しでもいらっしゃったら幸いです。
ご覧いただきありがとうございました。

2
0
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
2
0