accepts_nested_attributes_forとf.collection_check_boxesを使わずに、一つのフォームでモデルと中間テーブルを同時に保存する　

多対多のリレーションを考えたときに、親モデルを作成すると同時に子モデルも作成したいときがあります。ブログにカテゴリーをつけたいときとか。
**accepts_nested_attributes_for**を使用したやり方がよく紹介されていますが、評判が良くないといった書き込みがあったり、個人的に挙動がわかりにくかったのでaccepts_nested_attributes_forを使用しない方法を記載しました。
ターゲットはあくまでも初心者ですので、ていねいにコメントを多めにしながら書きました。


#サンプルとしてuser（ユーザー）とjob（仕事）の多対多の関係を考えます
userモデルとjobモデルが中間テーブルuser_jobで多対多のリレーションを持つとします。



##とりあえずuserを作成します。
userをつくりましょう。まずモデルから

```
$rails g model user name:string
$rails db:migrate
 ```

今回は名前だけ持たせます。
コントローラをつくります。userをつくるだけのシンプルなものです。

```
$rails g controller users
```
```ruby:users_controller
def new
    @user = User.new
    @users = User.all
  end

  def create
    User.create(user_params)
    redirect_to new_user_path
  end

  private
  def user_params
    params.require(:user).permit(:name)
  end

```

```html:users/new.html.erb
<h1>ユーザー登録</h1>
<%= form_for @user do |f| %>
<%= f.label :name %>
<%= f.text_field :name%>
<%= f.submit "登録" %>
<% end %>

<% @users.each do |user| %>
<%= "#{user.id}:#{user.name}" %><br>
<% end %>
```

```ruby:config/routes
resources :users
```
<img width="363" alt="スクリーンショット 2019-10-18 11.34.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378348/36078c6f-e2c6-db39-45c4-0200a21f6860.png">
こんな感じになりましたでしょうか。
この段階で`http://www.localhost:3000/users/new`にアクセスすればuserの名前を登録するかんたんなwebサイトになります。一郎、次郎、三郎を作成しました。

#user登録と同時に中間テーブル（user_jobs)を作成することを考えてみる
##とりあえずjobモデルをつくりましょう
名前だけを持たせます。
```
$rails g model Job name:string 
$rails db: migrate
```
migrateを忘れずに。
jobをいくつかつくりたいのですが、formを作るのが面倒なのでコンソールでjobを作成します。

```console
> Job.create name: "farmer"
> Job.create name: "fisherman"
```
今回はfarmerとfishermanを作成しました。
##中間テーブルuser_jobsを作成する

```
$rails g model UserJob user:references job:references
$rails db:migrate
```
`user:references job:references`とすることでuser_idとjob_idを自動生成してくれます。

##has_manyとbelongs_toを加える
```ruby:user.rb
#これを書き加える
has_many :user_jobs
has_many :jobs, through: :user_jobs
```
has_manyを書き加えることで`user.jobs.build`や`user.user_jobs`といったメソッドが使えるようになる。`belongs_to`は中間テーブル作成時に初めからあるはずです。




##ここでユーザー登録と同時にuser_jobsも登録することを考えます。

```html:users/new.html.erb
<h1>ユーザー登録</h1>
<%= form_for @user do |f| %>
  <!--ここはいつも通りの@userのname部分のform-->
  <%= f.label :name %>
  <%= f.text_field :name%>
  <!--ここが中間テーブルuser_jobsを保存させるためのform
  パラメータを扱いやすくするためfields_forでネストさせます-->
  <%= f.fields_for :job do |j| %>
    <!--jobは複数あるのでeachで全て取り出してチェックボックスにします-->
    <% Job.all.each do |job| %>
      <%= j.label job.name %>
      <%= j.check_box job.name,{}, true, false %>
    <% end %>
  <% end %>
  <%= f.submit "登録" %>
<% end %>
<!--この部分は保存したuserとjobを確認しているだけなので分かれば何でもいいです。-->
<% @users.each do |user| %>
  <% if user.jobs.first.nil? %>
    <%= "#{user.id}:#{user.name}" %><br>
  <% else %>
    <%= "#{user.id}:#{user.name}:" %>
    <% user.jobs.each do |job| %>
      <%= "#{job.name}" %>
    <% end %>
    <br>
  <% end %>
<% end %>
```
**f.fields_for**をつかって中間テーブル作成用のformをuser作成formにネストしています。
ここでは、あらかじめ登録したjobをチェックボックスで選択してuserに登録するようにします。
formでネストすることによって
`params[:user][:job]= {"farmer"=>"true", "fisherman"=>"true"}`
のようにチェックボックスでの値が取り出せます。

##あとはコントローラで中間テーブルを保存する

```ruby:users_conntroller.rb
def create
  created_user = User.create(user_params)
  #checkboxで受け取ったパラメータはhashになっているので一つづつ取り出して保存する
  unless params[:user][:job].nil?  #nilガード
    params[:user][:job].each do |key, value|  
      if value == false　　#チェックされていない場合はスキップする
        next
      else
        job_id = Job.find_by(name: key).id  #keyで探して保存するだけです
        user_job = UserJob.create(user_id: created_user.id, job_id: job_id)
      end
    end
  end

  redirect_to new_user_path
end

  private
  def user_params
    params.require(:user).permit(:name)
  end
```

<img width="460" alt="スクリーンショット 2019-10-21 0.27.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378348/fd3113f3-0caf-70cc-7ddb-94a1e375c560.png">

##終わり
チェックボックスに入れてuser登録するとこんな感じになりましたでしょうか？
**accepts_nested_attributes_for**を使ってみたり、**f.collection_check_boxes**をつかってみたりしましたが、いまいち挙動がつかめず気持ち悪かったので、きちんと自分で書いてみました。
リファクタリングできたり、fatコントローラである点は置いておいてください。
参考になればうれしいです。