どうもチャンクノです!!
今回は中間テーブルについて書いていきます。
多分基本的に中間テーブルを作成する場合、多対多の両テーブルのidを中間テーブルに持たせると思います。
https://qiita.com/gomasio1010/items/2cff1e11d2cc82fde464
この記事のような感じ。
でも中間テーブルって必ずしもidじゃなくていいみたいです。
自分が今担当している部分の中間テーブルは
create_table :search_condition_areas do |t|
t.string :area_code
t.references :search_condition, foreign_key: true
t.timestamps
end
こんな感じになってます。
このままだと :area_codeがただのstring型のカラムになってしまうので、
has_many :search_condition_areas, foreign_key: :area_code, primary_key: :code
has_many :search_conditions, through: :search_condition_areas
モデル側でforeign_keyとprimary_keyを指定してあげてます。
これで基本的な中間テーブルを作成した時と同じ状態になりました。
さて、気になるのはこれをcreateする時ですね。
def search_condition_params
params.require(:search_condition).permit(
:hoge, :huga, :foo, area_?: [] <= ここ
)
end
ここの?に何が入るかわかりますか?
僕は脳死で area_codes: [] にしました!!!
え、違うの?そんな声が聞こえてきそうですねえ。うんうん。違うんです。
ここは普通に area_ids: [] です。
例えば @search_condition.area_ids とコンソールで叩けばidではなく
[83] pry(main)> @search_condition.area_ids
=> ["11101000000", "13101000000", "13103000000"]
こんな感じで @search_condition に紐づく中間テーブルが持っているarea_codeが返ってきます。
[99] pry(main)> area.search_condition_ids
(1.8ms) SELECT `search_conditions`.`id` FROM `search_conditions`
INNER JOIN `search_condition_areas` ON `search_conditions`.`id` = `search_condition_areas`.`search_condition_id`
WHERE `search_condition_areas`.`area_code` = '13000000000'
=> [3]
逆も然り。これは普通にsearch_conditionのidが返ってきます。
〜ids <= この部分は collection_singular_ids このメソッドを使っています。
一対多の時使えるメソッドみたいですね。
https://railsdoc.com/association
ここに色々書かれています🙆♂️
https://www.rubydoc.info/gems/activerecord/ActiveRecord%2FAssociations%2FClassMethods:has_many
英語できる人はこっちの方がいいっぽいです。
これがあったので、もしかしてarea_codesじゃなくてarea_idsなんじゃね?って気付くことができました!!
で、ここで気を付けなければいけないのが
@search_condition.area_ids = @new_search_condition.area_ids
@search_condition.save
コントローラーの処理でこのような記述を使用する場合ですね。
どうなると思いますか?
@search_condition.area_ids = @new_search_condition.area_ids
これ @search_condition.save の処理が走る前に上記の部分が上書き保存されてしまいます。
どうやらautosaveが自動で走ってしまうみたいです。
accepts_nested_attributes_for を使っている場合必ずautosaveがtrueになってしまうのでどうしようもないっぽいです。
User.rb
has_one :search_condition
accepts_nested_attributes_for :search_condition <= 多分ここが原因
おそらくですが親テーブルに accepts_nested_attributes_for が使用されていたらautosave: falseは使えないんでしょうね。
なので、ここでは親テーブルが保存されてから子テーブルを上書き保存する処理を書くのがいいのかなーと思います。
if @search_condition.save
@search_condition.area_ids = new_search_condition.area_ids
else
render "new"
end
こんな感じ。
多分もっといいやり方があるんだろうなあ。
ごちゃごちゃと色々書いてきましたが、まとめると
・中間テーブルに持たせるものは両テーブルのidじゃなくても良い。
・でもその場合はidを持たせないテーブルにforeign_keyとprimary_keyの記述を忘れずに。
・idを持たせない場合でもコントローラーのパラメーターは必ず~idsにする。
・accepts_nested_attributes_forを使用している場合はautosaveに気を付ける。
こんなところですかね😯
あんまり中間テーブルの実装には触れてこなかったのでお勉強になりました🙏
覚えることは山のようにあるなあ、、、。
では今回はこんな感じで!!
間違ってるところとかあったらコメントください!
それでは皆様良きプログラミングライフを🙏