nested_form_fieldsを使って複数情報を一括で「登録＋削除」する場合、登録情報の制約は独自バリデーションにする必要があるっぽい？

# やりたいこと

「タスクテーブル（tasks）」に、「タグマスタ（tags）」情報を複数紐付ける。

- 「tasks」と「tags」の関係は【N : N】
    - 中間テーブルとして「task_tags」テーブルを使用。
   ![スクリーンショット 2019-05-05 11.52.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410674/e95c8673-8e2b-d6eb-50eb-f25121138de8.png)

```ruby:task.rb
  ####↓↓↓↓tag関係のアソシエーション情報↓↓↓↓############
  has_many :task_tags , dependent: :destroy
  # nestedfieldの使用にあたり、↓を追記
  accepts_nested_attributes_for :task_tags, allow_destroy: true

  has_many :pasted_tags , through: :task_tags , source: :tag
  ####↑↑↑↑tag関係のアソシエーション情報↑↑↑↑############
```
```tag.rb
  ####↓↓↓↓アソシエーション情報↓↓↓↓############
  has_many :task_tags, dependent: :restrict_with_error
  ####↑↑↑↑アソシエーション情報↑↑↑↑############
```
```task_tag.rb
  ####↓↓↓↓アソシエーション情報↓↓↓↓############
  belongs_to :task
  belongs_to :tag
  ####↑↑↑↑アソシエーション情報↑↑↑↑############
```

- 「tasks」の登録時に、複数の「tags」を紐付けられるようにする。
- 一つの 「tasks」情報に対しては、同じ「tags」情報は紐付けられないようにする。

# 「nested_form_fields」を使ってみると……
使い方については、
[https://qiita.com/classe729/items/651c0f07e1f442d99c74](https://qiita.com/classe729/items/651c0f07e1f442d99c74)
[https://www.virment.com/add_and_remove_rails_nested_form_dynamically/](https://www.virment.com/add_and_remove_rails_nested_form_dynamically/)
参照（特に1つ目のリンクにあるように、requireの入れ忘れには要注意）。
なおrequireは、記述する順番にも注意しないと既存のjs関係に影響を与える場合もありそう
……自分の場合、なんにも考えず

//= require jquery3　←他のjs用に、すでに書いていたもの
……中略……
//= require jquery　←今回追記したもの

とかしたら、rspecテストでjsが動かなくなった（いや、「jquery3」があるなら「jquery」要らんだろ、気付けよ！）。

##【ちょっと寄り道】
##### task_tagsの「tag_id」は、【登録されているtags情報のどれか】を選択して登録する
##### →「nested_form_fields」内の入力フォームも一工夫（本題とは関係ないけど）。
tasksの登録、更新画面

```ruby:_form.hetl.erb
<%= form.nested_fields_for :task_tags, wrapper_tag: :tr do |q| %>
  <td ><%= q.select   :tag_id, get_tags_as_selectbox_info , class: 'form-control' %></td>
  <td ><%= q.remove_nested_fields_link 'Delete', class: 'btn btn-danger', role: 'button' %></td>
<% end %>
```
で、この二行目の「get_tags_as_selectbox_inf」は

```ruby:tags_helper.rb
module TagsHelper

  def get_tags_as_selectbox_info
    tags = Tag.all.order(cd: "ASC")
    selectbox_info = []

    tags.each do |tag|
      # 表示形式は「コード：名称」、登録はidで行う。
      selectbox_info.push(["#{tag.cd}:#{tag.name}",tag.id])
    end
    return selectbox_info
  end

end
```
とすることで、tagの「コード：名称」を画面上で表示・選択。
選択されたtagのidを「task_tags」の「tag_id」として登録することにする。

### ↓↓　実装結果は、こんな感じ　↓↓　……ここまでは（比較的）順調だった。
![スクリーンショット 2019-05-05 12.54.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410674/3e4bb5dc-6fda-365c-1b4f-536d645272d1.png)
これで通常の登録、削除はできるようになった。
ただ↑だと一つのtaskに「001：社長案件」という同じtag（画面表示上は「ラベル」）を複数登録しようとしている。
……こういった情報をバリデーションでどう弾くか？

##　標準の「uniqueness」制約を加えればいい……そんなふうに考えていた時期が俺にもありました。

え、だってつまり、中間テーブル「task_tags」で【「task_id」＋「tag_id」】をユニークにすればいいんでしょ？

```task_tag.rb
  ########↓バリデーション情報↓########
  validates :task_id,  uniqueness: { scope: :tag_id}
  ########↑バリデーション情報↑########
```
としてみたら、通るはずのものがバリデーションエラーになったり、エラーになるはずのものが通ったり。
これは推測も含みますが、

【rails標準のバリデーション】
→登録しようとしている情報を、一行ずつ順番に**現在時点（＝登録前）のテーブル情報**と比較して、それを登録できるか判断。
【「nested_form_fields」による登録……いや、普通の登録でも同じかも？】
→上記バリデーションが全て終わった後で、改めてデータを登録。

としているっぽい。

なので、
タグ「001：社長案件」が登録されているタスクに対し、
![スクリーンショット 2019-05-05 13.17.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410674/6895500e-69d3-382d-1047-56178772969d.png)

例①）
①ー1：登録されている「001：社長案件」をDelete
①ー2：「Add new」で追加した「タグのselect枠」で、改めて「001：社長案件」を選択
（画面上の表示は変化なし）

→「①ー2」についてのバリデーション時は、DBにはまだ「①ー1」情報が残っている
**→バリデーションに引っかかる**

例②）
②ー1：「Add new」で追加した「タグのselect枠」で、「101：要、社長決済」を選択
②ー2：「Add new」で追加した「タグのselect枠」で、「101：要、社長決済」を（追加でもう一つ）選択
![スクリーンショット 2019-05-05 13.26.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410674/55b38c27-b230-e1ba-b202-88223f70cc73.png)

→「②ー2」についてのバリデーション時は、DBにはまだ「②ー1」情報が登録されていない
**→バリデーションに引っかからない**

となってしまう模様……orz。

### 仕方がないから独自バリデーションを……掛けるためのデータはどう取得すればいいんだろう？
####「task_tags」に登録する情報についてのバリデーションではあるのだが……
「task_tags」モデルでのバリデーションだと、【登録する「task_tags」、**1レコードごと** 】についてしか確認できない。
仕方がないので「tasks」モデルのバリデーションで、【紐付いて登録するはずの「task_tags」情報を確認】する方法を探してみた。

![スクリーンショット 2019-05-05 13.47.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410674/3a4f94cf-3cf3-2f41-c04d-86c7771abb69.png)
↑の情報を、
・1行目、2行目をDelete(削除)→「500：中止の可能性あり」が新たな1行目になる。
・新たな2行目、3行目として「003：部長案件」「103：要、部長決済」を追加
![スクリーンショット 2019-05-05 13.51.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410674/537769fb-4b4d-c373-d4b7-bb6a9bb90145.png)

この登録時、taskモデルバリデーションを走るプログラムをbinding.pryで止めてみると

```ターミナルの.log

[1] pry(#<Task>)> task_tags
  TaskTag Load (0.2ms)  SELECT "task_tags".* FROM "task_tags" WHERE "task_tags"."task_id" = $1  [["task_id", 47]]
=> [#<TaskTag:0x00007f960d851bf0 id: 40, task_id: 47, tag_id: 3, created_at: Sun, 05 May 2019 13:45:41 JST +09:00, updated_at: Sun, 05 May 2019 13:46:46 JST +09:00>,
 #<TaskTag:0x00007f960d8519e8 id: 41, task_id: 47, tag_id: 5, created_at: Sun, 05 May 2019 13:45:41 JST +09:00, updated_at: Sun, 05 May 2019 13:46:46 JST +09:00>,
 #<TaskTag:0x00007f960d8517e0 id: 42, task_id: 47, tag_id: 11, created_at: Sun, 05 May 2019 13:45:41 JST +09:00, updated_at: Sun, 05 May 2019 13:46:46 JST +09:00>,
 #<TaskTag:0x00007f960d85b3a8 id: nil, task_id: 47, tag_id: 15, created_at: nil, updated_at: nil>,
 #<TaskTag:0x00007f960d858f18 id: nil, task_id: 47, tag_id: 17, created_at: nil, updated_at: nil>]
[2] pry(#<Task>)> task_tags[0]._destroy
=> true
[3] pry(#<Task>)> task_tags[1]._destroy
=> true
[4] pry(#<Task>)> task_tags[2]._destroy
=> false

```
・『task_tags[行番号]』で、【今回追加登録，更新，削除する情報（及び、変更なしで登録したままにしておく情報）】を取得できる。
・『task_tags[行番号]._destroy』で、それが【今回削除する情報】かどうかを確認できる（削除ならTrue、そうでないならFalseが返される。

***ーーこれだ！　これが欲しかったんだ！***（見つけるのに数時間掛かった）

###後は、これを使ってバリデーションをでっち上げれば解決。
taskモデルにて、

```task.rb
class Task < ApplicationRecord

  validate :tag_not_deplicate

  def tag_not_deplicate
    #「今回登録しようとしているタグ」を集める枠を用意する
    tags_try_to_save=[]

    task_tags.each do |task_tag|
      #「_destroy」ではない→「今回登録しようとしているタグ」であるならば
      if task_tag._destroy == false
        #そのタグidを枠に格納
        tags_try_to_save.push(task_tag["tag_id"] )
      end
    end

########中略########

end
```

書き方はもう少し工夫できるかもだけど、これでとりあえずバリデーションが効くようになった。
でも削除データかどうかを識別するための「_destroy」のT/F判定とか、gemは何かカスタマイズしようとした時の情報取得方法が難しすぎる（今回も、コントローラ内で取得できるparams情報とは、びみょーにデータの持ち方が違うんです）。

参考用に、登録時にコントローラ内でbinding.pryした場合の、paramsが持っているデータ
(細かい数値（id）は、先程のものとは一致していません）。

```ターミナルの.log
    77: def update
    78:   binding.pry
 => 79:   respond_to do |format|
    80:     if @task.update(task_params)==true
    81:       format.html{redirect_to edit_task_path(@task) , notice: t('activerecord.normal_process.do_update') }
    82:     else
    83:       format.html{render "edit"}
    84:     end
    85:   end
    86: end

[1] pry(#<TasksController>)> params
=> <ActionController::Parameters {"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"S3OMv57a9ZUTRB8qbJ8YvdFBc3hyZZX0aOcPTuS27HeOgaStaopBXdsVsUChtGBupzsT36DZ+zUGKt7qzkyNDQ==", "task"=>{"user_id"=>"2", "status"=>"0", "name"=>"qwe", "content"=>"wer", "limit"=>"", "priority"=>"2", "task_tags_attributes"=>{"0"=>{"_destroy"=>"1", "tag_id"=>"11", "id"=>"42"}, "1"=>{"_destroy"=>"1", "tag_id"=>"15", "id"=>"43"}, "2"=>{"tag_id"=>"17", "id"=>"44"}, "3"=>{"tag_id"=>"4"}, "4"=>{"tag_id"=>"3"}}}, "commit"=>"登録する", "controller"=>"tasks", "action"=>"update", "id"=>"47"} permitted: false>
[2] pry(#<TasksController>)> params[:task][:task_tags_attributes]
=> <ActionController::Parameters {"0"=>{"_destroy"=>"1", "tag_id"=>"11", "id"=>"42"}, "1"=>{"_destroy"=>"1", "tag_id"=>"15", "id"=>"43"}, "2"=>{"tag_id"=>"17", "id"=>"44"}, "3"=>{"tag_id"=>"4"}, "4"=>{"tag_id"=>"3"}} permitted: false>

```

あるいは、もっと簡単なやり方があるのだろうか？