Ruby
RubyOnRails

RailsでDM(ダイレクトメッセージ)を送れるようにしよう

目標

ユーザー間でDMを送り合えるようになる!!

イメージ

UserがいろんなUserとDMをできると考えます。
ここでは4つのモデルを通して、DMを実装します!
Userモデル: User情報
Roomモデル: 今回はチャットルームに2人のUserが入るイメージです
Entryモデル: どのUserがどのRoomに所属しているか
Messageモデル: UserがどのRoomでどんなMessageを送ったか

スクリーンショット 2018-08-07 1.24.51.png

いざ実装

プロジェクトの作成

$ rails new dm_sample

Userモデルの作成

今回はdeviseを使用して、ログインできるようにします。

Gemfileに以下を追加してください

gem 'devise'
$ bundle install

Railsプロジェクトにdeviseをインストールします

$ rails g devise:install

ここからUserモデルを作成していきます。

$ rails g devise:controllers users
$ rails g devise user name:string
$ rails g devise:views
$ rails db:migrate

他のモデルの作成

$ rails g model room name:string
$ rails g model entry user_id:integer room_id:integer
$ rails g model message user_id:integer room_id:integer content:text
$ rails db:migrate

アソシエーション

1対多の関係が複数発生したのでアソシエーションを行います。
app/modelsの中の次のファイルを以下のように書き換えてください。

user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  has_many :messages
  has_many :entries
end
entry.rb
class Entry < ApplicationRecord
  belongs_to :user
  belongs_to :room
end
room.rb
class Room < ApplicationRecord
  has_many :messages
  has_many :entries
end
message.rb
class Message < ApplicationRecord
  belongs_to :user
  belongs_to :room
end

コントローラーの作成

$ rails g controller users index show
$ rails g controller rooms
$ rails g controller messages
users_controller.rb
class UsersController < ApplicationController
  before_action :authenticate_user!, :only => [:show]
  def index
    @users=User.all
  end

  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
end

rooms_controller.rb
class RoomsController < ApplicationController
  before_action :authenticate_user!
  def create
    @room = Room.create
    @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
    else
      redirect_back(fallback_location: root_path)
    end
  end
end
messages_controller.rb
class MessagesController < ApplicationController
  before_action :authenticate_user!, :only => [:create]

  def create
    if Entry.where(:user_id => current_user.id, :room_id => params[:message][:room_id]).present?
      @message = Message.create(params.require(:message).permit(: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
end

ルーティングの作成

route.rb
Rails.application.routes.draw do
  devise_for :users
  resources :users, :only => [:index, :show]
  root "users#index"
  resources :messages, :only => [:create]
  resources :rooms, :only => [:create, :show, :index]
end

ビューの作成

users/index.html.erb
<h1>DMサンプルアプリ</h1>
<% if user_signed_in? %>
<h2>Hello <%= current_user.email %></h2>
  <% @users.each do |u| %>
  <p><a href="/users/<%= u.id %>"><%= u.email %>さん</a></p>
  <hr>
  <% end %>
  <%= link_to "ログアウト", destroy_user_session_path, :method => :delete %>
<% else %>
  <%= link_to "ユーザーを登録する", new_user_registration_path %>
  <br>
  <%= link_to "ログインする", new_user_session_path %>
<% end %>
users/show.html.erb
<h1>ユーザー詳細</h1>

<h2><%= @user.email %></h2>

<% if @user.id == current_user.id %>

<% else %>
  <% if @isRoom == true %>
    <p><a href="/rooms/<%= @roomId %>">チャットへ</a>
  <% else %>
    <%= form_for @room do |f| %>
      <%= fields_for @entry do |e| %>
        <%= e.hidden_field :user_id, :value=> @user.id %>
      <% end %>
      <%= f.submit "チャットを始める" %>
    <% end %>
  <% end %>
<% end %>

<%= link_to "ユーザー一覧に戻る", users_path %>
rooms/show.html.erb
<h1>DM</h1>

<h4>参加者</h4>
<% @entries.each do |e| %>
  <h5><strong><a href="/users/<%= e.user.id %>"><%= e.user.email%>さん</a></strong></h5>
<% end %>

<hr>
<% if @messages.present? %>
  <% @messages.each do |m| %>
    <strong><%= m.content %></strong>
    <small>by <strong><a href="/users/<%= m.user_id %>"><%= m.user.email %>さん</a></strong></small>
    <hr>
  <% end %>
<% else %>
  <h3 class="text-center">メッセージはまだありません</h3>
<% end %>

<%= form_for @message do |f| %>
  <%= f.text_field :content, :placeholder => "メッセージを入力して下さい" , :size => 70 %>
  <%= f.hidden_field :room_id, :value => @room.id %>
  <br>
  <%= f.submit "投稿する" %>
<% end %>

<%= link_to "ユーザー一覧に戻る", users_path %>

完成

これでユーザー同士のDM機能が完成しました。viewにcssを全く加えていないのでかなり簡素なものとなっています。