はじめに
Ruby on Rals チュートリアル(5.1版)の課題の「DM機能」を作成します。
チュートリアルの続きから機能を追加しているためUserモデル・ヘルパーメソッドの作成などは行っておりませんのでご注意下さい。
また、Action Cableを使用しておらずメッセージを送信する度に全てのメッセージ取得するという処理を行っています。
なおこの記事では
Ruby 2.7.2
Rails 5.1
を使用しております。
作成するもの
users/:id/messages にDMを表示し、メッセージ送信機能も持たせることにします。
messageモデルの作成、Userモデルとの関連付け
$ rails g model Message from_id:integer to_id:integer content:text
送信者のid,受信者のidでメッセージを判別しております。
class CreateMessages < ActiveRecord::Migration[5.1]
def change
create_table :messages do |t|
t.integer :from_id
t.integer :to_id
t.text :content
t.timestamps
end
add_index :messages, :from_id
add_index :messages, :to_id
end
end
from_id,to_idにindexを張り、migrationを行います。
$ rails db:migrate
class Message < ApplicationRecord
default_scope -> { order(created_at: :asc) }
validates :from_id, presence: true
validates :to_id, presence: true
validates :content, presence: true, length: { maximum: 300 }
end
メッセージの並び順、バリデーションを設定します。
class User < ApplicationRecord
has_many :outgoing_messages, class_name: "Message",
foreign_key: "from_id",
dependent: :destroy
has_many :incoming_messages, class_name: "Message",
foreign_key: "to_id",
dependent: :destroy
.
.
.
end
Userモデルとの紐付を行います。
ルーティング
Rails.application.routes.draw do
resources :users do
member do
resources :messages, only: [:create, :index]
end
end
end
以下のルーティングが完成
GET /users/:id/messages(.:format) messages#index
POST /users/:id/messages(.:format) messages#create
indexアクションで特定の相手とのメッセージ一覧をレンダリングし、createアクションで新規メッセージを送信+viewの更新を行います。
Messagesコントローラーの処理
$ rails g controller Messages index create
Messagesコントローラーを作成します。
class MessagesController < ApplicationController
before_action :setup_users
def index
@messages = Message.where("from_id IN (:ids) AND to_id IN (:ids)",ids:@ids)
@message = Message.new
end
def create
@message = current_user.outgoing_messages.build(message_params)
@message.to_id = params[:id]
@message.save
@messages = Message.where("from_id IN (:ids) AND to_id IN (:ids)",ids:@ids)
respond_to do |format|
format.html { redirect_to messages_urL(@to_user)}
format.js { render "create"}
end
end
private
def message_params
params.require(:message).permit(:content)
end
def setup_users
@to_user = User.find(params[:id])
@ids = [@to_user.id,current_user.id]
end
end
indexメソッドでは
fromまたはtoカラムに自分または相手のidを含むmessageを取り出しインスタンス変数に保存しております。
createメソッドでの処理
今回はAction Cableを使用しないので相手が送信した新規のメッセージを取得するためにメッセージ送信の失敗・成功に関わらずメッセージを全て取得します。
また、current_userメソッドはrails tutorialで作成したものです。
View
まずはindexメソッドからレンダリングされるindex.html.erbです。
<h2><%= @to_user.name %></h2>
<ol id="messages">
<%= render @messages %>
</ol>
<div id="error_messages">
<%= render "shared/error_messages", object:@message %>
</div>
<%= render "shared/message_form" %>
<li <%= "class = my-content" if message.from_id == current_user.id %>>
<%= gravatar_for message.from, size: 50 %>
<div class="content">
<span class="message">
<%= message.content %>
</span>
<span class="timestamp">
<%= message.created_at %>
</span>
</div>
</li>
Twitterなどで見られるように相手のメッセージと自分のメッセージの表示方法を変えるため、自分のメッセージの場合は別のcssを適用するためのクラス名をつける処理を行っています。
<% provide(:button_text, 'Send Message') %>
<%= form_with model: @message do |f| %>
<%= f.text_area :content, class: 'form-control' %>
<%= f.submit yield(:button_text), class: "btn btn-primary" %>
<% end %>
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
次にcreateメソッドでレンダリングされるviewです。
$(".form-control").val("");
$("#messages").html("<%= escape_javascript(render(@messages)) %>");
$("#error_messages").html("<%= escape_javascript(render('shared/error_messages', object: @message)) %>");
フォームのクリア、メッセージの取得、エラーメッセージの表示を行います。
scss
// messages
# messages {
list-style: none;
padding: 0;
li {
padding: 10px 0;
// border-top: 1px solid #e8e8e8;
display: flex;
justify-content: flex-start;
}
.content {
display: inline-block;
.message {
padding: 20px;
display: inline-block;
background:$gray-medium-light;
border-radius: 10px;
}
.timestamp {
color: $gray-light;
display: block;
font-size: .75em;
}
}
.gravatar {
margin-right: 10px;
width: 40px;
height: 40px;
}
li.my-content{
justify-content: flex-end;
text-align: right;
.message {
background:#71151a;
border-radius: 10px;
color: #fff;
}
.gravatar{
display: none;
}
}
}
完成
今のところ自分がメッセージを送信した時しか新しいメッセージを取得することが出来ていないので、Action Cableを利用してリアルタイムチャット機能の追加などを行いたいと思います。