🐣

[Ruby on Rails] DM機能の作成

2023/07/09に公開

今回は相互フォローしている人限定で、DM機能が使用出来る様にします
文字制限は140字まで送信可能にします

  • DM機能には4つのテーブルを使用します
    • Users テーブル
    • Rooms テーブル
    • Entries テーブル
    • Messages テーブル

テーブルの設計

  • Rooms テーブル
カラム名 データ型 カラムの説明
user refaremces userのテーブルを指定
  • Entries テーブル
カラム名 データ型 カラムの説明
user refarences userのテーブルを指定
room refarences roomのテーブルを指定
  • Messages テーブル
カラム名 データ型 カラムの説明
user refarences userのテーブルを指定
room refarences roomのテーブルを指定
message text メッセージを送る際の文字列
  • refareneces 新しく作成するテーブルのカラムに、作成済ののテーブルを指定する場合に使います

モデルの作成

$ rails g model Rooms user:refarences
$ rails g model Entries user:refarences room:refarences
$ rails g model Messages user:refarences room:refarences message:text

モデルの関連付け

user.rb
  has_many :messages, dependent: :destroy
  has_many :entries, dependent: :destroy
room.rb
  belongs_to :user
  has_many :messages, dependent: :destroy
  has_many :entries, dependent: :destroy
entry.rb
  belongs_to :user
  belongs_to :room
message.rb
  belongs_to :user
  belongs_to :room
  validates :message, presence: true, length: {maximum: 140}
プチ解説
  • presence: true は存在しなければいけない つまり文字を入力しないと送信できない
  • length 配列に入っている数を数える
  • maximum: 140 文字制限を140字までに設定

作成した、テーブルの内容を含めてモデルを作成します

ルーティングの設定

routes.rb
  resources :messages, only: [:create]
  resources :rooms, only: [:create, :show]

コントローラーの変更

user_controller
  def show
    @user = User.find(params[:id])
    @currentUserEntry = Entry.where(user_id: current_user.id)
    @userEntry = Entry.where(user_id: @user.id)
    if @user.id == current_user.id
    else
      @currentUserEntry.each do |cu|
        @userEntry.each do |u|
          if cu.room_id == u.room_id then
            @isRoom = true
            @roomId = cu.room_id
          end
        end
      end
      if @isRoom
      else
        @room = Room.new
        @entry = Entry.new
      end
    end
  end

解説
わかりにくいので、今回使用しない記述は消しています

@
  • where テーブル内の条件に一致したレコードを配列の形で取得することができるメソッド
  • Entry モデルから ログインしているユーザーの id を取得
  • Entry モデルから選択したユーザーの id を取得
if @user
  • ログインしている user であるという条件をつけます
  • else でログインしてるユーザーでなかったらとします unless を使用しても平気です
  • room が作成されている場合とされていない場合に条件分岐させます
  • cu.room_id == u.room_id  で2つのroomのid が同じであるとする
  • true でroomが存在していることを書きます
if @isRoom
  • else で room が存在していない場合とします
  • Room の新しいインスタンスと Entry の新しいインスタンスを作成します

View 画面の変更

users/show.html.erb
      <% unless @user.id == current_user.id %>
        <% if (current_user.following? @user) && (@user.following? current_user) %>
        <% if @isRoom == true %>
          <p class="user-show-room"><a href="/rooms/<%= roomId %>" class="btn btn-primary btn-lg">chatへ</a>
        <% else %>
          <%= form_for @room do |f| %>
            <%= fields_for @entry do |e| %>
              <%= e.hidden_field :user_id, value: @user.id %>
            <% end %>
            <%= f.submit "chatを始める", class:"btn btn-primary btn-lg user-show-shat" %>
          <% end %>
        <% end %>
        <% end %>
      <% end %>

解説

unless
  • unless を使用し、現在ログインしているユーザーではなかったらと条件をつけます
  • 2行目の if で相互フォローしていると条件をつけます
  • 3行目の if で room がすでにある場合とない場合で条件分岐します
  • room がすでにある場合は roomId を指定し、room へのリンクをつけます
else
  • form_for rails で情報を送信するためのメソッドで使い方は
    <%= from_for('モデルクラスのインスタンス') do |f| %>
  • fields_for 同じフォームで別のモデルオブジェクトも編集したい場合に使用する
  • hidden_field form_with や form_for タグの中でパラメーターをユーザーが直接フォームから入力させないまま、値を受け渡したい時に使用するもの
  • value は値を指定します

コントローラーの作成

Room のコントローラーを作成します

$ rails g controller Rooms show

続いてコントローラーを定義します

rooms_controller
  def create
    @room = Room.create(user_id: current_user.id)
    @entry1 = Entry.create(:room_id => @room.id, :user_id => current_user.id)
    @entry2 = Entry.create(params.require(:entry).permit(:user_id, :room_id).merge(:room_id => @room.id))
    redirect_to "/rooms/#{@room.id}"
  end

  def show
    @room = Room.find(params[:id])
    if Entry.where(:user_id => current_user.id, :room_id => @room.id).present?
      @messages = @room.messages
      @message = Message.new
      @entries = @room.entries
      @myUserId = current_user.id
    else
      redirect_to root_path
    end
  end

解説

create
  • Room.create で room を作成し、 ログインしているユーザーの id を渡します
  • Entry.create で entryを作成し、 @room の id とログインしているユーザーの id を渡します
  • merge 他の条件と結合するという意味で、使い方は
    モデル.merge(他の条件,*rest)
  • paramsで受け取ったデータと @room の id を結びつけています
show
  • where メソッドはテーブル内の条件に一致したレコードを配列の形で取得することができるメソッド
  • present メソッドは変数の値が存在するかしないかによって後続の処理を変更したい場合に使用するメソッド

romms/show ページの作成

rooms/show.html.erb
<% @entries.each do |e| %>
  <% if @myUserId != e.user.id %>
    <h2><%= e.user.name %></h2>
  <% end %>
<% end %>

<% if @messages.present? %>
  <% @messages.each do |m| %>
    <% @myUserId == m.user.id %>
    <p><%= m.user.name %></p>
      <p><%= m.message %></p>
  <% end %>
<% end %>

<%= form_for @message do |f| %>
  <%= f.text_field :message, class:"form-text-field" %>
    <%= f.hidden_field :room_id, value: @room.id %>
      <%= f.submit "投稿" %>
<% end %>

ユーザーの名前とメッセージが送れるよう作成
レイアウトなどはお好みで

コントローラーを定義

messages_controller
  def create
    if Entry.where(:user_id => current_user.id, :room_id => params[:message][:room_id]).present?
      @message = Message.create(params.require(:message).permit(:message, :user_id, :content, :room_id).merge(:user_id => current_user.id))
      redirect_to "/rooms/#{@message.room_id}"
    else
      redirect_back(fallback_location: root_path)
    end
  end

最後に messages_controller にメッセージを create させる記述をします

こちらの記事を参考にさせて頂きました

Discussion