LoginSignup
0
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-07-25

やりたいこと

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

モデルの関係性

マッサージ師

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

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

スクリーンショット 2020-07-25 20.28.36.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に入りました。
スクリーンショット 2020-07-25 20.54.20.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 %>

こうなります。

スクリーンショット 2020-07-25 21.20.08.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」は必要ありません。

途中で眠くなったわ。。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0