轨道 - 设计/充当可标记的宝石 - 使标记所有权对用户唯一



用户可以登录(设计gem),创建一个文本帖子,并标记该帖子(通过acts-as-taggable-on gem)。如果某个标签在帖子中多次使用,则该标签的 CSS 会发生变化。例如,在下面的示例中,#foobar 上的下划线样式消失了。但是,就目前而言,这种情况正在跨用户帐户发生。这意味着用户为标记其帖子而输入的标签并非其帐户所独有。换句话说,如果用户 1 在单独的帐户上标记 #foobar 及之后,则 User2 也会标记 #foobar - 两个用户帐户上的下划线都会消失。我的目标是使用户在发布表单中输入的标签对该用户是唯一的。

例:

用户1 帐户

https://i.stack.imgur.com/dgIQ8.jpg

用户2 帐户

http://imgur.com/29gzutj

我已经尝试了很多解决方案 - 没有一个有效!!请帮忙!

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

新.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 %>

索引.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

发布模型

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

一种方法:

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> 

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

问题是每当任何tagger使用标记时,tags表上的 taggings_count 属性都会递增。所以你需要提出一个新的查询。

您可以通过选择标记表

来获取要查找的数据,进行一些表联接,以便仅选择特定用户创建的标记,然后从中获取要查找的计数。

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

 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"}]

但如果你真的想让它恢复ActsAsTaggableOn::Tag s,我认为这是可能的。

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

   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

最新更新