[Rails]セレクトボックスで動的なグループ招待機能を作る

## やりたい事

Railsでアプリを作っているのですが、グループへの招待機能ってどう実装すれば良いんだ？と思い、考えてみました。

想定している仕様ですが、ユーザーは予めいくつかのグループを作成しており、ユーザーリストを見ながら「コイツいいじゃん！」って思ったユーザーを招待出来るようにします。
「どのユーザーをどのグループに招待するか」という組み合わせを取得する必要があります。

また、UIについてですが、ユーザー一覧に自分で作成したグループが表示されるセレクトボックスを設置し、ワンクリックで招待出来るようにしたいです。
送信ボタン等を押させたくないのでjavascriptでセレクトボックスの要素を取得して、get送信させる工夫が必要ですね。

## 準備

まずは、rails newしてアプリを作成して下さい。

```rb
rails new add_user --skip-bundle
```

### jQuery追加

今回は、jqueryを使用します。
``Gemfile.rb``にjqueryを追加します。

```rb

gem 'jquery-rails'

# bundle installも忘れずに!
bundle install
```

``app/assets/javascripts/application.js``に下記を追加してjqueryを読み込む設定をします。

```rb
//= require jquery
```

ちなみにES6を勉強中なのでES6で書きたかったのですが、ネット上の資料が豊富なのでjqueryに頼ってしまいました・・・

### Modelの作成



```rb
rails g model user name:string introduction:text
rails g model group name:string introduction:text admin_user_id:integer
rails g model add_user_to_group user_id:integer group_id:integer activation:boolean
```

booleanにdefault: falseとnull: falseを追加。

```rb
class CreateAddUserToGroups < ActiveRecord::Migration[5.2]
  def change
    create_table :add_user_to_groups do |t|
      t.integer :user_id
      t.integer :group_id
      t.boolean :activation, default: false, null: false

      t.timestamps
    end
  end
end
```

マイグレーションしましょう。

```rb
rake db:migrate
```

### テストデータ追加

今回の説明では、ユーザー1が「フットサルサークル」、「写真部」、「ボードゲーム愛好会」というグループを作り、他のユーザーを招待するというケースを想定して説明します。
作成したモデルにテストデータを追加しましょう。

```rb
# rail consoleを開く
rails console

# User作成

User.create(name: 'ユーザー1', introduction: 'グループを作りました!!')
User.create(name: 'ユーザー2', introduction: 'フットサルが趣味です')
User.create(name: 'ユーザー3', introduction: '写真が好きです')
User.create(name: 'ユーザー4', introduction: 'ボドゲマニアです')

# Group作成
# admin_user_idはグループの作成者user_id。ユーザー1が下記の3つのグループを作った事にします。

Group.create(name: 'フットサルサークル', introduction: 'みんなでフットサルしましょう', admin_user_id: 1)
Group.create(name: '写真部', introduction: '写真を撮りに行きましょう', admin_user_id: 1)
Group.create(name: 'ボードゲーム愛好会', introduction: 'ボードゲームしましょう', admin_user_id: 1)

```
admin_user_id: 1となっていますが、実際にはグループ作成時にカレントユーザーのidを入れます。
テストなので、ユーザー1が作ったグループという事にします。

### コントローラー作成

ユーザーのリストとグループへの招待機能を作ります。
下記を実行してコントローラーを作って下さい。

```rb
rails g controller Users index add_user_to_group
```

routes.rbも自動で下記のようになるはずなので、確認。

```rb
Rails.application.routes.draw do
  get 'users/index'
  get 'users/add_user_to_group'
end
```

## コーディング作業

ユーザー1が自分のグループに他のユーザーを招待するというケースを前提に説明します。

まず```app/controllers/users_controller.rb```のindexに```@users```、```@group```を追加して下さい。
Groupは実際にはカレントユーザーが作成したグループを取得して実装します。
ユーザーの一覧から特定のユーザーを選び、自分が作成したグループに招待するためです。

```rb
class UsersController < ApplicationController
  def index
    @users = User.all
    # テストなのでadmin_user_idに1を設定していますが、実際はcurrent_userのidを設定しましょう。
    @groups = Group.where(admin_user_id: 1)
  end

  def add_user_to_group
  end
end

```

``app/views/users/index.html.erb``を下記のようにする。
グループ招待のセレクトボックスは自分以外のユーザーに表示します。自分で自分を招待する仕様はおかしいので。
ここでもuser.idが1じゃない時としてますが、実際はカレントユーザーのidを入れて、ログインしているユーザーと照合しましょう。

```erb

<h1>Users#index</h1>
<p>Find me in app/views/users/index.html.erb</p>

<% @users.each do |user| %>
 <p><%= user.name %>:<%= user.introduction %></p>
  <!-- テストなので、user.idが1じゃない時にしてますが、実際はcurrent_userのidではない時に表示にしましょう -->
  <% if user.id != 1 %>
  <select class='group' id=<%= "#{user.id}" %>>
    <option value="0">グループへ招待</option>
    <% @groups.each do |group| %>
      <option value=<%= "#{group.id}" %>><%= "#{group.name}" %></option>
    <% end %>>
  </select>
  <% end %>
  <hr>
<% end %>

```


この時点で```rails s```して```localhost:3000/users/index```にアクセスすると下記の画面になるはずです。
**ユーザー1がログインして、他のユーザーをグループへ招待するというテストケースです。**
ユーザー1以外のユーザーには、ユーザー1が作ったグループのセレクトボックスが追加されています。

![スクリーンショット 2019-05-14 6.37.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343136/004d12ae-fde8-839c-2aff-c2fc4002ce1c.png)

## グループへの招待機能

セレクトボックスで選択したユーザーのidとグループのidを動的にget送信します。送信した後にコントローラーでユーザーとグループの組み合わせを招待情報として保存します。

### js記述とget送信

先程のviewにjavascriptを追加します。viewにjavascriptをベタ書きするのは、**本当は良くない**のですが、筆者の体力もそろそろ限界のため、一旦ここに書いて動作確認とQiitaへのアウトプットをさせて下さい！

```erb
<h1>Users#index</h1>
<p>Find me in app/views/users/index.html.erb</p>
# ここから
<script>
  $(function() {
    // groupクラスの変更を検知する
    $('.group').change(function() {
      $.ajax({
　　　　　// getしたいurl
        url: "/users/add_user_to_group",
        // 送信形式にget設定
        type: "GET",
　　　　　// パラメーターにuser_idとgroup_idを追加して、コントローラーへ渡す
        data: { user_id: $(this).attr('id'), group_id: $(this).has('option:selected').val() }
      });
　　　　　// グループへ招待したら、画面上からグループを消す。
        $(this).children('option:selected').remove();
    });
  });
</script>
# ここまで追加

<% @users.each do |user| %>
 <p><%= user.name %>:<%= user.introduction %></p>
  <!-- テストなので、user.idが1じゃない時にしてますが、実際はcurrent_userではない時に表示にしましょう -->
  <% if user.id != 1 %>
  <select class='group' id=<%= "#{user.id}" %>>
    <option value="0">グループへ招待</option>
    <% @groups.each do |group| %>
      <option value=<%= "#{group.id}" %>><%= "#{group.name}" %></option>
    <% end %>>
  </select>
  <% end %>
  <hr>
<% end %>

```

### ユーザーとグループの組み合わせをDBに保存

パラメーターで、``user_id``と``group_id``が渡ってきているので、``add_user_to_group``にて招待データを登録します。
``AddUserToGroup``に``user_id``と``group_id``を登録する事で、どのユーザーが、どのグループに招待されているかわかるはず！
あとは、招待されたユーザー側の機能で、``activation``カラムをtrueにしたら正式にグループ参加する仕様で良いかな。

```rb
class UsersController < ApplicationController
  def index
    @users = User.all
    # テストなのでadmin_user_idに1を設定していますが、実際はcurrent_userのidを設定しましょう。
    @groups = Group.where(admin_user_id: 1)
  end

  # 以下を追加
  def add_user_to_group
    AddUserToGroup.create(user_id: params[:user_id], group_id: params[:group_id])
  end
end

```

見辛いですけど、```user_id```と```group_id```の組み合わせがちゃんと入ってますね！
![スクリーンショット 2019-05-14 7.45.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343136/0a94e35d-139f-e287-4069-b8f5f55fc9a1.png)

とりあえずここまで。

## 改善点

今回、jQuery使っちゃいましたが、本当はES6勉強してるので、ES6で書きたいです。
[JavaScript(ES6)の基本文法まとめ](https://qiita.com/fujigaki/items/409dd0aa3fd1e3f6b269)
[JavaScript(ES6)の関数、オブジェクト、クラスのまとめ](https://qiita.com/fujigaki/items/26f989b638cf92c234a9)

あと、jQueryに関してなんですが、過去資料がかなり豊富なのに最近の記事は少ない感じがしました。
ブームが去ったという事でしょうか。
実務でもjQueryよりReact、Vue.jsを使いそうなので、フレームワークで実装出来るようにしたいですね。