3

我正在开发一个多站点 CMS,它具有站点之间交叉发布的概念。多种类型的内容(文章、活动、简历等)可以与许多站点相关联,并且站点可以有许多内容。内容片段和站点之间的多对多关联还必须支持每个关联内容项的几个共同属性——站点起源的概念(这是内容出现的原始站点吗?)以及给定关联站点上给定内容的“主要”和“次要”内容状态。

我的想法是创建一个名为 ContentAssociation 的多态连接模型,但我无法让多态关联按我预期的方式运行,我想知道我是否可能把这一切都搞错了。

这是我对连接表和模型的设置:

create_table "content_associations", :force => true do |t|
  t.string   "associable_type"
  t.integer  "associable_id"
  t.integer  "site_id"
  t.boolean  "primary_eligible"
  t.boolean  "secondary_eligible"
  t.boolean  "originating_site"
  t.datetime "created_at"
  t.datetime "updated_at"
end

class ContentAssociation < ActiveRecord::Base
  belongs_to :site
  belongs_to :associable, :polymorphic => true
  belongs_to :primary_site, :class_name => "Site", :foreign_key => "site_id" 
  belongs_to :secondary_site, :class_name => "Site", :foreign_key => "site_id"
  belongs_to :originating_site, :class_name => "Site", :foreign_key => "site_id"
end

class Site < ActiveRecord::Base
  has_many :content_associations, :dependent => :destroy 
  has_many :articles, :through => :content_associations, :source => :associable, :source_type => "Article"
  has_many :events, :through => :content_associations, :source => :associable, :source_type => "Event"

  has_many :primary_articles, :through => :content_associations, 
                              :source => :associable, 
                              :source_type => "Article", 
                              :conditions => ["content_associations.primary_eligible = ?" true]

  has_many :originating_articles, :through => :content_associations, 
                                  :source => :associable, 
                                  :source_type => "Article", 
                                  :conditions => ["content_associations.originating_site = ?" true]

  has_many :secondary_articles, :through => :content_associations, 
                                :source => :associable, 
                                :source_type => "Article", 
                                :conditions => ["content_associations.secondary_eligible = ?" true]
end

class Article < ActiveRecord::Base
  has_many :content_associations, :as => :associable, :dependent => :destroy
  has_one :originating_site, :through => :content_associations, 
                             :source => :associable, 
                             :conditions => ["content_associations.originating_site = ?" true]

  has_many :primary_sites, :through => :content_associations, 
                           :source => :associable
                           :conditions => ["content_associations.primary_eligible = ?" true]

  has_many :secondary_sites, :through => :content_associations, 
                             :source => :associable
                             :conditions => ["content_associations.secondary_eligible = ?" true]                         
end

我已经尝试了上述关联声明的很多变体,但无论我做什么,我似乎都无法获得我想要的行为

@site = Site.find(2)
@article = Article.find(23)
@article.originating_site = @site
@site.originating_articles #=>[@article]

或这个

@site.primary_articles << @article
@article.primary_sites #=> [@site]

Rails 的内置多态性是否是用于影响站点及其各种内容之间的这些连接的错误机制?看起来它很有用,因为我需要以多对多的方式将多个不同的模型连接到一个通用模型,但是我很难找到以这种方式使用它的任何示例。

也许复杂性的一部分是我需要双向关联——即查看与给定文章关联的所有站点查看与给定站点关联的所有文章。我听说过插件has_many_polymorphs,看起来它可以解决我的问题。但我在这里尝试使用 Rails 3,但不确定它是否受支持。

非常感谢任何帮助——即使它只是让我更加了解我对多态性在这种情况下的使用的不完全理解。

提前致谢!

4

3 回答 3

9

如果您需要比 STI 允许的关联更具可扩展性,您可以尝试编写自己的集合助手来进行额外的类型自省。

任何时候定义与belongs_to,has_manyhas_one等的关系时,您还可以定义与该集合相关的辅助函数:

class Article < ActiveRecord::Base
  has_many :associations, :as => :associable, :dependent => :destroy
  has_many :sites, :through => :article_associations

  scope :originating_site, lambda { joins(:article_associations).where('content_associations.originating_site' => true).first }
  scope :primary_sites, lambda { joins(:article_associations).where('content_associations.primary_eligable' => true) }
  scope :secondary_sites, lambda { joins(:article_associations).where('content_associations.secondary_eligable' => true) }
end

class Site < ActiveRecord::Base
  has_many :content_associations, :as => :associable, :dependent => :destroy do
    def articles
      collect(&:associable).collect { |a| a.is_a? Article }
    end
  end
end

class ContentAssociation < ActiveRecord::Base
  belongs_to :site
  belongs_to :associable, :polymorphic => true
  belongs_to :primary_site, :class_name => "Site", :foreign_key => "site_id"
  belongs_to :secondary_site, :class_name => "Site", :foreign_key => "site_id"
  belongs_to :originating_site, :class_name => "Site", :foreign_key => "site_id"
end

如果您需要它们更干燥,您可以将这些函数 defs 移到其他地方:

module Content
  class Procs
    cattr_accessor :associations
    @@associations = lambda do
      def articles
        collect(&:associable).collect { |a| a.is_a? Article }
      end

      def events
        collect(&:associable).collect { |e| e.is_a? Event }
      end

      def bios
        collect(&:associable).collect { |b| b.is_a? Bio }
      end
    end
  end
end


class Site < ActiveRecord::Base
  has_many :content_associations, :as => :associable, :dependent => :destroy, &Content::Procs.associations
end

由于本例中的文章、事件和简历都在做同样的事情,我们可以进一步干燥:

module Content
  class Procs
    cattr_accessor :associations
    @@associations = lambda do
      %w(articles events bios).each do |type_name|
        type = eval type_name.singularize.classify
        define_method type_name do
          collect(&:associable).collect { |a| a.is_a? type }
        end
      end
    end
  end
end

现在它开始变得更像一个通用插件,而不是特定于应用程序的代码。这很好,因为您可以轻松地重复使用它。

于 2010-08-23T21:02:01.193 回答
1

只是一个镜头,但你看过多态 has_many :through => 关系吗?有一些有用的博客文章 - 试试http://blog.hasmanythrough.com/2006/4/3/polymorphic-throughhttp://www.inter-sections.net/2007/09/25/polymorphic-has_many -through-join-model/ (这里也有一个问题)。希望其中的一些帮助有点,祝你好运!

于 2010-08-17T18:15:27.993 回答
1

在这种情况下,我认为多态不是正确的方法,至少从我对您系统设计的理解来看。这是一个使用 STI 的示例。这很复杂,如果我遗漏了什么,请原谅我。我对新的 arel 语法也不是很擅长,所以不能保证不修改就可以正常工作。

class Article < ActiveRecord::Base
  has_many :article_associations, :dependent => :destroy
  has_many :sites, :through => :article_associations

  scope :originating_site, lambda { joins(:article_associations).where('content_associations.originating_site' => true).first }
  scope :primary_sites, lambda { joins(:article_associations).where('content_associations.primary_eligable' => true) }
  scope :secondary_sites, lambda { joins(:article_associations).where('content_associations.secondary_eligable' => true) }
end

class Site < ActiveRecord::Base
  has_many :content_associations, :dependent => :destroy
  has_many :article_associations
  has_many :articles, :through => :article_associations
end

class ContentAssociation < ActiveRecord::Base
  belongs_to :site
  belongs_to :primary_site, :class_name => "Site", :foreign_key => "site_id"
  belongs_to :secondary_site, :class_name => "Site", :foreign_key => "site_id"
  belongs_to :originating_site, :class_name => "Site", :foreign_key => "site_id"
end

class ArticleAssociation < ContentAssociation
  belongs_to :article
end

我在这里所做的是为每种数据类型创建一个基本关联模型和一个单独的子关联。因此,如果您需要按类型访问关联,您将可以访问,site.articles但您也可以获得site.content_assocations包含所有内容的列表。

STI 功能将需要一个type:string列来存储数据类型。除非您使用ContentAssociation模型,否则这将自动处理。由于ArticleAssociation正在使用article_id,您还需要添加它,以及子模型使用的所有其他列。

于 2010-08-17T23:01:46.293 回答