検索条件フォームのようにテーブルと完全に同一でないフォームもform_forを使って実装できる

[【ruby】ActiveModelを使ってDBと関係ないFormを作成する【Rails】](http://tanihiro.hatenablog.com/entry/2014/01/09/193720)
> RailsでFormを扱う時は、Modelと紐付けることで、validationなどの設定はModelに書くことができ、非常に便利です。
> 
> ですが、DBと関係ない場合は紐付ける対象のModelが生成されていません。
> 
> そこで使用するのがActiveModelです。

上のページに書いてあることが全てなのですが、あまりにもショックが大きかったので書き残しておきます。

## form_forは入力フォームに初期値を設定してくれる
Webアプリケーションで入力フォームを作成する場合、入力された情報を保持しておいて、次の画面の入力フォームに初期値として表示したいことがあります。検索画面で検索結果と一緒にフォームも表示する場合や、登録画面で入力エラーがあって同じ画面に戻ってきた時などです。

テキストボックスならvalue属性に入力された値をセットしてあげるだけでよいのですが、ラジオボタンや複数選択のチェックボックスなんかだと、選択肢をループで回して入力された値がvalue属性と一致したら`checked="checked"`を差し込んで…みたいなことをやる必要がありました。

Railsでは`form_for`ヘルパーがその辺の面倒な処理を面倒見てくれるのですが、呼び出すためにはモデルオブジェクトを渡す必要があります。
データを登録したり編集したりする画面なら、対応するモデルオブジェクトを渡せばいいのですが、検索フォームの入力項目というのはデータベースのテーブルとは完全に一致しない場合が多いと思います。
日付の範囲指定のために開始と終了で２つの項目が必要だったり、関連する別テーブルの値を条件にしないといけなかったりで、モデルクラスと似ているけど少しだけ違う形になります。

## 検索条件フォームはform_tagで書くしかないと思っていた
検索条件を入力するフォームには、対応するモデルクラスがないので`form_for`の恩恵には預かれません。モデルがなくても呼べる`form_tag`というヘルパーメソッドを使うしかないと思って作成したフォームがこちらです。

### 画面イメージ
![Practice 2015-02-02 23-16-47.png](https://qiita-image-store.s3.amazonaws.com/0/67427/86936ace-0d05-74d7-d5e5-6cf98c4049b8.png)

### コントロール
```rb:app/controllers/employees_controller.rb
class EmployeesController < ApplicationController
  def index
    @name = params[:name]
    @sex_id = params[:sex_id]
    @birth_from = params[:birth_from]
    @birth_to = params[:birth_to]
    @department_id = params[:department_id]
    @favorite_ids = params[:favorite_ids]

    @employees = # 検索処理は未実装...
  end

  def find
  end
end
```

### ビュー
```erb:app/view/employees/
_employees_find_form.html.erb
<%= form_tag({action: :index}, {method: :get}) do %>
  <%# テキストボックス %>
  <p>
    <%= label_tag :name, '氏名:' %>
    <%= text_field_tag :name, @name %>
  </p>

  <%# ラジオボタン選択 %>
  <p>
    <%= label_tag :sex_id, '性別:' %>
    <%= radio_button_tag :sex_id, '', @sex_id == '' %>
    <%= label_tag :sex_id_, '指定なし' %>
    <% Sex.all.each do |option| %>
      <%= radio_button_tag :sex_id, option.id, option.id.to_s == @sex_id %>
      <%= label_tag "sex_id_#{option.id}", option.name %>
    <% end %>
  </p>

  <%# テキストボックス（日付の範囲指定） %>
  <p>
    <%= label_tag :birth_from, '生年月日:' %>
    <%= text_field_tag :birth_from, @birth_from %> 〜
    <%= text_field_tag :birth_to, @birth_to %>
  </p>

  <%# プルダウン選択 %>
  <p>
    <%= label_tag :department_id, '部署:' %>
    <%= select_tag :department_id,
      options_from_collection_for_select(Department.all, "id", "name",
                                         selected: @department_id),
      include_blank: true %>
  </p>

  <%# チェックボックス複数選択 %>
  <p>
    <%= label_tag :favorite, '好物:' %>
    <% Favorite.all.each do |option| %>
      <%= check_box_tag "favorite_ids[]", option.id,
        @favorite_ids.present? &&
        @favorite_ids.include?(option.id.to_s),
        id:"favorite_id_#{option.id}" %>
      <%= label_tag "favorite_id_#{option.id}", option.name %>
    <% end %>
  </p>

  <p>
    <%= submit_tag '検索する' %>
  </p>
<% end %>
```
すごくグチャグチャしています。メンテナンスするのも大変そうです。

## ActiveModelを使えば対応するテーブルがなくてもform_forが使える
詳細は上のリンクにかいてありますが、`ActiveModel`を`include`したモデルクラスを検索フォームとして作ることで、`form_for`に渡せるようになります。
というわけで書き直したものがこちらです。

### モデル
```rb:app/models/employee_find_form.rb
class EmployeeFindForm
  include ActiveModel::Model

  attr_accessor :name, :sex_id, :birth_from, :birth_to,
    :department_id, :favorite_ids
end
```

### コントローラ
```rb:app/controllers/employees_controller.rb
class EmployeesController < ApplicationController
  def index
    @form = EmployeeFindForm.new(params.require(:employee_find_form))
    @employees = # 検索処理は未実装...
  end

  def find
    @form = EmployeeFindForm.new
  end
end
```

### ビュー
```erb:app/view/employees/_employees_find_form.html.erb
<%= form_for @form, url: '/employees', method: :get do |f| %>
  <%# テキストボックス %>
  <p>
    <%= f.label :name, '氏名:' %>
    <%= f.text_field :name %>
  </p>

  <%# ラジオボタン選択 %>
  <p>
    <%= f.label :sex_id, '性別:' %>
    <%= f.radio_button :sex_id, '', checked: @sex_id == '' %>
    <%= f.label :sex_id_, '指定なし' %>
    <%= f.collection_radio_buttons :sex_id, Sex.all, :id, :name %>
  </p>

  <%# テキストボックス（日付の範囲指定） %>
  <p>
    <%= f.label :birth_from, '生年月日:' %>
    <%= f.text_field :birth_from %> 〜
    <%= f.text_field :birth_to %>
  </p>

  <%# プルダウン選択 %>
  <p>
    <%= f.label :department_id, '部署:' %>
    <%= f.collection_select :department_id, Department.all, :id, :name, include_blank: true %>
  </p>

  <%# チェックボックス複数選択 %>
  <p>
    <%= f.label :favorite, '好物:' %>
    <%= f.collection_check_boxes :favorite_ids, Favorite.all, :id, :name, checked: @form.favorite_ids %>
  </p>

  <p>
    <%= f.submit '検索する'%>
  </p>
<% end %>
```

すごくスッキリしました！
「`xxx_tag`なんて二度と使うもんか！」という気持ちです。