環境
ruby 2.4.2
rails 5.1.4
やりたいこと
eager loadingする際に、参照先テーブルが持ってる外部テーブルまでloadingしたい。
ということがあったので、備忘録も兼ねて紹介したと思います。
# => : 参照先とする
A => B
B => C
となっていた時に、AがBを通してCまでloadingするということです。
例えば下記のような3つのmodelがあり、Prefectureテーブル(A)からCountryテーブル(B)を通して、Continent(C)テーブルをloadingします。
class Prefecture < ApplicationRecord
belongs_to :country
end
class Country < ApplicationRecord
belongs_to :continent
end
class Continent < ApplicationRecord
has_many :countries
end
やりかた
このように、includeするcounrty(B)のシンボルに、continent(C)をネストする形で渡してあげると良いです。
class PrefecturesController < ApplicationController
@prefectures = Prefecture.all.includes(country: :continent)
end
ちなみにもし、counrty(B)がcontinentの他に、prefecture以外にLeaguesという外部テーブルとも関連付けが合って、それも参照がしたい場合、このようにしてあげる。
class Country < ApplicationRecord
belongs_to :continent
belongs_to :league
end
class PrefecturesController < ApplicationController
@prefectures = Prefecture.all.includes(country: [:continent, :league])
end
おまけ
デメテルの法則では、「オブジェクトAは別のオブジェクトBのサービスを要求してもよい(メソッドを呼び出してもよい)が、オブジェクトAがオブジェクトBを「経由して」さらに別のオブジェクトCのサービスを要求してはならない。」ということが言われており、今回のケースに当てはまってしまっています。
しかし個人的には、今回のケースのオブジェクトA(Prefecture)が要求したオブジェクトC(Continent)のサービスは、オブジェクトCの値だったので、そんなに問題ないかなという認識です。
もしこれが、continent_modelに定義したメソッドの呼び出しなどになるとデメテルの法則で言われているように、保守が辛くなってしまうので気をつけたいですね。
デメテルの法則に関してはこちらの記事がわかりやすいです。
http://ruby-rails.hatenadiary.com/entry/20150922/1442923521