Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

目標

ユーザー間で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'

gemを追加したので以下のコマンドを実行しましょう。

$ bundle install

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

$ rails g devise:install

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

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

他のモデルの作成

次にRoomモデル、 Entryモデル、Messageモデルを作成します。

$ rails g model room name:string
$ rails g model entry user:references room:references
$ rails g model message user:references room:references content:text
$ rails db:migrate

アソシエーション

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

user.rb

user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  has_many :messages, dependent: :destroy
  has_many :entries, dependent: :destroy
end

entry.rb

entry.rb
class Entry < ApplicationRecord
  belongs_to :user
  belongs_to :room
end

room.rb

room.rb
class Room < ApplicationRecord
  has_many :messages, dependent: :destroy
  has_many :entries, dependent: :destroy
end

message.rb

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を全く加えていないのでかなり簡素なものとなっています。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away