都道府県 + 市町村をチェックボックスで複数選択。登録する方法。

##やりたいこと
チーム開発で、一人のマッサージ師に対して複数の出張範囲を登録出来る機能を実装した。モデルの関係性など勉強になったのでメモ。完全自分用です。

###モデルの関係性
マッサージ師

```
class Masseur < ApplicationRecord
  has_many :business_trip_ranges, dependent: :delete_all
  has_many :cities, through: :business_trip_ranges
  accepts_nested_attributes_for :business_trip_ranges, reject_if: :reject_business_trip_range, allow_destroy: true
```
出張範囲

```
class BusinessTripRange < ApplicationRecord
  belongs_to :masseur
  belongs_to :city
end

```
市町村

```
class City < ApplicationRecord
  belongs_to :prefecture
  has_many :masseurs, through: :business_trip_ranges
  has_many :business_trip_ranges
end
```

ややこしいですが図にするとこんな感じ。

<img width="1338" alt="スクリーンショット 2020-07-25 20.28.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/625856/132fd0d4-99a9-c764-6999-8854bcddcb9c.png">

##作業の流れ

1、APIで取ってきた都道府県 + 市町村データをPrefectureモデルとCityモデルに保存する
2、保存したデータを使って都道府県チェックボックスを作成していきます
3、チェックボックスにチェックした出張範囲はBusinessTripRangeに保存される

ざっくりしすぎか。
##APIでデータを取ってくる
このサイトから都道府県 + 市町村データがAPIで取ってこれます。APIが使えるならこっちの方が手っ取り早いです。なおAPIキーが必要になるのでサイト内で申請してください。
https://opendata.resas-portal.go.jp/

まずは以下のようにしてAPIデータを取得し

```
module StoreManager::BusinessTripRangesHelper
  require 'net/http'
  require 'uri'
  require 'json'
  
  # 都道府県のAPIを叩き、データを出力
  def prefectures_api(url)
    uri = URI.parse(URI.escape(url))

    https = Net::HTTP.new(uri.host, uri.port)
    https.use_ssl = true

    param = {}
    param['X-API-KEY'] = '123hsydeusnsindidkm' # 自分が取得したAPI-KEY

    req = Net::HTTP::Get.new(uri.request_uri, param)
    res = https.request(req)

    json = res.body
    result = JSON.parse(json)
  end
  
end
```

seeds.rbファイルで取得したデータをPrefectureモデルとCityモデルのカラムに入れて都道府県 + 市町村データを格納します

```
include StoreManager::BusinessTripRangesHelper

# 都道府県データを取得
prefectures = prefectures_api("https://opendata.resas-portal.go.jp/api/v1/prefectures")
# 東京都の市/区データを取得
cities = prefectures_api("https://opendata.resas-portal.go.jp/api/v1/cities?prefCode=13")

prefectures["result"].each do |value|
  prefecture_name = value["prefName"]
  Prefecture.find_or_create_by(name: prefecture_name)
end

cities["result"].each do |value|
  city_name = value["cityName"]
  City.find_or_create_by(name: city_name, prefecture_id: 13)
end
```

```
$ rails db:seed
```
これでデータがPrefectureとCityに入りました。
<img width="1174" alt="スクリーンショット 2020-07-25 20.54.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/625856/dffc1a23-c090-332c-1210-5a02b53df8e2.png">
都道府県 + 市町村だからとんでもなくデータ多いです。写真はPrefectureモデルに入っているデータですがCityモデルにも市町村データはちゃんと入ってるぞい。


##保存したデータを元にチェックボックス作る

今回使ったのはcolection_check_boxesというメソッド。なんか最初はややこしかったけど分かると使いやすいかも。

```
<%= form_with(model: @current_masseur, url: store_manager_business_trip_ranges_update_path(@current_masseur), method: :patch, local: true) do |f| %>
        <table class="prefecture table table-hover">
          <thead>
            # Prefectureモデルに格納した47都道府県データを全てブロック変数prefectureに代入。 
            <%= collection_check_boxes :prefecture, :prefecture_ids, Prefecture.all, :id, :name, include_hidden: false do |prefecture| %>
            <tr>
              <th>
　　　　　　　　　 ＃ブロック変数.textで都道府県名、ブロック変数.checkboxでチェックボックスを作成
                <%= prefecture.check_box + prefecture.text %>
              </th>
            </tr>
          </thead>
          
          <tbody>
            <tr>
              <td class="city-check-boxes">
                <div id="city-list">
                  #市町村も同様です
                  <%= f.collection_check_boxes :city_ids, City.all, :id, :name, include_hidden: false do |city| %>
                    <ul class="city-check-box">
                       <% if prefecture.object.id == city.object.prefecture_id %>
                         <div id="boxes">
                           <li class="city-check-box-item"><%= city.check_box + city.text %></li>
                         </div>
                       <% end %>
                    </ul>
                  <% end %>
　　　　　　　　　 </div>
              </td>
            </tr>
            <% end %>
          </tbody>
        </table>
        
        <div class="actions text-center">
          <%= f.submit "登録する", class: "btn btn-primary mt-5 w-50" %>
        </div>
      <% end %>
```

こうなります。

<img width="1174" alt="スクリーンショット 2020-07-25 21.20.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/625856/681e2daf-4536-d6ab-9d93-b3c5b858d241.png">


ちなみにコントローラーはこうなってます。

```
class StoreManager::BusinessTripRangesController < StoreManager::Base
  
  before_action :set_masseur, only: [:edit, :update, :show]
  
  def edit
    @prefectures = Prefecture.all
    @ranges = @current_masseur.business_trip_ranges
  end
  
  def update
    # チェックがあった場合
    if params[:masseur].present?
      @current_masseur.update(city_business_trip_range_params)
      flash[:success] = "出張範囲を更新しました。"
      redirect_to store_manager_masseurs_business_trip_ranges_url
    # 一つもチェックがなかった場合
    else
      flash[:danger] = "出張範囲を選択してください。"
      render :edit
    end
  end
  
  private
  
  def set_masseur
    @current_masseur = Masseur.find(params[:masseur_id])
  end
  
  def city_business_trip_range_params
    if params[:masseur].present?
      params.require(:masseur).permit(city_ids: [])
    end
  end
 
end
```

重要なのはストロングパラメーターで配列を受け取る時に

```
                          　　　このように書く↓
params.require(:masseur).permit(city_ids: [])
```

もう一つはupdateアクションの@current_masseur.update(city_business_trip_range_params)
このように書くことで「作成」「編集」「削除」が同時にできてしまうこと。
故に「new」「create」「destroy」は必要ありません。

途中で眠くなったわ。。