acts-as-taggable-on を利用すると以下のように簡単にタグクラウドの実装が出来てしまいます。
mbleigh/acts-as-taggable-on: A tagging plugin for Rails applications that allows for custom tagging along dynamic contexts.
<% tag_cloud(@tags, %w(css1 css2 css3 css4)) do |tag, css_class| %> <%= link_to tag.name, { :action => :tag, :id => tag.name }, :class => css_class %> <% end %>
内部的には
counter cache を利用して、都度件数を算出することがないようにしています。
# acts-as-taggable-on-5.0.0/lib/acts_as_taggable_on/tagging.rb module ActsAsTaggableOn class Tagging < ::ActiveRecord::Base #:nodoc: DEFAULT_CONTEXT = 'tags' belongs_to :tag, class_name: '::ActsAsTaggableOn::Tag', counter_cache: ActsAsTaggableOn.tags_counter
Counter Cache には、条件を追加できない
ところが、デフォルトでは Counter Cache に条件を追加することはできません。
例えば、記事を表示するようなブログの場合、まだpublishedしていない記事の場合は、tagの件数に含めたくないですよね。
いくつか解決策はあるようで、以下のgemを利用すれば条件の追加ができるようになります。
- magnusvk/counter_culture: Turbo-charged counter caches for your Rails app.
- ActiveRecordのcounter_cacheに条件を与える - Qiita
ただ、acts-as-taggable-on のデータ構造は、汎用的に作られたタグ管理のため、私が望む形で利用することができませんでした。(使い方間違っただけかもしれませんが)
他の解決策
例えば、counter cache をやめる。
counter cache の自動更新ではなく、Active Record の callback を利用し任意のタイミングで更新させる。
邪悪な感じがしますが、これで対応して見ました。
やり方
1. counter cache を止める
counter cacheの機能自体は、以下で止めることができます。
taggings_count カラムはそのまま利用することにします。
Home · mbleigh/acts-as-taggable-on Wiki
ActsAsTaggableOn.tags_counter = false
2. callback で更新する
タグ付け対象のモデル
class Article < ApplicationRecord after_update :update_counter_cache private def update_counter_cache if saved_change_to_attribute?(:status) taggings.each { |tagging| tagging.update_counter_cache } end end
タグ管理テーブル
# app/lib/core_ext/acts_as_taggable_on/tagging.rb # config/initilaizers で読み込むようにしています module ActsAsTaggableOn class Tagging after_save :update_counter_cache after_update :update_counter_cache def update_counter_cache article_ids = ActsAsTaggableOn::Tagging.where(tag_id: self.tag_id).pluck(:taggable_id) published_article_count = Article.published.where(id: article_ids).count self.tag.update!(taggings_count: published_article_count) end end end