所以我现在有这样的系统:
class Tag < ActiveRecord::Base
# attr :name
end
class Article < ActiveRecord::Base
has_many :tag_associations, dependent: :destroy
has_many :tags, through: :tag_associations
end
class Company < ActiveRecord::Base
has_many :articles
end
class User < ActiveRecord::Base
belongs_to :company
end
我想这样做:
user.company.articles.includes(:tags).all
我将这样使用它(我已经有一个问题,关于这方面的热切加载):
company.articles.all.select do |article|
article.tags.any? do |tag|
tag.name == 'foo'
end
end
如何在articles_by_tag
的公司对象上创建新的关联?
class Company < ActiveRecord::Base
has_many :articles
has_many :articles_by_tag, scope -> { |tag| ... }
end
我怎么会使用它呢?
company.articles_by_tag(tag: 'foo').all
这是一本X&Y问题。急于加载所有东西,然后在Ruby中使用带有块的.select
循环浏览,基本上就像在餐厅点每一个披萨,只为了找到一个上面有凤尾鱼的披萨,然后扔掉其他所有东西。
相反,您使用INNER JOIN来过滤行:
Acticle.joins(:tags)
.where(tags: { name: 'foo' })
这将只返回标记表中匹配的行。
如果你想把它写进一个范围,你可以这样做:
class Article < ApplicationRecord
scope :with_tag, ->(name){ joins(:tags).where(tags: { name: name }) }
end
作为奖励,您可以使用GROUP
和HAVING
查找带有标签列表的文章:
class Acticle < ApplicationRecord
def self.with_tags(*tags)
left_joins(:tags)
.group(:id)
.where(tags: { name: tag })
.having(Tag.arel_table[Arel.star].count.eq(tags.length))
end
end