グループ招待機能の実装方法

※ 初めて書いた記事ですので、間違い等あればご指摘いただけますと幸いです。

## 概要
通知機能を利用して、チーム機能に招待機能を実装する。

## 前提
- ユーザー機能を実装済み
- チーム機能を実装済み
- 通知機能を実装済み

## 参考記事
通知機能はこちらの記事を参考させていただきました。
[【Rails】通知機能を誰でも実装できるように解説する【いいね、コメント、フォロー】](https://qiita.com/nekojoker/items/80448944ec9aaae48d0a)

## 通知モデルの概要
以下、テーブル情報の例です。

|visitor_id|visited_id|post_id|comment_id|action|checked|
|:-:|:-:|:-:|:-:|:-:|:-:|
|1|2|nil|nil|follow|false|
|1|2|3|nil|like|false|
|1|2|nil|nil|comment|false|

- visitor_id：通知を送ったユーザーのID
- visited_id：通知を送られた（受け取る）ユーザーのID
- post_id：いいねされた投稿
- comment_id：投稿へのコメントID
- action：通知の種類（ここでは`follow`,`like`,`comment`）
- checked：通知を確認したかどうか

## この記事の招待機能の概要

**チームに加入できるリンクが付いている通知を送る。**

|visitor_id|visited_id|team_id|action|checked|
|:-:|:-:|:-:|:-:|:-:|:-:|
|1|2|1|invitaion|false|

1. 上記のようなテーブル情報を持つ通知を生成し、通知一覧に表示させる。

2. 通知に記載されているリンクを踏むと招待されたチームに加入する。

このような流れでチームへの招待を実装します。

## 実装の手順

1. ルーティングを設定。（config/routes.rb）
- 通知生成用のメソッドを作成。（app/models）
- コントローラに処理を記述。（app/controllers）
- フォームを記述。（app/views）

本記事ではこの順番で実装を進めていきます。

## 実装

- ルーティングの設定。

```config/routes.rb
resources :teams, only: [:index, :show, :new, :create, :edit, :update] do
    get  :join
    post :invitation
end

```
招待通知を生成するためのアクション`invitation`。
チームに加入する処理を行うアクション`join`。
上記の２つのアクションのルーティングを記述します。
<br>

- 通知生成用のメソッドを作成。

```app/models/team.rb
def team_invitation_notification(current_user, visited_id, team_id)
    # すでに招待用の通知が送られているか検索。
    temp = Notification.where(visitor_id: current_user.id, visited_id: visited_id, team_id: team_id)
　　　　  # 上記で検索した通知がない場合のみ、通知レコードを作成。
    if temp.blank?
      notification = current_user.active_notifications.new(
        visited_id: visited_id,
        team_id: team_id,
        action: "invitation",
      )
      # エラーがなければ、通知レコードを保存。
      notification.save if notification.valid?
    end
  end
end
```
<br>

- コントローラに処理を記述。

招待通知生成の処理を行う`invitation`アクションを記述。

```app/controllers/teams_controller.rb
    def invitation
        @team = Team.find(params[:id])
　　　　　　　　　　　　　　　　# この後 view に設置するフォームの値を参照する。
        @user = User.find_by(id: params[:user_id])
        notification = Notification.where(visited_id: @user.id, team_id: @team.id, action: "invitation")
        unless notification.exists?
          # それぞれの仮引数を置き換えて、team.rb に記述したメソッドを呼び出す。
          @team.team_invitation_notification(current_user, @user.id, @team.id)
          # 遷移する前のURLを取得し、リダイレクトさせる。
          redirect_to request.referer, notice: "招待を送りました。"
        else
          redirect_to request.referer, alert: "すでに招待しています。"
        end
     end
```
<br>

チーム加入処理を行う`join`アクションを記述。

```app/controllers/teams_controller.rb
    def join
        @team = Team.find(params[:id])
        # @team.users に、current_user　のレコードが含まれていなければ以下の処理を行う。
        unless @team.users.include?(current_user)
　　　　　　　　　　　　　　　　　　　　# @team.users に、current_user のレコードを追加する。
          @team.users << current_user
        　　　　# 招待通知を検索して削除。
          notification = Notification.find_by(visited_id: current_user.id, team_id: @team.id, action: "invitation")
          notification.destroy
        end
        redirect_to team_path(@team), notice: "チームに参加しました。"
      end
```
<br>

- フォームを記述。

```app/views/teams/show.html.erb
# @other_users は、チームに所属していないユーザーの値を取得している。

<% if @other_users.exists? %>
  <%= form_with model: @user, url: invitation_team_path(@team), method: :post, local: true do |f| %>
    <%= f.collection_select :user_id, @other_users, :id, :nickname %>
    <%= f.submit "招待する" %>
  <% end %>
<% end %>
```
<br>

- 実際に表示される通知を記述。

`action`カラムの値によって表示する内容を変えるようにしたいので、`case`文を使って表示を分岐させます。


```app/views/notifications/_notification.html.erb
    <% case notification.action %>
        <% when "invitaion" %>
            <%= link_to "visitor.name", user_path(visitor) %>
            さんからチームへの招待が届きました。
            #　このリンクをクリックすると、チーム加入処理を行う join アクションが働く。
            <%= link_to "参加する", team_join_path(notification.team_id) %>
```
<br>
以下の記述は、`<% if notification.action == "invitation" %>`と同義です。

```
<% case notification.action %>
        <% when "invitaion" %>
```
通知機能のように、分岐が多い場合には`case`文を用いる方が良いと思います。

<br>

実装の工程は以上です。
お疲れ様でした。

初学者が考えた程度のものですので、足りないところが多々あるかと存じますが、どなたかのお役に立てば幸いです。