概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Rails belongs_to association - refactor queries and stay DRY
- 原文公開日: 2018/01/02
- 著者: Paweł Dąbrowski
Rails: belongs_to関連付けをリファクタリングしてDRYにする(翻訳)
belongs_to
リレーションはRailsアプリで最もよく使われる関連付けなので、皆さまのアプリでも多数使われていると確信しています。さて、Job
とCategory
という2つのモデルがあるとしましょう。1つのjobは1つのcategoryに属し、1つのcategoryには多くのjobがあるというシンプルな関連付けがなされています。
Category
モデルにはpublished:boolean
という属性があり、そのcategoryのjobを表示してよいかどうかを指定します。ここでの目的は、publishされたカテゴリに割り当てられているjobだけを返すクエリを作成することです。
普通の方法
通常は、以下のような方法を使います。
Job.joins(:category).where(categries: {published: true})
これに何かまずい点があるのでしょうか?別にありません。しかしここでは「ロジックの分離」に着目したいと思います。Job
モデルで何か操作を行う場合、publishされたcategoryだけを取り出すという条件を満たすことを気にかけるべきではありません。これはCategory
モデルに関連するロジックなので、このロジックをそちらに移動しましょう。
ロジックの分離
class Category < ActiveRecord::Base
has_many :jobs
def self.publishable
where(published: true)
end
end
ここではクラスメソッドの代わりにスコープを使うこともできます(スコープかクラスメソッドかについては別記事をご覧ください【原文リンク切れ】)。今回の場合、クラスメソッドの方が私にとって明確に思えたのでクラスメソッドを使うことにします。これによってJob
モデルは次のようになります。
class Job < ActiveRecord::Base
belongs_to :category
def self.publishable
joins(:category).merge(Category.publishable)
end
end
これで、次のように呼び出せるようになりました。
Job.publishable
改善の理由
メンテナンスするコードがもっとたくさんある場合になぜ2番目のソリューションの方がよいかについて疑問をお持ちの方もいらっしゃるかもしれません。理由は次のとおりです。
- ロジックを分離できます。categoryに関連するものは
Category
モデルに配置され、jobに関連するものはJob
に配置されています。あなたの同僚は、ロジックをチェックして正確にはどんなクエリが使われるべきかを調べなくても、Job.publishable
を呼び出すだけで済みます。 -
最初のバージョンのクエリだと、アプリのあちこちに
Job.joins(:category).where(categries: {published: true})
がばらまかれてしまいます。そのcategoryがpublishされているかどうかを調べるために条件をもっと詳しくチェックしなければならないとしたらどうしますか?ばらまかれているコードをすべて見つけ出すという残念な方法を取らざるを得なくなります。しかし2番目の方法なら、メソッドに変更を加えるだけで済みます。他に何も変更する必要はありません。 -
人間にとってより読みやすいコードになります。これはチームで若手開発者を抱えている場合に非常に重要な点です。
-
Category
モデルに関連付けられているあらゆるモデルでCategory.published
を使えるようになります。
Railsでお困りの方にお知らせ
知りたいことがありましたら、twitter または連絡用フォームにてお気軽にお問い合わせください。