1

Как бы я начал создавать полиморфные отношения has_and_belongs_to_many с Rails/ActiveRecord?Полиморфные отношения habtm с Rails/ActiveRecord

Большинство примеров я вижу, включает создание отношений belongs_to, которая ограничивает мою полиморфную сторону, чтобы быть связаны только с одним из родителей:

Таблица: Задача

Таблицы: Tasks_Targets

Таблица: CustomerStore

Таблица: SoftwareSystem

Оба CustomerStore и SoftwareSystem были бы ти pe «Целевое» в этом случае. Из того, что я понимаю, если я реализую полиморфные отношения, как показывает большинство примеров, я могу только связать 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 =>: targetable,: source_type => 'CustomerStore' has_many: software_systems,: through =>: task_targets,: source =>: targetable,: source_type => 'SoftwareSystem' " Который ко мне поражает цель полиморфных отношений. Есть ли способ настроить его, чтобы я мог получить все целевые объекты через Task.target AS WELL как возможность для Targetable.tasks? –

-1

В дополнение ответ Nakajima с относительно вашей заботы, вот как я бы это сделать:

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_id в таблице task_targets.

 Смежные вопросы

  • Нет связанных вопросов^_^