多対多の関係性にフォームオブジェクトなどが絡んで訳わからなくなった話

### アプリの仕様
- キャンプの記録をするアプリです
- ユーザーはキャンプのタイトルとキャンプスタイルという名のタグをつけて投稿します。
- キャンプタイトルとタグを同時に保存するためにフォームオブジェクトを使用しています。
- ユーザーは自分の持っているアイテムを登録できます。
- キャンプには複数のアイテムを持っていける。アイテムは何度でもキャンプに持っていけることからキャンプとアイテムは多対多の関係です。
- キャンプ記録を作成する際持って行ったアイテムの登録を実現させたい

### 発生したエラー

```
undefined method `id' for #<CampTags:0x00007ff6085ac980>
Request
Parameters:

{"authenticity_token"=>"6h75fWC/+zhPx96ZoRyir+kyevpfqLqawP75tlOKrGHOXHlfKtMlG0qvpQMLgrjc5Ld3x1bvduvrtllnJPZagw==",
 "camp_tags"=>{"title"=>"bboobb", "style"=>"無骨キャンプ", "item_ids"=>["1", "2"]},
 "commit"=>"Send"}
```

### 状況
- @item_idsをeachにかけることでitemのidを一つずつ取り出すのは成功しています。
- campのデータについてはtagと同時に作成されるためFormオブジェクトで保存
- itemに関しては保存してあったものをselectによって選択する
- campとtagの情報については登録保存は成功している
- @camp_id = 3と直球にidを指定してやるとitemとcampの関連付けはできた
- @campを取り出せれば解決する
- @campを指定すると:camp_tagsからidを取り出そうとするため@camp.idが無いとエラーになる

@campを確認するとidは保存されているようです。

```
>> @camp
=> #<CampTags:0x00007ff6085ac980 @errors=#<ActiveModel::Errors:0x00007ff6085ac250 @base=#<CampTags:0x00007ff6085ac980 ...>, @messages={}, @details={}>, @title="bboobb", @style="無骨キャンプ", @item_ids=["1", "2"], @user_id=1, @validation_context=nil, @camp=#<Camp id: 36, user_id: 1, title: "bboobb", created_at: "2021-03-17 06:28:53", updated_at: "2021-03-17 06:28:53">, @tag_id=#<Tag id: 4, style: "無骨キャンプ", created_at: "2021-03-16 09:04:46", updated_at: "2021-03-16 09:04:46">>
>>  
```

### 該当するコード

コントローラー

```camps_controller.rb
class CampsController < ApplicationController
  before_action :authenticate_user!, except: [:index, :show]
  before_action :set_item, only: [:index, :new, :show, :create]
  def index
    @tags = Tag.all
  end

  def new
    @camp = CampTags.new
  end

  def create
    @camp = CampTags.new(camp_params)

    if @camp.valid?
      @tag_list = camp_params[:style].split(/[[:blank:]]+/).select(&:present?)
      @camp.save(@tag_list)
      @camp_id = @camp.id
      @item_ids = @camp.item_ids
      @item_ids.each do |item_id|
        CampItemRelation.create(camp_id: @camp_id, item_id: item_id)
      end
      return redirect_to root_path
    else
      render "new"
    end
  end

  private

  def camp_params
    params.require(:camp_tags).permit(:title, :style, item_ids: []).merge(user_id: current_user.id)
  end

  def set_item
    if user_signed_in?
      user = User.find(current_user.id)
      @items = user.items
    end
  end

end
```

formオブジェクト

```camp.tags.rb
class CampTags

  include ActiveModel::Model
  attr_accessor :title, :style, :user_id, :item_ids

  with_options presence: true do
    validates :title
    validates :style
  end

  def save(tag_list)
    @camp = Camp.create(user_id: user_id, title: title)
    tag_list.each do |tag|
      unless Tag.find_by(style: tag)
        @tag = Tag.create(style: tag)
        CampTagRelation.create(camp_id: @camp.id, tag_id: @tag.id)
      else
        @tag_id = Tag.find_by(style: tag)
        CampTagRelation.create(camp_id: @camp.id, tag_id: @tag_id.id)
      end
    end
  end

end
```

入力フォーム

```new.html.erb
<div class="wrapper">
  <div class="side-ber">
    <%= render "side_ber" %>
  </div>
  <%= form_with model: @camp, url: camps_path, class:'form-wrap', local: true do |f| %>
    <div class='form'>
      <div class="title-field">
        <%= f.label :title,  "キャンプタイトル" %>
        <%= f.text_area :title, class:"input-title" %>
      </div>
      <div class="tag-field", id='tag-field'>
        <%= f.label :style, "キャンプスタイル" %>
        <%= f.text_field :style, class:"input-tag" %>
      </div>

      <p>使用アイテムを選択 </p>
      <select name="camp_tags[item_ids][]" multiple>
        <% @items.each do |item| %>
          <option value=<%= item.id %>><%= item.name %></option>
        <% end %>
      </select>


    </div>
    <div class="submit-post">
      <%= f.submit "Send", class: "submit-btn" %>
    </div>
  <% end %>
</div>
```

### ルーティング

```routes.rb
Rails.application.routes.draw do
  root to: "camps#index"
  resources :camps, only: [:new, :create, :show]
  resources :items, only: [:new, :create, :show]
  devise_for :users
end
```

### テーブル同士のアソシエーション

```item.rb
class Item < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to :genre
  belongs_to :user
  has_many :camp_item_relations
  has_many :camps, through: :camp_item_relations
  

  with_options presence: true do
    validates :genre_id, numericality: { other_than: 1, message: 'Select'}
    validates :name
    validates :feature
    validates :price, numericality: { with: /\A[0-9]+\z/, message: 'Half-width number' }
  end
  
end
```

```camp.rb
class Camp < ApplicationRecord
  belongs_to :user
  has_many :camp_tag_relations
  has_many :tags, through: :camp_tag_relations
  has_many :camp_item_relations
  has_many :items, through: :camp_item_relations
  
  validates :title, presence: true

end
```

itemとcampの中間テーブル

```camp.item.relations.rb
class CampItemRelation < ApplicationRecord
  belongs_to :camp
  belongs_to :item
end
```

### 試したこと

コントローラーではitem_idsを扱える
フォームオブジェクトではcamp.idを扱えることから
CampItemRelation.createでのcampとitemのidの保存を別々で行ってみた。
最初にフォームオブジェクトで```CampItemRelation.create(camp_id: @camp.id)```として
次にコントローラーで```CampItemRelation.create(item_id: item_id)```
こうすることでデータを扱えるうちに保存できると考えましたが、データはどうやら同時に保存しないといけないらしくこれらの記述は読み飛ばされたようでした。

```camps.controller.rb
class CampsController < ApplicationController
  before_action :authenticate_user!, except: [:index, :show]
  before_action :set_item, only: [:index, :new, :show, :create]
  def index
    @tags = Tag.all
  end

  def new
    @camp = CampTags.new
  end

  def create
    @camp = CampTags.new(camp_params)

    if @camp.valid?
      @tag_list = camp_params[:style].split(/[[:blank:]]+/).select(&:present?)
      @camp.save(@tag_list)

      @item_ids = @camp.item_ids
      @item_ids.each do |item_id|
        CampItemRelation.create(item_id: item_id)
      end
      return redirect_to root_path
    else
      render "new"
    end
  end

  private

  def camp_params
    params.require(:camp_tags).permit(:title, :style, item_ids: []).merge(user_id: current_user.id)
  end

  def set_item
    if user_signed_in?
      user = User.find(current_user.id)
      @items = user.items
    end
  end

end
```

```camp.tags.rb
class CampTags

  include ActiveModel::Model
  attr_accessor :title, :style, :user_id, :item_ids

  with_options presence: true do
    validates :title
    validates :style
  end

  def save(tag_list)
    @camp = Camp.create(user_id: user_id, title: title)
    CampItemRelation.create(camp_id: @camp.id)
    tag_list.each do |tag|
      unless Tag.find_by(style: tag)
        @tag = Tag.create(style: tag)
        CampTagRelation.create(camp_id: @camp.id, tag_id: @tag.id)
      else
        @tag_id = Tag.find_by(style: tag)
        CampTagRelation.create(camp_id: @camp.id, tag_id: @tag_id.id)
      end
    end
  end

end
```

### 試したこと2

save処理後すぐに保存された最新のレコードを取り出す

```camps.controller.rb
  def create
    @camp = CampTags.new(camp_params)

    if @camp.valid?
      @tag_list = camp_params[:style].split(/[[:blank:]]+/).select(&:present?)
      @camp.save(@tag_list)
      camp = Camp.order(updated_at: :desc).limit(1)
      @camp_id = camp.ids
      @item_ids = @camp.item_ids
      binding.pry
      @item_ids.each do |item_id|
        CampItemRelation.create(camp_id: @camp_id, item_id: item_id)
      end
      return redirect_to root_path
    else
      render "new"
    end
  end
```

```camp = Camp.order(updated_at: :desc).limit(1)```
とすることで最新のレコードを一個取り出しています。
答えまで後少し！！！


### たどり着いた回答

```camps.controller.rb
  def create
    @camp = CampTags.new(camp_params)

    if @camp.valid?
      @tag_list = camp_params[:style].split(/[[:blank:]]+/).select(&:present?)
      @camp.save(@tag_list)
      camp = Camp.order(updated_at: :desc).limit(1)
      @camp_id = camp.ids
      @item_ids = @camp.item_ids
      @item_ids.each do |item_id|
        CampItemRelation.create(camp_id: @camp_id[0], item_id: item_id)
      end
      return redirect_to root_path
    else
      render "new"
    end
  end
```
saveメソッド後すぐに最新のレコードを取り出してそれを変数に入れる
```order```で最新のレコードを一つしか取り出していないがidの取り出しには```ids```としなければならないようです。
保存の際に```@camp_id[0]```とすることで配列の一つ目のデータを指定することが叶っています。

以上