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を利用すれば条件の追加ができるようになります。
ただ、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
タグ管理テーブル
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