1

Rails/ActiveRecordと多相のhas_and_belongs_to_many関係を作成するにはどうすればよいですか?Rails/ActiveRecordとの多形habtmの関係

私は一つだけの親に関連しているに私の多型側を制限belongs_toの関係作成伴う参照例のほとんど:

表:タスク

表:Tasks_Targets

表:CustomerStoreを

表:SoftwareSystem

CustomerStoreとSoftwareSystem両方がTYであろうこの場合、「ターゲット設定可能」となります。私が理解しているところでは、ほとんどの例が示すように多相関係を実装すると、Targetableをタスクに関連付けることしかできません。

いくつかの明確化は、ほとんどの検索はまだオンライン...

感謝を原因不明のこの関係の背後にある理論の一部を残すように役立つかもしれません!

答えて

6

あなたのドメインについてのあなたの説明を与えて、あなたの問題をどのように解決するかについて、小さなテスト駆動の例を挙げました。ドメインの不一致が見つかった場合は、さらに気をつけてください(私はacts_as_fu gemを使用して、その場でテストモデルを呼び起こしています)。あなたの懸念に関して中島の答えを補完するために

require 'acts_as_fu' 

# class Task < ActiveRecord::Base 
build_model(:tasks) do 
    integer :task_target_id 

    has_many :task_targets 
    has_many :customer_stores, :through => :task_targets, :source => :targetable, :source_type => 'CustomerStore' 
    has_many :software_systems, :through => :task_targets, :source => :targetable, :source_type => 'SoftwareSystem' 
end 

# class TaskTarget < ActiveRecord::Base 
build_model(:task_targets) do 
    string :targetable_type 
    integer :targetable_id 
    integer :task_id 

    belongs_to :targetable, :polymorphic => true 
    belongs_to :task 
end 

# class CustomerStore < ActiveRecord::Base 
build_model(:customer_stores) do 
    has_many :task_targets, :as => :targetable 
    has_many :tasks, :through => :task_targets 
end 

# class SoftwareSystem < ActiveRecord::Base 
build_model(:software_systems) do 
    has_many :task_targets, :as => :targetable 
    has_many :tasks, :through => :task_targets 
end 

require 'test/unit' 

class PolymorphicDomainTest < Test::Unit::TestCase 
    # Test that customer stores can have multiple tasks 
    def test_customer_store_gets_task 
    task = Task.create! 
    customer_store = CustomerStore.create! 
    customer_store.task_targets.create! :task => task 
    assert customer_store.tasks.include?(task) 
    end 

    def test_many_customer_stores_get_task 
    task_a = Task.create! 
    task_b = Task.create! 
    customer_store = CustomerStore.create! :tasks => [task_a, task_b] 
    assert customer_store.tasks.include?(task_a) 
    assert customer_store.tasks.include?(task_b) 
    end 

    # Test that software systems can have multiple tasks 
    def test_software_system_gets_task 
    task = Task.create! 
    software_system = SoftwareSystem.create! 
    software_system.task_targets.create! :task => task 
    assert software_system.tasks.include?(task) 
    end 

    def test_many_software_systems_get_task 
    task_a = Task.create! 
    task_b = Task.create! 
    software_system = SoftwareSystem.create! :tasks => [task_a, task_b] 
    assert software_system.tasks.include?(task_a) 
    assert software_system.tasks.include?(task_b) 
    end 

    # Test that Tasks can have multiple customer stores 
    def test_task_has_many_customer_stores 
    task = Task.create! 
    customer_store_a = CustomerStore.create! 
    customer_store_b = CustomerStore.create! 
    task.customer_stores = [customer_store_a, customer_store_b] 
    task.save! 
    task.reload 
    assert task.customer_stores.include?(customer_store_a) 
    assert task.customer_stores.include?(customer_store_b) 
    end 

    # Test that Tasks can have multiple software systems 
    def test_task_has_many_software_systems 
    task = Task.create! 
    software_system_a = SoftwareSystem.create! 
    software_system_b = SoftwareSystem.create! 
    task.software_systems = [software_system_a, software_system_b] 
    task.save! 
    task.reload 
    assert task.software_systems.include?(software_system_a) 
    assert task.software_systems.include?(software_system_b) 
    end 
end 
+0

これはかなり良いようです!私の唯一の関心事は、「タスク」でそれぞれの「ターゲット設定可能」を定義することが固まっていることです。これを回避する方法はありますか? Task.targets/targetablesはどうですか? –

+0

私は今日のほとんどのためにこれを悩ましてきましたが、私はまだ私が持っているものにかなり満足していません... "has_many:customer_stores、:through =>:task_targets、:source =>: "SoftwareSystem" " 多態性の関係の目的を一括して打ち消すのはどれですか?それをセットアップする方法はありますか?Targetable.tasksを使えるように、Task.targetを通してすべてのターゲット設定を取得できますか? –

-1

は、ここで私はそれを行うだろう方法は次のとおりです。

class Task < ActiveRecord::Base 
    def targets 
    # Get Array of all targetables 
    tt = TaskTarget.select_all("SELECT targetable_type, targetable_id FROM task_targerts WHERE task_id = #{self[:id]}") 

    # Build Hash of targetable_type => Array of targetable_ids 
    targetables = Hash.new { |hash, key| hash[key] = [] } 
    tt.each do |targetable| 
     targetables[targetable.targetable_type] << targetable.targetable_id 
    end 

    # Query each "targetable" table once and merge all results 
    targetables.keys.map{|key| (eval key).find(targetables[key])}.flatten 
    end 
end 

は、テーブルtask_targetsのインデックスtask_idに確認してください。