要約
railsで一覧画面に何かしらの集計値とかフラグを表示したいときがあると思います。
いいね数とかそういうの。
それをcounter_cacheとかでデータとして持たずに、動的に集計してきれいに組み込む方法です。
やり方
class Post < ApplicationRecord
has_many :likes, as: :likable
end
class Like < ApplicationRecord
belongs_to :likable, polymorphic: true
end
@posts = Post.all.select("posts.*, (select count(*) from likes l where l.likable_type='Post' and l.likable_id=posts.id) as likes_count")
@posts.first.likes_count # as xxxがattributeになる
=> 3
サブクエリ(select count(*) from likes l where l.likable_type='Post' and l.likable_id=posts.id)
の中は好きなように書けるので、かなり汎用性高くフラグなりなんなりつけられるので、これだけ覚えておけば集計付き一覧はだいたいできると思います。
個人的にrailsのjoin+preload/eager_load/includesなどを使ってrailsのオブジェクトとしてしっかりつくる、という方法にこだわってきたのですが、これだとassociationの書かれ方に影響されて毎回違う書き方になってしまいます。フラグがひとつふたつ付けばいい場合にはやりすぎになってしまいがちです。
一方で、この書き方は多少生のsql書いてしまいますが、使い回しが効くのではないかと思います。