0

I am using ActsAsTaggableOn on my Item model.

To get a list of all the items tagged with a particular tag, I have to do this:

Item.tagged_with("some_tag")

However, I am using this in a navigation partial to dynamically generate navigation of tags based on the amount of items in a tag. I want the 4 tags that have the highest number of items.

So I am doing this in my navigation partial:

<% Item.tag_counts.order(:count).limit(4).each do |tag| %>
    <% if params[:tag] == tag.name %>
        <li class="active"><%= link_to tag.name.capitalize, tag_path(tag.name) %></li>
    <% else %>
        <li><%= link_to tag.name.capitalize, tag_path(tag.name) %></li>
    <% end %>
<% end %>

However, when I only have say 2 or 3 items (i.e. any amount below 4 but greater than 1) and I have an item that has been tagged but not 'approved' (i.e. it doesn't show on the page until it has been approved by an admin and the flag is_approved changes from false to true) the tag shows up in this list (which is bad, I don't want it to show up when no items have been approved), but the item doesn't show up when you click the tag (which is good because it hasn't been approved).

How do I modify the each statement to also accommodate items that have been approved?

The easiest way to determine if an Item has been approved is like this:

item.is_approved?

Example:

> i
 => #<Item id: 17, name: "10pp-logo", link: "10pound.zip", description: "10PP Logo.<br><br>Full of graphical good...", price: 6.99, user_id: 1702, created_at: "2013-11-24 12:56:05", updated_at: "2013-11-24 12:56:05", image: "ten.png", is_approved: false, approved_at: nil> 
> i.is_approved?
 => false 

Edit 1:

 > Item.tag_counts
   (2.0ms)  SELECT items.id FROM "items" 
  ActsAsTaggableOn::Tag Load (4.5ms)  SELECT tags.*, taggings.tags_count AS count FROM "tags" JOIN (SELECT taggings.tag_id, COUNT(taggings.tag_id) AS tags_count FROM "taggings" INNER JOIN items ON items.id = taggings.taggable_id WHERE (taggings.taggable_type = 'Item' AND taggings.context = 'tags') AND (taggings.taggable_id IN(13,15,17)) GROUP BY taggings.tag_id HAVING COUNT(taggings.tag_id) > 0) AS taggings ON taggings.tag_id = tags.id
 => [#<ActsAsTaggableOn::Tag id: 6, name: "gmail">, #<ActsAsTaggableOn::Tag id: 11, name: "10pp">, #<ActsAsTaggableOn::Tag id: 4, name: "twitter">, #<ActsAsTaggableOn::Tag id: 5, name: "facebook">, #<ActsAsTaggableOn::Tag id: 8, name: "fitness">, #<ActsAsTaggableOn::Tag id: 1, name: "notepad">, #<ActsAsTaggableOn::Tag id: 2, name: "paper">, #<ActsAsTaggableOn::Tag id: 9, name: "logo">, #<ActsAsTaggableOn::Tag id: 7, name: "login form">] 
marcamillion
  • 32,933
  • 55
  • 189
  • 380

1 Answers1

0

Why not only list the approved items?

class Item < ActiveRecord::Base
  def self.approved_and_tagged_with(tags)
    self.tagged_with(tags).where(approved: true)
  end
end

# View
Item.approved_and_tagged_with("some_tag")

Update: to wrap the most tagged items

class Item < ActiveRecord::Base
  scope :approved, where(approved: true)

  # @param: options. allow nil
  #   example: most_tagged(limit: 10, require_approval: false)
  def self.most_tagged(options={})
    limit = options[:limit] || 4
    require_approval = options[:require_approval] || true
    results = tag_counts.order(:count).limit(limit)
    results = results.approved if require_approval?
    results
  end
end

# View
Items.most_tagged.each do |item|
  # blah blah
end
Billy Chan
  • 24,625
  • 4
  • 52
  • 68
  • The issue is that I am not calling just 1 tag. I think you are on the right track, but my `navigation` doesn't quite work like that. I want to modify the iterator, I am updating the question with the full code for the navigation partial. – marcamillion Nov 25 '13 at 08:29
  • It doesn't matter how many tags you want or any other fancy things you need to do. As long as you need to hit db, you can combine the approved logic inside, by wrapping act_as_taggable_on method. – Billy Chan Nov 25 '13 at 08:41
  • So ideally I would love to be able to as I am iterating through all of the tags on `tag_count`, to just look at the tags `where(is_approved: true)`. – marcamillion Nov 25 '13 at 08:41
  • Can you update your answer to reflect your suggestion and my actual code please. Thanks. – marcamillion Nov 25 '13 at 08:42
  • Nice...out of curiosity...why did you include `options.require_approval || true`? – marcamillion Nov 25 '13 at 09:00
  • That's totally optional, depends on your need. For example you can show all items to admin including unapproved. By the way, I don't know where the `:count` comes from so you need revise that part. – Billy Chan Nov 25 '13 at 09:02
  • By the way...I love this....getting this error though: `NoMethodError at / undefined method 'limit' for {}:Hash` – marcamillion Nov 25 '13 at 09:05
  • Oh, they should be `options[:limit]`. I wrote too much JS today, mixed:) – Billy Chan Nov 25 '13 at 09:23
  • I think you are missing a method `require_approval?`. Getting that error. – marcamillion Nov 25 '13 at 09:49
  • Also...`approved` was scoped to Item, not a collection. Getting this error too: `NoMethodError at / undefined method 'approved' for #`. – marcamillion Nov 25 '13 at 10:57
  • @marcamillion, I think you've quit understood that StackOverflow is not a place for asking working code. Actually the code I provided is a bit hypothetical, it just provides another way of thinking, and very probably contains bugs. I'd be happy if the code could be of a bit help and provide some inspiration. However, to make it working in your app, you still need to adjust and test it. – Billy Chan Nov 25 '13 at 11:17