Rails - Devise/Acts-as-taggable-on Gems - 使标签所有权对用户唯一?

Rails - Devise/Acts-as-taggable-on Gems - Make tag ownership unique to a user?

用户可以登录 (devise gem), create a text post, & tag that post (via acts-as-taggable-on gem)。如果在 post 秒内多次使用一个标签,则该标签的 CSS 会发生变化。例如,在下面的示例中,#foobar 上的下划线样式消失了。但是,就目前而言,这是跨用户帐户发生的。这意味着用户输入的标记他们的 post 的标签不是他们帐户独有的。换句话说,如果 User1 标记 #foobar 之后,在一个单独的帐户上,User2 也标记#foobar - 两个用户帐户的下划线都消失了。我的目标是使用户在 posting 表单中输入的标签对该用户是唯一的。

示例:

用户 1 帐户

http://imgur.com/VnnO7IQ

用户 2 帐户

http://imgur.com/29gzutj

我尝试了很多解决方案 - none 其中有效!!请帮忙!

posts_controller.rb

class PostsController < ApplicationController

 def index    
    if params[:tag]
     @posts = current_user.posts.tagged_with(params[:tag]).order(created_at: :desc)
    else 
     @posts = current_user.posts.includes(:things).all.order(created_at: :desc)
    end 
 end 

 def new
    @post = current_user.posts.build
 end

 def create
    @post = current_user.posts.build(post_params)
    if @post.save
        redirect_to '/posts'
    else
        render 'new'
    end

end

new.html.erb

<button id='addtext'>text</button>

<%= form_for @post, html: { multipart: true } do |f| %>

<%= f.text_field :tag_list %>
 <%= f.fields_for :things do |ff| %>
<% end %> 

<%= f.submit %>
<% end %>

index.html.erb

<div id="posts">
  <%= render @posts %>
</div>

_post.html.erb

<div class="post">
   <%= raw post.tags.order("taggings_count DESC").map {|t| link_to t.name, tag_path(t.name), class: css_class_for_tag(t.taggings_count)}.join(' ') %> 

   <% post.things.each do |thing| %>
     <%= thing.try(:text) %>
   <% end %>
</div>

post_helper.rb

module PostsHelper
 def css_class_for_tag count
  case count
   when 1 
     'new_tag'
   else
    'used_tag' 
   end
 end
end

post型号

class Post < ActiveRecord::Base
  acts_as_taggable
  ActsAsTaggableOn.delimiter = ' '
  ActsAsTaggableOn.force_lowercase = true
  belongs_to :user 
  has_many :things, dependent: :destroy
  accepts_nested_attributes_for :things, allow_destroy: true 
end

事物模型

class Thing < ActiveRecord::Base
  belongs_to :post
  has_attached_file :image, styles: { small: "300x300>", medium: "600x600>" }
  validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end

用户模型

class User < ActiveRecord::Base
  acts_as_tagger
  has_many :posts 
  devise :database_authenticatable, :registerable,
     :recoverable, :rememberable, :trackable, :validatable
end

架构

ActiveRecord::Schema.define(version: 20160303110608) do

create_table "posts", force: :cascade do |t|
 t.datetime "created_at", null: false
 t.datetime "updated_at", null: false
 t.integer  "user_id"
end

create_table "taggings", force: :cascade do |t|
 t.integer  "tag_id"
 t.integer  "taggable_id"
 t.string   "taggable_type"
 t.integer  "tagger_id"
 t.string   "tagger_type"
 t.string   "context",       limit: 128
 t.datetime "created_at"
end

add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"

create_table "tags", force: :cascade do |t|
 t.string  "name"
 t.integer "taggings_count", default: 0
end

add_index "tags", ["name"], name: "index_tags_on_name", unique: true

create_table "things", force: :cascade do |t|
 t.text     "text"
 t.integer  "post_id"
 t.datetime "created_at",         null: false
 t.datetime "updated_at",         null: false
 t.integer  "order"
end

create_table "users", force: :cascade do |t|
 t.string   "email",                  default: "", null: false
 t.string   "encrypted_password",     default: "", null: false
 t.string   "reset_password_token"
 t.datetime "reset_password_sent_at"
 t.datetime "remember_created_at"
 t.integer  "sign_in_count",          default: 0,  null: false
 t.datetime "current_sign_in_at"
 t.datetime "last_sign_in_at"
 t.string   "current_sign_in_ip"
 t.string   "last_sign_in_ip"
 t.datetime "created_at",                          null: false
 t.datetime "updated_at",                          null: false
end

add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true

end

问题是 tags table 上的 taggings_count 属性会在任何 tagger 使用标签时递增。所以你需要想出一个新的查询。

您可以通过在标记 table 上创建 select 来获取您正在寻找的数据,进行一些 table 连接,这样您就可以 select 标记由特定用户制作,您可以从中获得所需的计数。

我写了一个查询来演示您需要的结构:

 class Post < ActiveRecord::Base
   acts_as_taggable
   ActsAsTaggableOn.delimiter = ' '
   ActsAsTaggableOn.force_lowercase = true
   belongs_to :user
   has_many :things, dependent: :destroy
   accepts_nested_attributes_for :things, allow_destroy: true

   def tags_with_user_specific_counts
     query = <<-SQL
       SELECT taggings.tag_id,
              tags.name,
              count(taggings.id)
         FROM taggings
         INNER JOIN tags
           ON taggings.tag_id = tags.id
         WHERE taggings.tagger_id = '#{user_id}'
           AND taggings.tagger_type = 'User'
           AND taggings.taggable_id = '#{id}'
           AND taggings.taggable_type = 'Post'
         GROUP BY taggings.tag_id, tags.name
     SQL
     ActiveRecord::Base.connection.execute(query).to_a
   end
 end

唯一要注意的是我没有使用 ActiveRecord 方式编写这个,所以这会生成一个包含您想要的数据的哈希数组:

[{"tag_id"=>"1", "name"=>"jim", "count"=>"1"},
 {"tag_id"=>"2", "name"=>"bob", "count"=>"2"}]

但是如果你真的想要这个 return ActsAsTaggableOn::Tags,我认为这是可能的。

编辑 - 此 post 已更新为 运行 使用 taggings.tagger_id 的查询。此 post 的先前版本建议 运行 使用此查询:

   def tags_with_user_specific_counts
     query = <<-SQL
       SELECT taggings.tag_id,
              tags.name,
              users.id as user_id,
              count(taggings.id)
         FROM taggings
         INNER JOIN tags
           ON taggings.tag_id = tags.id
         INNER JOIN posts
           ON taggings.taggable_id = posts.id
           AND taggings.taggable_type = 'Post'
         INNER JOIN users
           ON posts.user_id = users.id
         WHERE users.id = '#{user_id}'
         GROUP BY taggings.tag_id, tags.name, users.id
     SQL
     ActiveRecord::Base.connection.execute(query).to_a
   end

一种方法:

tag_counts = ActsAsTaggableOn::Tag.joins(:taggings).
  where(taggings: { taggable_type: "Post", taggable_id: current_user.post_ids }).
  group("tags.id").count

给你这样的东西:

{ 7=>2, 1=>2, 5=>4 }

第一个数字是标签 ID,第二个数字是该标签在 current_user 中出现的次数。

现在您可以这样做了:

<div class="post">
  <%= raw post.tags.map{ |t| [t.name, tag_counts[t.id]] }.sort_by{ |t| -t[1] }.
                    map{ |t| link_to t[0], tag_path(t[0]), class: css_class_for_tag(t[1])}.join(' ') %>
</div> 

这可以写得更有表现力,但比...更长 ;-) 基本上,我们像以前一样遍历 post 的标签,然后在第一个 map 语句中将每个标签与其特定于 current_user 的计数连接起来。然后我们按计数倒序排序。在最后的 map 语句中,我们像以前一样生成链接,只是再次使用用户特定计数。