コマンド
rails g model Conversation sender_id:bigint recipient_id:bigint
コマンド
rails g model Message context:text user:references conversation:references
コマンド マイグレーション
rails db:migrate
app\models\conversation.rb
class Conversation < ApplicationRecord belongs_to :sender, foreign_key: :sender_id, class_name: "User" belongs_to :recipient, foreign_key: :recipient_id, class_name: "User" has_many :messages, dependent: :destroy validates :sender_id, :uniqueness => {:scope => :recipient_id} scope :involving, -> (user) { where("conversations.sender_id = ? OR conversations.recipient_id = ?", user.id, user.id) } scope :between, -> (user_A, user_B) { where("(conversations.sender_id = ? AND conversations.recipient_id = ?) OR (conversations.sender_id = ? AND conversations.recipient_id = ?)", user_A, user_B, user_B, user_A) } end
app\models\message.rb
class Message < ApplicationRecord belongs_to :user belongs_to :conversation validates_presence_of :context, :conversation_id, :user_id def message_time self.created_at.strftime('%Y年%-m月%-d日 %-H時%-M分') end end
「app\controllers」フォルダに「conversations_controller.rb」ファイルを新規作成してください。
app\controllers\conversations_controller.rb(新規作成したファイル)
class ConversationsController < ApplicationController before_action :authenticate_user! def index @conversations = Conversation.involving(current_user) end def create if Conversation.between(params[:sender_id], params[:recipient_id]).present? @conversation = Conversation.between(params[:sender_id], params[:recipient_id]).first else @conversation = Conversation.create(conversation_params) end redirect_to conversation_messages_path(@conversation) end private def conversation_params params.permit(:sender_id, :recipient_id) end end
「app\controllers」フォルダに「messages_controller.rb」ファイルを新規作成してください。
app\controllers\messages_controller.rb(新規作成したファイル)
class MessagesController < ApplicationController before_action :authenticate_user! before_action :set_conversation def index if current_user == @conversation.sender || current_user == @conversation.recipient @other = current_user == @conversation.sender ? @conversation.recipient : @conversation.sender @messages = @conversation.messages.order("created_at DESC") else redirect_to conversations_path, alert: "権限がありません。" end end def create @message = @conversation.messages.new(message_params) @messages = @conversation.messages.order("created_at DESC") if @message.save redirect_to conversation_messages_path(@conversation) end end private def set_conversation @conversation = Conversation.find(params[:conversation_id]) end def message_params params.require(:message).permit(:context, :user_id) end end
記述追加 config\routes.rb(54行目)
resources :conversations, only: [:index, :create] do resources :messages, only: [:index, :create] end
config\routes.rb
Rails.application.routes.draw do #ルートをpages#homeに設定 root 'pages#home' get 'pages/home' get '/your_trips' => 'reservations#your_trips' get '/your_reservations' => 'reservations#your_reservations' get 'search' => 'pages#search' get 'dashboard' => 'dashboards#index' get '/host_calendar' => "calendars#host" get '/payment_method' => "users#payment" get '/payout_method' => "users#payout" get '/notification_settings' => 'settings#edit' post '/add_card' => "users#add_card" post '/notification_settings' => 'settings#update' resources :users, only: [:show] do member do post '/verify_phone_number' => 'users#verify_phone_number' patch '/update_phone_number' => 'users#update_phone_number' end end resources :rooms, except: [:edit] do member do get 'listing' get 'pricing' get 'description' get 'photo_upload' get 'amenities' get 'location' get 'preload' get 'preview' end resources :photos, only: [:create, :destroy] resources :reservations, only: [:create] resources :calendars end resources :guest_reviews, only: [:create, :destroy] resources :host_reviews, only: [:create, :destroy] resources :revenues, only: [:index] resources :reservations, only: [:approve, :decline] do member do post '/approve' => "reservations#approve" post '/decline' => "reservations#decline" end end resources :conversations, only: [:index, :create] do resources :messages, only: [:index, :create] end devise_for :users, path: '', path_names: {sign_in: 'login', sign_out: 'logout', edit: 'profile', sign_up: 'registration'}, controllers: {omniauth_callbacks: 'omniauth_callbacks', registrations: 'registrations'} # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end
「app\views」フォルダに「conversations」フォルダを新規作成して下さい。
作成した「conversations」フォルダに「index.html.erb」ファイルを新規作成します。
app\views\conversations\index.html.erb(新規作成したファイル)
<div class="row"> <div class="col-md-12"> <div class="panel panel-default"> <div class="panel-heading">あなたの会話</div> <div class="panel-body"> <div class="container"> <% @conversations.each do |conversation| %> <% other = current_user == conversation.sender ? conversation.recipient : conversation.sender %> <%= link_to conversation_messages_path(conversation), data: { turbolinks: false} do %> <div class="row conversation"> <% if conversation.messages.any? %> <div class="col-md-2"> <%= image_tag avatar_url(other), class: "img-circle avatar-medium" %> </div> <div class="col-md-3" style="white-space: nowrap"> <%= other.fullname %><br/> <%= conversation.messages.last.message_time %> </div> <div class="col-md-7"> <%= conversation.messages.last.context %> </div> <% end %> </div> <% end %> <% end %> </div> </div> </div> </div> </div>
「app\views」フォルダに「messages」フォルダを新規作成してください。
作成した「messages」フォルダに「index.html.erb」ファイルを新規作成します。
app\views\messages\index.html.erb(新規作成したファイル)
<div class="row"> <div class="col-md-3 text-center"> <%= image_tag avatar_url(@other), class: "img-circle avatar-medium" %> <strong><%= @other.fullname %></strong> <%= link_to "プロフィールを見る", @other, class: "btn btn-default" %> </div> <div class="col-md-9"> <div class="panel panel-default"> <div class="panel-heading"> <%= @other.fullname %> さんとの会話内容 <input id="conversation_id" type="hidden" value="<%= @conversation.id %>"> </div> <div class="panel-body"> <div class="container text-center"> <%= form_for [@conversation, @conversation.messages.new], remote: true do |f| %> <div class="form-group"> <%= f.text_field :context, placeholder: "メッセージを入力してください。", class: "form-control" %> </div> <%= f.hidden_field :user_id, value: current_user.id %> <div> <%= f.submit "メッセージを送る", class: "btn btn-normal" %> </div> <% end %> </div> </div> </div> <div id="chat"> <%= render @messages %> </div> </div> </div>
「app\views\messages」フォルダに「_message.html.erb」ファイルを新規作成してください。
app\views\messages\_message.html.erb(新規作成したファイル)
<div class="panel"> <div class="panel-body"> <%= image_tag message.user.gravatar_url, class: "img-circle avatar-medium" %> <strong><%= message.user.fullname %></strong> <span class="pull-right"><%= message.message_time %></span> <br/> <div class="row-space-2"> <%= message.context %> </div> </div> </div>
記述追加 app\views\users\show.html.erb(5行目)
<% if current_user && current_user != @user %> <%= link_to "メッセージを送る", conversations_path(sender_id: current_user.id, recipient_id: @user.id), method: :post, class: "btn btn-default" %> <% end %>
app\views\users\show.html.erb
<div class="row"> <div class="col-md-3"> <div class="center"> <%= image_tag @user.gravatar_url, class: "avatar-full" %> <% if current_user && current_user != @user %> <%= link_to "メッセージを送る", conversations_path(sender_id: current_user.id, recipient_id: @user.id), method: :post, class: "btn btn-default" %> <% end %> </div> <div class="panel panel-default"> <div class="panel-heading" style="text-align: center;"><%= @user.fullname %></div> <br/> <div class="center"> <%= @user.description %> </div> <hr/> <div class="panel-body"> <ul class="sidebar-list"> <% if @user.confirmation_token %> <li>Emailアドレス<span class="pull-right icon-babu"><i class="fa fa-check-circle-o"></i></span></li> <% end %> <% if @user.phone_verified %> <li>電話番号<span class="pull-right icon-babu"><i class="fa fa-check-circle-o"></i></span></li> <% end %> </ul> </div> </div> </div> <div class="col-md-9"> <br/> <h4><%= @user.fullname %>さんが登録しているお部屋(<%= @rooms.length %>)</h4><br/> <div class="row"> <%= render partial: "rooms/rooms_list", locals: {rooms: @rooms} %> </div> <br/> <h4>ゲストからのレビュー (<%= @guest_reviews.count %>)</h4> <div class="row"> <%= render "reviews/guest_list" %> </div> <br/> <h4>ホストからのレビュー (<%= @host_reviews.count %>)</h4> <div class="row"> <%= render "reviews/host_list" %> </div> <br/> </div> </div>
ブラウザ確認
http://localhost:3000/users/4
ユーザプロフィールに「メッセージを送る」ボタンが出ます。
メッセージを入力します。
メッセージが表示されます。
ナビゲーションバーのリンクを更新します。
記述追加 app\views\shared\_navbar.html.erb
38行目と61行目にリンクを追加しています。
<!-- ナビゲーションバー --> <nav class="navbar navbar-default navbar-static-top" style ="margin: 0;"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">ナビゲーション トグル</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <%= link_to "テストサイトMinpaku", root_path, class: "navbar-brand" %> </div> <div id="navbar" class="navbar-collapse collapse"> <ul class="nav navbar-nav navbar-right"> <% if (!user_signed_in?) %> <li><%= link_to "ログイン", new_user_session_path %></li> <li><%= link_to "新規ユーザ登録", new_user_registration_path %></li> <% else %> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> <!-- アバター画像 --> <%= image_tag current_user.gravatar_url, class: "img-circle avatar-small" %> <!-- 氏名表示に変更 --> <%= current_user.fullname %> <span class="caret"></span> </a> <ul class="dropdown-menu"> <li><%= link_to "ダッシュボード", dashboard_path %></li> <li><%= link_to "登録したお部屋の管理", rooms_path %></li> <li><%= link_to "お部屋の新規登録", new_room_path %></li> <li><%= link_to "受注予約の管理(ホスト用)", your_reservations_path %></li> <li><%= link_to "予約履歴(ゲスト用)", your_trips_path %></li> <li><%= link_to "ホスト用カレンダー", host_calendar_path %></li> <li><%= link_to "収益", revenues_path %></li> <li><%= link_to "クレジットカード登録", payment_method_path, data: { turbolinks: false} %></li> <li><%= link_to "振り込み口座登録", payout_method_path %></li> <li><%= link_to "メッセージを見る", conversations_path %></li> <li role="separator" class="divider"></li> <li><%= link_to "ユーザ登録情報修正", edit_user_registration_path %></li> <li><%= link_to "予約メール通知設定", notification_settings_path %></li> <li><%= link_to "ログアウト", destroy_user_session_path, method: :delete %></li> </ul> </li> <% end %> </ul> </div> </div> </nav> <% if (user_signed_in?) && !current_page?(root_path) && !current_page?("/rooms/#{params[:id]}") %> <nav class="navbar navbar-default" style="background-color: #89dfd7;"> <div class="container"> <ul class="nav navbar-nav"> <li <% if current_page?(dashboard_path) %> class="active" <% end %> > <%= link_to "ダッシュボード", dashboard_path %></li> <li <% if current_page?(host_calendar_path) %> class="active" <% end %> ><%= link_to "ホスト用カレンダー", host_calendar_path %></li> <li <% if current_page?(revenues_path) %> class="active" <% end %> ><%= link_to "収益", revenues_path %></li> <li <% if current_page?(payment_method_path) %> class="active" <% end %> ><%= link_to "クレジットカード登録", payment_method_path, data: { turbolinks: false} %></li> <li <% if current_page?(payout_method_path) %> class="active" <% end %> ><%= link_to "振り込み口座登録", payout_method_path %></li> <li <% if current_page?(notification_settings_path) %> class="active" <% end %> ><%= link_to "予約メール通知設定", notification_settings_path %></li> <li <% if current_page?(conversations_path) %> class="active" <% end %> ><%= link_to "メッセージを見る", conversations_path %></li> </ul> </div> </nav> <% end %>