2
0

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 1 year has passed since last update.

Rails 相互フォロー時にDM機能によってメッセージを送信可能にする

Posted at

はじめに

  • ユーザーが相互フォローをしている際に、お互いのみが参照できるDM機能を実装する
  • 学習内容の備忘録です

前提条件

  • ユーザー機能が実装できている
  • フォロー、フォロワー機能が実装できている
  • Bootstrap4を導入している

手順

  1. テーブル設計
  2. アソシエーション
  3. ルーティングとコントローラー
  4. ビュー

1.テーブル設計

  • まず、元々あるUsersテーブルとDMをするための部屋RoomsテーブルはN:Nの関係
  • 次に、中間テーブルであるEntriesテーブルを作成→Users:Entries=1:N
  • さらに、中間テーブルMessagesテーブルを作成→Users:Messages=1:N

2.アソシエーション

まず、新しいモデルを作成。

ターミナル
rails g model Room user:references
rails g model Entry user:references room:references
rails g model Message user:references room:references
rails db:migrate

次に、それぞれのモデルへ記述。

user.rb
has_many :messages, dependent: :destroy
has_many :entries, dependent: :destroy
room.rb
has_many :messages, dependent: :destroy
has_many :entries, dependent: :destroy
entry.rb
belongs_to :user
belongs_to :room
message.rb
validates :message, length: { maximum: 140 }
belongs_to :user
belongs_to :room

補足
「validates :message, length: { maximum: 140 }」によって、message属性がnullでないことを検証し、さらに文字列の長さが140文字以下であることを検証。つまり、空欄はダメ、141文字以上もダメ。

3.ルーティングの設定とコントローラーの記述

まず、ルーティングにこちらの記述を追加。

routes.rb
get 'message/:id' => 'messages#show', as: 'message'
resources :messages, only: [:create]

続いて、コントローラーを作成。

ターミナル
rails g controller messages

さらに、messagesコントローラーへ記述。
showアクションとcreateアクションを記述。

messages_controller.rb
# showアクションとcreateアクションを実行する前に、ユーザーの認証(ログイン)を確認
before_action :authenticate_user!, only: [:show, :create]

  # showアクションは、特定のユーザーの詳細ページを表示するため
  def show
    @user = User.find(params[:id])

    # 現在のユーザーが参加しているすべてのルームのIDを取得
    rooms = current_user.entries.pluck(:room_id)
    
    # 特定のユーザーと現在のユーザーの間で共有されているルームのエントリーを見つける
    entries = Entry.find_by(user_id: @user.id, room_id: rooms)

        # エントリーが存在する場合、関連するルームを@roomに代入
    unless entries.nil?
      @room = entries.room

    # エントリーが存在しない場合、新しいルームを作成し、保存し、お互いのエントリーを作成
    else
      @room = Room.new(user_id: current_user.id)
      @room.save
      Entry.create(user_id: current_user.id, room_id: @room.id)
      Entry.create(user_id: @user.id, room_id: @room.id)
    end

    # @roomに関連するメッセージを@messagesに、新しいメッセージを作成したものは@messageに代入
    @messages = @room.messages
    @message = Message.new(room_id: @room.id)
  end

  # createアクションは、新しいメッセージを作成するため
  def create

    # 現在のユーザーに関連付けられた新しいメッセージを作成
    @message = current_user.messages.new(message_params)

    # メッセージを保存し、元のURLへリダイレクト
    @message.save
    redirect_to request.referer
  end

  private

  # フィルタリングも忘れずに
  def message_params
    params.require(:message).permit(:message, :room_id)
  end

4.ビュー

まずは、messageを始めるためのリンクを、ユーザーの詳細ページへ。
ここでは、部分テンプレートに記述。

_info.html.erb
<div class='row mt-2'>
  <% unless user.id == current_user.id %>
    <% if current_user.is_followed_by?(user) && user.is_followed_by?(current_user) %>
      <% if current_user != @user %>
        <%= link_to 'messageを始める', message_path(@user.id)%>
      <% end %>
    <% end %>
  <% end %>
</div>

補足
モデルに「is_followed_by?」メソッドを定義しているため、「<% if current_user.is_followed_by?(user) && user.is_followed_by?(current_user) %>」で相互フォロー状態であることを確認できる。つまり、相互フォロー状態で、自分のユーザー詳細出なければ、「messageを始める」ボタンが出現!

補足2
「is_followed_by?」メソッドについては以下の通り
ちなみに、当時はこのように書いてしまったが、「reverse_of_relationships(反対からの)」よりも、「passive_relationships(受け身の)」の方がリーダブルだと思われる。

user.rb
def is_followed_by?(user)
    reverse_of_relationships.find_by(follower_id: user.id).present?
end

あとは、DMを送り合う部屋への記述!

messages/show.html.erb
<h4 class="rooms-title text-center mb-3">チャットルーム</h4>

<div class="row justify-content-center mb-3">
  <div class="left-button">
    <%= link_to "ユーザー一覧に戻る", users_path, class:"edit-link" %>
  </div>
</div>

<div class="row justify-content-center mb-3">
  <div class="posts ">
    <%= form_with model: @message do |f| %>
      <%= f.text_field :message, placeholder: "メッセージを入力して下さい" , size: 70, class:"form-text-field" %>
      <%= f.hidden_field :room_id %>
      <%= f.submit "投稿",class: 'form-submit' %>
    <% end %>
  </div>
</div>

<div class="row justify-content-center text-center">
  <div class="chat">
    <% if @messages.present? %>
      <% @messages.each do |m| %>
        <div class="chat-box">
          <div class="chat-face">
            <%= link_to user_path(m.user.id) do %>
              <%= image_tag m.user.get_profile_image, size: "50x50", class: "user-image" %>
            <% end %>
            <%= m.user.name %>
            <strong><%= m.message %></strong>
            <%= m.created_at.strftime("%Y-%m-%d %H:%M")%>
          </div>
        </div>
      <% end %>
    <% end %>
  </div>
</div>

あまり、見た目が美しくないので、今後、改善を重ねていきます。

参考サイト

RailsでややこしいDM機能を1万字でくわしく解説してみた
とても、わかりやすく、理解が深まりました。ありがとうございます。

まとめ

  • DMを送るためには、EntriesとMessagesの2つの中間テーブルにそれぞれ役割がある

フラッシュメッセージや、レイアウトなどは未完ですが、とりあえずDMは送り合うことができるようになりました。

⚠️学習4ヶ月目の初学者による投稿です。
⚠️間違いがあるかもしれません。ご容赦ください。
⚠️ご指導、ご教授いただけると幸いです。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?