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 のビルトイン ポリモーフィズムは、サイトとコンテンツのさまざまな部分との間のこれらの接続に影響を与えるために使用するメカニズムが間違っているのでしょうか? 複数の異なるモデルを 1 つの共通モデルに多対多で接続する必要があるため、便利なようですが、この方法で使用する例を見つけるのに苦労しました。

おそらく、複雑さの一部は、双方向の関連付けが必要なことです。つまり、特定の記事が関連付けられているすべてのサイトを表示、特定のサイトに関連付けられているすべての記事を表示します。プラグインhas_many_polymorphsについて聞いたことがありますが、問題が解決するようです。しかし、ここで Rails 3 を使用しようとしていますが、まだサポートされているかどうかはわかりません。

このコンテキストでのポリモーフィズムの使用に関する私の不完全な理解に光を当てるだけであっても、どんな助けも大歓迎です。

前もって感謝します!

4

3 に答える 3

9

アソシエーションをSTIが許可するよりも拡張可能にする必要がある場合は、追加の型のテストを行う独自のコレクションヘルパーを作成してみてください。

belongs_tohas_manyなどとの関係を定義するときはいつでもhas_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

よりドライにする必要がある場合は、これらの関数defを別の場所に移動できます。

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 に答える