LoginSignup
12

More than 3 years have passed since last update.

rails メッセージ機能実装

Posted at

少しまがあきました
以前の続きを

目次

1,モデルを作成する
2,ルーティングを設定する
3,該当するアクションをコントローラに定義する
4,メッセージ送信機能を実装する
5,グループにメッセージを表示する
6,サイドバーに最新のメッセージを表示する
7,細かいリンクの作成

1,モデルを作成する

ターミナル
$ rails g model message
202XXXXXXXXXXX_create_messages.rb
class CreateMessages < ActiveRecord::Migration[5.2]
  def change
    create_table :messages do |t|
      t.string :content
      t.string :image
      t.references :group, foreign_key: true
      t.references :user, foreign_key: true
      t.timestamps
    end
  end
end
ターミナル
$ rails db:migrate 

アソシエーションの定義

app/models/message.rb
class Message < ApplicationRecord
  belongs_to :group
  belongs_to :user

  validates :content, presence: true, unless: :image?
end

前半のvalidates :content, presence: trueは
contentカラムが空の場合は保存しない、というバリデーションです。

後半で、unless: :image?という条件を追加しています。
unlessはifの逆の役割があります。if: :image?であれば
imageカラムが空でなければという意味になりますので
unless: :image?はimageカラムが空だったらという意味です。

app/models/group.rb
class Group < ApplicationRecord
# 〜省略〜
  has_many :messages
end
app/models/user.rb
class User < ApplicationRecord
# 〜省略〜
  has_many :messages
end

2,ルーティングを設定する

routes
Rails.application.routes.draw do
  devise_for :users
  root 'groups#index'
  resources :users, only: [:edit, :update]
  # 以下のgroupsとメッセージをネストして作成
  resources :groups, only: [:new, :create, :edit, :update] do
    resources :messages, only: [:index, :create]
  end
end

3. 該当するアクションをコントローラに定義する

2で設定したルーティングで必要なアクションをコントローラに定義します。

4,メッセージ送信機能を実装する

CarrierWave導入

機能の一つとして
画像の送信には、CarrierWaveというgemを使用します。

まず導入から記載して行きます。

ターミナル
$ brew install imagemagick
Gemfile
# 〜省略〜
gem 'carrierwave'
gem 'mini_magick'
ターミナル
$ bundle install

railsでuploader image追加

ターミナル
$ rails g uploader image

rails g uploader imageコマンドを実行すると
app/uploadersディレクトリ以下にimage_uploader.rbが作成されます。

app/models/message.rb
# 〜省略〜
  mount_uploader :image, ImageUploader
app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  include CarrierWave::MiniMagick # ①ここのコメントアウトを外してください。
  # storage :fog
# 〜省略〜
  process resize_to_fit: [800, 800] # ②任意の箇所に追記してください
                                    # resize_to_fitは縦横比を維持したまま、縦横を800px以内にリサイズするという意味です。
# 〜省略〜
end

メッセージ送信機能を実装

messages_controller.rb
class MessagesController < ApplicationController
  before_action :set_group #①事前動作の設定

  def index
    @message = Message.new
    @messages = @group.messages.includes(:user)# 「n+1問題」を避けるために、includes(:user)の記述を忘れずに行いましょう。
  end

  def create #②createアクションの定義
    @message = @group.messages.new(message_params)
    if @message.save
      redirect_to group_messages_path(@group), notice: 'メッセージが送信されました'
    else
      @messages = @group.messages.includes(:user)
      flash.now[:alert] = 'メッセージを入力してください。'
      render :index
    end
  end

  private

  def message_params
    params.require(:message).permit(:content, :image).merge(user_id: current_user.id)
  end

  def set_group #③ストロングパラメーターに設定
    @group = Group.find(params[:group_id])
  end
end
app/views/messages/_main_chat.html.haml
# 〜省略〜
    .form
      = form_for [@group, @message] do |f|
        = f.text_field :content, class: 'form__message', placeholder: 'type a message'
        .form__mask
          = f.label :image, class: 'form__mask__image' do
            = icon('fas', 'image', class: 'icon')
            = f.file_field :image, class: 'hidden'
        = f.submit 'Send', class: 'form__submit'
app/views/messages/index.html.haml
.wrapper
  = render "shared/side_bar"
  = render "main_chat"

5,グループにメッセージを表示する

app/views/messages/_main_chat.html.haml
.messages
  = render @messages
app/views/messages/_message.html.haml
.message
  .upper-message
    .upper-message__user-name
      = message.user.name
    .upper-message__date
      = message.created_at.strftime("%Y年%m月%d日 %H時%M分")
  .lower-message
    - if message.content.present?
      %p.lower-message__content
        = message.content
    = image_tag message.image.url, class: 'lower-message__image' if message.image.present?
views/shared/_side_bar.html.haml
.side-bar
  .header
    %h3.header__name
      = current_user.name
    %ul.header__lists
      %li.list
        = link_to new_group_path do
          = icon('fas', 'edit', class: 'icon')
      %li.list
        = link_to edit_user_path(current_user) do
          = icon('fas', 'cog', class: 'icon')
  .groups
    - current_user.groups.each do |group|
      .group
        = link_to group_messages_path(group) do
          .group__name
            = group.name
          .group__message
            メッセージはまだありません

他の呼び出し方

上記では、render @messagesという記述で部分テンプレートを呼び出しています。
これは、以下のコードを省略した書き方です。

インスタンス変数の名前を単数形にしたものと
部分テンプレートの名前が同じならこのような省略をすることができます。

.messages
  = render partial: 'message', collection: @messages

下もありますが遅いのでやりませんでした。

.messages
  - @messages.each do |message|
    = render partial: "message", locals: { message: message }

6. サイドバーに最新のメッセージを表示する

app/models/group.rb
class Group < ApplicationRecord
  has_many :group_users
  has_many :messages
  has_many :users, through: :group_users

  validates :name, presence: true

# 下記のshow_last_messageを追加
  def show_last_message
    if (last_message = messages.last).present?
      last_message.content? ? last_message.content : '画像が投稿されています' # 三項演算子 参照
    else
      'まだメッセージはありません。'
    end
  end
end
views/shared/_side_bar.html.haml
.side-bar
  .header
    %h3.header__name
      = current_user.name
    %ul.header__lists
      %li.list
        = link_to new_group_path do
          = icon('fas', 'edit', class: 'icon')
      %li.list
        = link_to edit_user_path(current_user) do
          = icon('fas', 'cog', class: 'icon')
  .groups
    - current_user.groups.each do |group|
      .group
        = link_to group_messages_path(group) do
          .group__name
            = group.name
          .group__message
            = group.show_last_message # ここを追加してmodelの内容を引っ張って来る

三項演算子

show_last_messageの内容で使用
初めに条件式を記述し、その後にtrueの時の値、続いてfalseの時の値を取ります。
1行で場合分けを行えるので、記述を短くまとめたい際に重宝します。

# 三項演算子の書き方

条件式 ? trueの時の値 : falseの時の値

ifを用いて書き直すと、以下のようになります。

def show_last_message
    if (last_message = messages.last).present?
      if last_message.content?
       last_message.content
      else
        '画像が投稿されています'
      end
    else
      'まだメッセージはありません。'
    end

7.細かいリンクの作成

以下の3点をそれぞれ設定して終わりです。

1. ヘッダーにグループ名とユーザー名が表示されるようにする

2. グループ編集ページへのリンクを設置する

3. グループ編集後のリダイレクト先を変更する

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12