【Rails】通知機能の実装

#実装機能
フォローまたはチャットの通知を一覧で受け取る
未読のものがあった場合、ヘッダー部分に未読マークをつける

#モデル作成

```
$ rails g model Notification
```

```Ruby:00000000000_create_notifications.rb
class CreateNotifications < ActiveRecord::Migration[5.2]
  def change
    create_table :notifications do |t|
      t.integer :visitor_id, null: false
      t.integer :visited_id, null: false
      t.integer :relationship_id
      t.integer :chat_id
      t.string :action, default: '', null: false
      t.boolean :checked, default: false, null: false

      t.timestamps
    end

    add_index :notifications, :visitor_id
    add_index :notifications, :visited_id
    add_index :notifications, :relationship_id
    add_index :notifications, :chat_id
  end
end
```

・visitor_id (通知を送ったユーザーのid)
・visited_id　(通知を送られたユーザーのid)
・relationship_id　（フォローid）
・chat_id　（チャットid）
・action （通知の種類（フォロー、チャット））
・checked　（通知を送られたユーザーが通知を確認したかどうか）

```
$ rails db:migrate
```


#関連付け　アソシエーション

```Ruby:app/models/user.rb
  has_many :active_notifications, class_name: 'Notification', foreign_key: 'visitor_id', dependent: :destroy
  has_many :passive_notifications, class_name: 'Notification', foreign_key: 'visited_id', dependent: :destroy
```

・active_notifications（自分からの通知）
・passive_notifications（相手からの通知）

```Ruby:app/models/relationship.rb
  has_many :notifications, dependent: :destroy
```

```Ruby:app/models/chat.rb
  has_many :notifications, dependent: :destroy
```

```Ruby:app/models/notification.rb
class Notification < ApplicationRecord
  default_scope -> { order(created_at: :desc) }
  belongs_to :relationship, optional: true
  belongs_to :chat, optional: true

  belongs_to :visitor, class_name: 'User', foreign_key: 'visitor_id', optional: true
  belongs_to :visited, class_name: 'User', foreign_key: 'visited_id', optional: true
end
```

・optional: true　（nilを許容）

#通知メソッド実装
フォローされた時、チャットが送られてきた時に通知がnotificationテーブルに登録されるように

```Ruby:app/models/chat.rb
 def create_notification_chat!(current_user, chat_id, room_id, visited_id)
    # チャットしている相手を取得し、通知を送る
    temp_ids = Chat.select(:user_id).where(room_id: room_id).where.not(user_id: current_user.id).distinct
    temp_ids.each do |temp_id|
      save_notification_chat!(current_user, chat_id, temp_id['user_id'])
    end
     # もしチャットが空だったら、投稿者に通知を送る
    save_notification_chat!(current_user, chat_id, visited_id) if temp_ids.blank?
  end

  def save_notification_chat!(current_user, chat_id, visited_id)
    # チャットは複数回することが考えられるため、複数回通知する
    notification = current_user.active_notifications.new(
      chat_id: chat_id,
      visited_id: visited_id,
      action: 'chat'
    )
    # 自分のチャットの場合は、通知済みとする
    if notification.visitor_id == notification.visited_id
      notification.checked = true
    end
    notification.save if notification.valid?
  end
```

```Ruby:app/models/user.rb
 # フォロー通知
  def create_notification_follow!(current_user)
    temp = Notification.where(["visitor_id = ? and visited_id = ? and action = ? ",current_user.id, id, 'follow'])
    if temp.blank?
      notification = current_user.active_notifications.new(
        visited_id: id,
        action: 'follow'
      )
      notification.save if notification.valid?
    end
  end
```

#コントローラー作成

```
$ rails g controller notifications
```

```Ruby:app/controllers/notifications_controller.rb
class NotificationsController < ApplicationController
  def index
    @notifications = current_user.passive_notifications.page(params[:page]).per(20)
    @notifications.where(checked: false).each do |notification|
      notification.update_attributes(checked: true)
    end
  end
end
```

通知一覧表示
見たら未読false→既読trueと変わるよう指定

#ルーティング

```Ruby:config/routes.rb
 resources :notifications, only: :index
```

#ビュー
部分テンプレート

```erb:app/views/notifications/_notification.html.erb
<% visitor = notification.visitor %>
<% visited = notification.visited %>

<div class='m-2'>
  <%= link_to user_path(visitor) do %>
    <span class="visitor_user_image_box"><%= attachment_image_tag visitor, :image, class:"visitor_user_image", fallback: "logo.png" %></span>
    <%= visitor.nickname %>
  <% end %>
  <span>さんが</span>

  <% case notification.action %>
    <% when 'follow' then %>
      <%= "あなたをフォローしました" %>
    <% when 'chat' then %>
      <%= link_to 'あなたにメッセージ', chat_path(notification.visitor), style:"font-weight: bold;", class: "border-bottom" %>
      <%= "を送りました" %>
  <% end %>
  <span><%= " (#{time_ago_in_words(notification.created_at)}前)" %></span><br>
</div>
```

```erb:app/views/notifications/index.html.erb
<div class="text-center">
  <% notifications = @notifications.where.not(visitor_id: current_user.id) %>
  <!--自分の投稿に対するいいね、コメントは通知に表示しない-->

  <% if notifications.exists? %>
    <%= render notifications %>
    <%= paginate notifications %>
  <% else %>
   <p>通知はありません</p>
  <% end %>
</div>
```

#未読の通知があることを知らせる

```erb:app/views/layouts/application.html.erb
<% if unchecked_notifications.any? %>
  <span>
    <i class="fas fa-circle n-circle"></i>
  </span>
<% end %>
<%= link_to "通知一覧", notifications_path %>
```

・fas fa-circle（fontawesome）
・n-circle（CSSで色付け　color: #efa04c;）