rails 都道府県をプルダウンで選択できるようにする #初心者

# 1. 背景

タイトルの通り都道府県をプルダウンで選択できるようにする方法をまとめました。
始めは、htmlをべたっと書いていたのですが、Active_Hashと言うものの存在を知り、そちらに移行しようとしました。

# 2. Active_Hashとは

初心者には以下の説明がわかりやすかったので引用。
> Active_Hashとは、都道府県名やカテゴリー選択などの変更されないデータをモデルファイル内に直接記述することで、データベースへ保存せずにデータを取り扱うことができるgemです。
> Active_Hashを用いることで、モデルファイルに直接記述した変更されないデータに対して、ActiveRecordのメソッドを用いることができます。

https://qiita.com/iwkmsy9618/items/60ea01f4f4285d14c597

# 3. gem の追加

Gemfileに追記したのち、``bundle install``する。

``` app/Gemfile
gem "active_hash"
```

# 4. modelの追加

私は手動で、modelsフォルダ直下にprefecture.rbを作成しました。
ActiveHash::Baseを継承したPrefectureクラスを作成します。
id:1がコメントアウトしてあるのは、作成したけれど不要になったからです。

``` app/models/prefecture.rb
class Prefecture < ActiveHash::Base
  self.data = [
    # {id:1, name:'選択してください'},
    {id:2, name:'オンラインショップのみ'},
    {id:3, name:'北海道'},
    {id:4, name:'青森県'},
    （略）
      ]

  include ActiveHash::Associations
  has_many :shops
end
```
associationsについては、正直まだ完全に理解していないのですが、モデル同士の関連付けを行うものだそうです（詳しくは、Railsガイドへ）。これによりモデルの操作を一貫させることができるらしい。
私が作成しているアプリケーションは、お店の情報を記録するデータベース（shopモデル）を持っているので、``has_many :shops``となっています。``has_many``は、1対多のつながりを表しています。

https://railsguides.jp/association_basics.html

# 4. 既存のmodelにassociationsを追加

``has_many``に対して、``belongs_to``関連付けを行います。今回の場合、1つのお店に対して、1つの都道府県を割り当てたいという形です。

``` app/models/shop.rb
class Shop < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to :prefecture
end
```

# 5. 入力欄の作成

入力欄にプルダウンを追加します。モデルの情報をもとにプルダウンを作成する``f.collection_select``メソッドを使用しました。

``` f.collection_select
f.collection_select(カラム名, プルダウンの表示に使うデータの配列, {オプション}, {HTMLオプション}
```

<table>
	<tbody>
		<tr>
			<td>カラム名</td>
			<td>コントローラで値を受け取る時の名前。params[:カラム名]</td>
		</tr>
		<tr>
			<td>プルダウンの表示に使うデータの配列</td>
			<td>モデルに設定した都道府県データ</td>
		</tr>
		<tr>
			<td>オプション</td>
			<td>デフォルト値などを設定することができる。オプションを設定しない場合でも{}は、入力が必要</td>
		</tr>
		<tr>
			<td>HTMLオプション</td>
			<td>idやclassを設定したい場合に入力</td>
		</tr>
	</tbody>
</table>

今回は以下のような形で設定しました。

``` app/views/shops/new.html.erb に追加した内容
<%= f.collection_select(:prefecture_id, Prefecture.all, :id, :name, {prompt: "選択してください"}, {class:"new-wrapper__main__input-select", id:"item-prefecture"}) %>
```

参考にしたページ

https://qiita.com/ohnitakahiro/items/c536fe65e37980e1087e

https://310nae.com/rails-selectbox/

https://railsdoc.com/page/collection_select

# 6. form_withヘルパーの追加

私はform_tagしか使ったことがない状況だったため、"f"が未定義というエラーに遭遇して、"f"とは？とさまよいました。以下のようなコードの"f"でした（初学者過ぎる）。

```
<%= form_with model: @user do |f| %>
    <!--フォーム内容 -->
<% end %>
```
参考にしたページ
https://qiita.com/YokoYokoko/items/77d0043e12406c7cec9e

form_withヘルパーは、URL、スコープ、モデルなどからフォームタグを生成してくれます。
Rails ドキュメントを見てなんとなく意味が分かりました。

https://railsdoc.com/page/form_with

今後どうするかは別として、とりあえずform_tag的な使い方ができればよかったので、以下のようにしました。``f.submit``を設定することで、``<input type="submit"~>``のタグが生成され、ボタンを押すと設定したurlに遷移します。

``` app/views/shops/new.html.erb に追加した内容
<%= form_with url: {controller: 'shops', action: 'create'}, method: :post do |f| %>
      <%= f.collection_select(:prefecture_id, Prefecture.all, :id, :name, {prompt: "選択してください"}, {class:"new-wrapper__main__input-select", id:"item-prefecture"}) %>
      <%= f.submit "作成" %>
<% end %>
```

参考にしたページ（既に1度参照していますが）

https://qiita.com/YokoYokoko/items/77d0043e12406c7cec9e

ここまでくればプルダウンは生成されたはずです。

### 2024/10/11 追記

とりあえずform_tag的な使い方をしましたが、``validation``の設定をして発生したエラーを出力しようとした辺りでエラーが発生しました。
解決の糸口になったのは、以下の記事です。非同期通信をするAjaxという仕組みが``form_with``では、自動的に設定される（？）ようです。

https://qiita.com/k_senbei/items/a361171f653edcd888ad

Ajaxの説明

https://qiita.com/hisamura333/items/e3ea6ae549eb09b7efb9

私は、上記を参考に対応しても状況が変わらず、以下の記事に記載のとおりrenderに``(status: :unprocessable_entity)``を追加することで解決しました。

``` app/controllers/shops_controller.rb に記載した内容
    if @shop.save
      flash[:notice] = "お店を投稿しました"
      redirect_to shops_path
    else
      flash.now[:notice] = "登録できませんでした"
      render new_shop_path, status: :unprocessable_entity
    end
```

https://zenn.dev/tya0116/articles/03bcf655e31508


# 7. 保存したデータから、都道府県を参照して出力する

他のモデルと同じように``find_by``メソッドを使用して出力できます（それはそう）。
私はshopモデルのprefecture_idに、入力されたprefecture_idを保存しているので以下の形です。

```
prefecture = Prefecture.find_by(id: shop.prefecture_id)
prefecture.name
```

参考にしたページ
https://pikawaka.com/rails/active_hash

# おわりに

自分があまりにも初学者過ぎて（ここには書いていない無関係なエラーにも悩まされ）愕然としておりますが、先人のお知恵をお借りして何とか実現できました。ありがとうございました。