rails メッセージ機能実装

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

# 目次

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

# 1,モデルを作成する

```bash:ターミナル
$ rails g model message
```

```ruby: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
```

```bash:ターミナル
$ rails db:migrate 
```

## アソシエーションの定義

```ruby: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カラムが空だったらという意味です。

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


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

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

```ruby: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を使用します。

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

```bash:ターミナル
$ brew install imagemagick
```

```ruby:Gemfile
# 〜省略〜
gem 'carrierwave'
gem 'mini_magick'
```

```bash:ターミナル
$ bundle install
```

### railsでuploader image追加

```bash:ターミナル
$ rails g uploader image
```

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

```ruby:app/models/message.rb
# 〜省略〜
  mount_uploader :image, ImageUploader
```

```ruby: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
```

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

```ruby: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
```

```haml: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'
```

```haml:app/views/messages/index.html.haml
.wrapper
  = render "shared/side_bar"
  = render "main_chat"
```
# 5,グループにメッセージを表示する

```haml:app/views/messages/_main_chat.html.haml
.messages
  = render @messages
```

```haml: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?
```

```haml: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という記述で部分テンプレートを呼び出しています。
これは、以下のコードを省略した書き方です。

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

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

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

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

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

```ruby: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
```


```ruby: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行で場合分けを行えるので、記述を短くまとめたい際に重宝します。

```bash
# 三項演算子の書き方

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

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

```ruby
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. グループ編集後のリダイレクト先を変更する