Railsで動的に営業時間入力フォームを表示させる方法（ストロングパラメータに注意！）

Railsアプリケーションにおいて、`テーブルA（hospitals）`と`テーブルB（business_days）`間の関係を管理するために`テーブルAB（hospital_business_days）`中間テーブルを設定し、さらにそれに`テーブルC（hospital_business_hours）`を1対多で関連付ける場合、複雑なフォームの扱いが必要になることがあります。本記事では、このような構成を`accepts_nested_attributes_for`を用いて実装し、チェックボックスで選択された曜日に応じて営業時間の入力フォームを動的に表示する方法を解説します。

📌 重要な参考資料 📌

▶️ 以下の記事も参考にしてください。 ◀️
[Railsアプリケーションにおける外部キー制約エラーの解決法](https://qiita.com/mathematics9973/items/39db3300770ccb71600d)




## 実装の概要

実装の核となるのは、次の4つの部分です。
※実際に私が使用した記述を引用しているのでテーブル名等は各自必要なものに読み替えてください。

1. **モデルの関連付け**:
   `hospital`と`business_day`の間に中間テーブル`hospital_business_day`を設定し、`hospital_business_day`から`hospital_business_hour`への1対多の関連付けを行います。

2. **ビューの構築**:
   フォームでは、開院日を選択するチェックボックスと、選択された曜日に応じて表示される営業時間入力フィールドを含めます。

3. **JavaScriptによる動的なフォームの更新**:
   チェックボックスの選択状態に基づいて、非同期通信で営業時間の入力フィールドを動的に更新します。

4. **ストロングパラメータの設定**:
   フォームから送信されるパラメータを適切に扱うため、ストロングパラメータを正確に設定する必要があります。

## ビューの実装

以下は、`hospital`の編集フォームと、動的に更新される営業時間入力フィールドのビューの例です。

```erb:edit.html.erb
<h2><%= resource_name.to_s.humanize %>の編集</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }, remote: true ) do |f| %>
  <%= render "hospitals/shared/error_messages", resource: resource %>
  <%= render 'common_fields', f: f, selected_days: @selected_days %>

  <div class="actions">
    <%= f.submit "更新" %>
  </div>
<% end %>
```

```erb:_commom.html.erb
<div class="hospital-field">
  <%= f.label :開院日, class: 'column-label' %><br />
  <%= f.collection_check_boxes(:business_day_ids, BusinessDay.all, :id, :day_of_week) %>
</div>

<div class="hospital-field">
  <%= f.label :開院時間, class: 'column-label' %><br />
  <table>
    <thead>
      <tr>
        <th>曜日</th>
        <th>開始時間</th>
        <th>終業時間</th>
      </tr>
    </thead>
    <tbody id="business-hours-table">
      <!-- 動的に更新される部分 -->
    </tbody>
  </table>
</div>
```

## JavaScriptによる動的な更新

チェックボックスの状態変化を

検知し、非同期通信で営業時間入力フィールドを更新するJavaScriptの実装例です。

```javascript
$(document).on('turbolinks:load', function() {
  $('input[name="hospital\\[business_day_ids\\]\\[\\]"][type="checkbox"]').change(function() {
    var selectedDays = $('input[name="hospital\\[business_day_ids\\]\\[\\]"][type="checkbox"]:checked').map(function() {
      return $(this).val();
    }).get();

    $.ajax({
      url: '/hospitals/update_business_hours',
      method: 'POST',
      data: { selected_days: selectedDays },
      success: function(data) {
        $('#business-hours-table').html(data);
      }
    });
  });
});
```

## ストロングパラメータの設定

フォームから送信されるネストされたパラメータを安全に扱うために、以下のようにストロングパラメータを設定します。

```ruby
devise_parameter_sanitizer.permit(:sign_up, keys: [
  :name,
  :representative,
  :representative_image,
  :contact_person,
  :postal_code,
  :prefecture,
  :city,
  :street_address,
  :telephone,
  :email,
  :description,
  :password,
  :password_confirmation,
  hos_dep_ids: [],
  business_day_ids: [
    hospital_business_hours_attributes: []    ※ここ重要
  ],
  hospital_business_days_attributes: [
    :id,
    :business_day_id,
    :_destroy,
    hospital_business_hours_attributes: [
      :id,
      :open_time,
      :close_time,
      :_destroy
    ]
  ]
])
```
このストロングパラメータの設定は、`hospital`オブジェクトに対する複雑なネストされた属性の更新を可能にします。特に、`hospital_business_days_attributes`と`hospital_business_hours_attributes`に対するネストされた更新を許可し、`_destroy`キーを含めることで、関連オブジェクトの削除も可能にしています。

## まとめ

Railsにおいて、`hospital`と`business_day`の関係を管理し、動的に営業時間入力フォームを表示させる方法を解説しました。この実装では、モデルの関連付け、ビューの構築、JavaScriptによる動的なフォームの更新、そしてストロングパラメータの適切な設定が重要です。これらのステップに従うことで、ユーザーフレンドリーなフォームを実現し、アプリケーションのユーザーエクスペリエンスを向上させることができます。

---