#目標
UserとShopがお互いにチャットでやり取りをできるようにする
#モデル設計
Userモデル:Deviseでログイン機能を実装
Shopモデル:Deviseでログイン機能を実装:
Roomモデル:どのUserがどのShopとチャットをしているのかという情報が格納される。UserとShopの中間テーブル
Messageモデル:どのルームでUserまたはShopのどちらが何を発信したのかという情報が格納される
#開発
##プロジェクトの作成
$ rails new chat_sample
##Userモデルの作成
####Gemfile
ログイン機能を実装するため、Gemfile
に以下を追加してください。
gem 'devise'
gemを追加したので以下のコマンドを入力してください。
$ bundle install
Railsのプロジェクトにdeviseをインストールさせます。
$ rails g devise:install
Userモデルをdeviseを用いて作っていきます。
$ rails g devise user name:string age:integer
$ rails g devise:controllers users
$ rails g devise:views users
$ rails db:migrate
##Shopモデルの作成
同様にShopモデルを作成します。
$ rails g devise shop name:string genre:string
$ rails g devise:controllers shops
$ rails g devise:views shops
$ rails db:migrate
#deviseのカスタマイズ
####devise.rb
devise.rb
の以下の部分を変更しよう
#以下を消す
# config.scoped_views = false
#以下を追加
config.scoped_views = true
これによりdeviseのビューを変更することができるようになりました。
###新規登録時にemail, password以外も登録できるようにする
まずはuserの新規登録時にname
とage
を登録できるようにする。
####users/registrations/new.html.erb
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<!--省略-->
<!--ここから追加部分 -->
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :age %><br />
<%= f.number_field :age %>
</div>
<!--ここまで追加部分 -->
<!--省略-->
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "users/shared/links" %>
####shops/registrations/new.html.erb
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<!--省略-->
<!--ここから追加部分 -->
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :genre %><br />
<%= f.text_field :genre %>
</div>
<!--ここまで追加部分 -->
<!--省略-->
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "shops/shared/links" %>
applicatoin_controller.rb
に以下を追加してください
####applicatoin_controller.rb
#省略
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
if resource_class == User
devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :age])
elsif resource_class == Shop
devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :genre])
else
super
end
end
#省略
これによりUserがログインした時はnameカラムとageカラムが同時に登録されるようになりました。
また、Shopがログインした時はnameカラムとgenreカラムが同時に登録されます。
#他のモデルの作成
$ rails g model room user:references shop:references
$ rails g model message room:references is_user:boolean content:text
$ rails db:migrate
##アソシエーションの作成
####user.rb
models/user.rb
に以下を追加
#以下を追加
has_many :rooms
####shop.rb
models/shop.rb
に以下を追加
#以下を追加
has_many :rooms
####room.rb
models/room.rb
に以下を追加
#以下を追加
has_many :messages
##コントローラーの作成
全体のトップページとなるtops_controller
を作成する.
$ rails g controller tops index
またチャット画面を作成、表示するためにrooms_controller
も作成する
$ rails g controller rooms show
また、メッセージを送るためにmessages_controller
も作成する
$ rails g controller messages
##ルーティングの作成
####routes.rb
config/routes.rb
を以下のように変更する。
Rails.application.routes.draw do
root 'tops#index'
devise_for :shops
devise_for :users
resources :rooms, :only => [:show, :create] do
resources :messages, :only => [:create]
end
end
##トップのビューの作成(/tops/index.html.erb)
###ログイン用のリンクの作成
####tops/index.html.erb
views/tops/index.html.erb
を以下のように変更する。
<h1>チャットサンプル</h1>
<h3>ユーザー</h3>
<%= link_to "ログイン", new_user_session_path %>
<%= link_to "新規登録", new_user_registration_path %>
<h3>ショップ</h3>
<%= link_to "ログイン", new_shop_session_path %>
<%= link_to "新規登録", new_shop_registration_path %>
$ rails s
で確認すると、ユーザーとショップそれぞれで「ログイン」、「新規登録」のリンクが貼られているはずです。
###ログインしている状態によって見た目を変更する
userとshopがログインするので使っている人の状態は以下の3つが考えられます。
① userとしてログインしている
② shopとしてログインしている
③ どちらでもログインしてない
これをビューで分けることによりログインしている状態で適切な情報が見られるようになります
・ userがログインしている時にはuserのログアウト用のリンク
・ shopがログインしている時にはshopのログアウト用のリンク
・ どちらもログインしてない時にはログイン用のリンク
を表示させます。
####tops/index.html.erb
<h1>チャットサンプル</h1>
<!--Userでログインしてたら-->
<% if user_signed_in? %>
~<%= current_user.name %>がログインしてます~
<%= link_to "ログアウト", destroy_user_session_path ,:method => :delete %>
<!--Shopでログインしてたら-->
<% elsif shop_signed_in? %>
~<%= current_shop.name %>がログインしてます~
<%= link_to "ログアウト", destroy_shop_session_path ,:method => :delete %>
<!--ログインしてなかったら-->
<% else %>
<h3>ユーザー</h3>
<%= link_to "ログイン", new_user_session_path %>
<%= link_to "新規登録", new_user_registration_path %>
<h3>ショップ</h3>
<%= link_to "ログイン", new_shop_session_path %>
<%= link_to "新規登録", new_shop_registration_path %>
<% end %>
##相手の一覧表示機能をつける
次に相手の情報を一覧表示させていきます。
・ userがログインしている時にはshopの名前
・ shopがログインしている時にはuserの名前
を一覧表示させられるようにしていきます。
####tops_controller.rb
class TopsController < ApplicationController
def index
if user_signed_in?
@shops = Shop.all
elsif shop_signed_in?
@users = User.all
end
end
end
userがログイン→Shopの全情報をとってきて@shops
にいれる
shopがログイン→Userの全情報をとってきて@users
にいれる
####tops/index.html.erb
これをビューで表示させましょう。tops/index.html.erb
を以下のように変更してください。
<h1>チャットサンプル</h1>
<!--Userでログインしてたら-->
<% if user_signed_in? %>
~<%= current_user.name %>がログインしてます~
<%= link_to "ログアウト", destroy_user_session_path ,:method => :delete %>
<br>
<h2>ショップ一覧</h2>
<br>
<% @shops.each do |shop| %>
<%= shop.name %>
<% end %>
<!--Shopでログインしてたら-->
<% elsif shop_signed_in? %>
~<%= current_shop.name %>がログインしてます~
<%= link_to "ログアウト", destroy_shop_session_path ,:method => :delete %>
<br>
<h2>ユーザー一覧</h2>
<br>
<% @users.each do |user| %>
<%= user.name %>
<% end %>
<!--ログインしてなかったら-->
<% else %>
<h3>ユーザー</h3>
<%= link_to "ログイン", new_user_session_path %>
<%= link_to "新規登録", new_user_registration_path %>
<h3>ショップ</h3>
<%= link_to "ログイン", new_shop_session_path %>
<%= link_to "新規登録", new_shop_registration_path %>
<% end %>
$ rails s
をターミナルに入力し、ブラウザでlocalhost:3000
を入力して確認してみてください。
each
メソッドでuser, shopの情報を順に表示させました。
##チャットルーム機能実装
ここからはtopページから実際のチャットページに飛べるようにしていきます(userやshopの詳細ページに一回飛んで、そこからチャットルームに飛ぶ方が実際はいいですww今回は簡易的なのでそこは省略ということで)
####tops_controller.rb
class TopsController < ApplicationController
def index
if user_signed_in?
@shops = Shop.all
rooms = current_user.rooms
#自分が入ってるroomの相手のidを格納する
@shop_ids = []
rooms.each do |r|
@shop_ids << r.shop_id
end
elsif shop_signed_in?
@users = User.all
rooms = current_shop.rooms
#自分が入ってるroomの相手のidを格納する
@user_ids = []
rooms.each do |r|
@user_ids << r.user_id
end
end
end
end
自分が入ってる Room
の相手の情報を配列に格納することで、viewで誰とのチャットなのか見られるようにします
####rooms_controller.rb
class RoomsController < ApplicationController
def show
@room = Room.find(params[:id]) #ルーム情報の取得
@message = Message.new #新規メッセージ投稿
@messages = @room.messages #このルームのメッセージを全て取得
if user_signed_in?
if @room.user.id == current_user.id
@shop = @room.shop
else
redirect_to "/"
end
elsif shop_signed_in?
if @room.shop.id == current_shop.id
@user = @room.user
else
redirect_to "/"
end
else
redirect_to "/"
end
end
def create
if user_signed_in?
#userがログインしてたらuser_idを, shopがログインしてたらshop_idを@roomにいれる
@room = Room.new(room_shop_params)
@room.user_id = current_user.id
elsif shop_signed_in?
@room = Room.new(room_user_params)
@room.shop_id = current_shop.id
else
redirect_to "/"
end
if @room.save
redirect_to :action => "show", :id => @room.id
else
redirect_to "/"
end
end
private
def room_shop_params
params.require(:room).permit(:shop_id)
end
def room_user_params
params.require(:room).permit(:user_id)
end
end
showでは新規メッセージ投稿ができたり、メッセージの閲覧ができるようにします
createでは適切な user_id
と shop_id
が入るようにしています
####messages_controller.rb
class MessagesController < ApplicationController
def create
@room = Room.find(params[:room_id])
@message = Message.new(message_params)
#メッセージがuserによるものだったらis_user=true, shopによるものだったらis_user=false
if user_signed_in?
@message.is_user = true
elsif shop_signed_in?
@message.is_user = false
end
@message.room_id = @room.id
if @message.save
redirect_to room_url(@room)
else
redirect_to room_url(@room)
end
end
private
def message_params
params.require(:message).permit(:content)
end
end
createではメッセージを作成するのですが、userとshopのどちらが話した内容なのかという情報も格納します。
####tops/index.html.erb
<h1>チャットサンプル</h1>
<!--Userでログインしてたら-->
<% if user_signed_in? %>
~<%= current_user.name %>がログインしてます~
<%= link_to "ログアウト", destroy_user_session_path ,:method => :delete %>
<br>
<h2>ショップ一覧</h2>
<br>
<% @shops.each do |shop| %>
<%= shop.name %>
<!-- もしチャットルームがあったらそのチャットページへ。なければ新たなチャットルームを作成 -->
<% if @shop_ids.include?(shop.id) %>
<br>
<%= link_to "チャットへ", room_path(current_user.rooms.find_by(shop_id: shop.id)) %><br>
<% else %>
<%= form_for Room.new do |f| %>
<%= f.hidden_field :shop_id, :value => shop.id %>
<%= f.submit %>
<% end %>
<% end %>
<% end %>
<!--Shopでログインしてたら-->
<% elsif shop_signed_in? %>
<h2>ユーザー一覧</h2>
<br>
~<%= current_shop.name %>がログインしてます~
<%= link_to "ログアウト", destroy_shop_session_path ,:method => :delete %>
<br>
<% @users.each do |user| %>
<%= user.name %>
<!-- もしチャットルームがあったらそのチャットページへ。なければ新たなチャットルームを作成 -->
<% if @user_ids.include?(user.id) %>
<br>
<%= link_to "チャットへ", room_path(current_shop.rooms.find_by(user_id: user.id)) %><br>
<% else %>
<%= form_for Room.new do |f| %>
<%= f.hidden_field :user_id, :value => user.id %>
<%= f.submit %>
<% end %>
<% end %>
<% end %>
<!--ログインしてなかったら-->
<% else %>
<h3>ユーザー</h3>
<%= link_to "ログイン", new_user_session_path %>
<%= link_to "新規登録", new_user_registration_path %>
<h3>ショップ</h3>
<%= link_to "ログイン", new_shop_session_path %>
<%= link_to "新規登録", new_shop_registration_path %>
<% end %>
####rooms/show.html.erb
<h1>チャットサンプル</h1>
<% if user_signed_in? %>
<h3><% @shop.name %>とのチャットルーム</h3>
<div class="chat-field">
<% @messages.each do |m| %>
<!-- メッセージがUserによるものだったら -->
<% if m.is_user %>
<!-- メッセージを右に寄せる -->
<div class="right-message">
<%= m.content %>
</div>
<!-- メッセージがShopによるものだったら -->
<% else %>
<!-- メッセージを左に寄せる -->
<div class="left-message">
<%= m.content %>
</div>
<% end %>
<% end %>
</div>
<% elsif shop_signed_in?%>
<h3><% @user.name %>とのチャットルーム</h3>
<div class="chat-field">
<% @messages.each do |m| %>
<!-- メッセージがUserによるものだったら -->
<% if m.is_user %>
<!-- メッセージを左に寄せる -->
<div class="left-message">
<%= m.content %>
</div>
<!-- メッセージがShopによるものだったら -->
<% else %>
<!-- メッセージを右に寄せる -->
<div class="right-message">
<%= m.content %>
</div>
<% end %>
<% end %>
</div>
<% end %>
<%= form_for [@room, @message] do |f| %>
<%= f.text_field :content %>
<%= f.submit "メッセージを送る"%>
<% end %>
誰が話しているのかによってトーク内容を左に表示させるか右に表示させるかを変化させます(LINEやMessengerのイメージ)
####application.css
.left-message{
text-align: left;
}
.right-message{
text-align: right;
}
#まとめ
userとshopの2つの視点から考えながらチャット機能を作成しました。
参考にしてみてください