2010-02-18 128 views
1
class User < ActiveRecord::Base 
    has_one :location, :dependent => :destroy, :as => :locatable 
    has_one :ideal_location, :dependent => :destroy, :as => :locatable 
    has_one :birthplace, :dependent => :destroy, :as => :locatable 
end 

class Location < ActiveRecord::Base 
    belongs_to :locatable, :polymorphic => true 
end 

class IdealLocation < ActiveRecord::Base 
end 

class Birthplace < ActiveRecord::Base 
end 

我看不出有任何理由在這種情況下有子類。位置對象的行爲是相同的,它們唯一的一點是使關聯變得容易。我還希望將數據存儲爲int而不是字符串,因爲它可以使數據庫索引更小。在軌道上避免STI

我想象像下面這樣,但我不能完成的想法:

class User < ActiveRecord::Base 
    LOCATION_TYPES = { :location => 1, :ideal_location => 2, :birthplace => 3 } 

    has_one :location, :conditions => ["type = ?", LOCATION_TYPES[:location]], :dependent => :destroy, :as => :locatable 
    has_one :ideal_location, :conditions => ["type = ?", LOCATION_TYPES[:ideal_location]], :dependent => :destroy, :as => :locatable 
    has_one :birthplace, :conditions => ["type = ?", LOCATION_TYPES[:birthplace]], :dependent => :destroy, :as => :locatable 
end 

class Location < ActiveRecord::Base 
    belongs_to :locatable, :polymorphic => true 
end 

這段代碼的下面失敗,基本上使其失去作用:

user = User.first 
location = user.build_location 
location.city = "Cincinnati" 
location.state = "Ohio" 
location.save! 

location.type # => nil 

這是顯而易見的,因爲沒有辦法將has_one聲明中的條件選項轉換爲等於1的類型。

我可以在視圖中的任何位置嵌入id這些字段出現,但這似乎也是錯誤的:

<%= f.hidden_field :type, LOCATION_TYPES[:location] %> 

有什麼辦法來避免額外的子類或使LOCATION_TYPES方法的工作?

在我們的特殊情況下,應用程序是非常位置感知的,對象可以有多種不同類型的位置。我只是很奇怪,不想要所有這些子類?

任何建議,你有讚賞,告訴我我瘋了,如果你想,但你想看看10 +不同的位置模型漂浮在應用程序/模型?

回答

1

據我所見,位置是位置是位置。您所指的不同「子類」(IdealLocation,Birthplace)似乎只是描述了位置與特定用戶的關係。如果我弄錯那部分,就阻止我。

知道了,我可以看到兩個解決方案。

首先是將位置視爲價值對象而不是實體。 (更多關於條款:Value vs Entity objects (Domain Driven Design))。在上面的例子中,您似乎將位置設置爲「俄亥俄州辛辛那提」,而不是從數據庫中找到「辛辛那提,俄亥俄州」對象。在那種情況下,如果辛辛那提有許多不同的用戶,那麼儘管只有一個辛辛那提,俄亥俄州,你的數據庫中只有同樣多的「辛辛那提,俄亥俄州」位置。對我而言,這是一個明確的信號,表明您正在使用值對象,而不是實體。

該解決方案將如何看待?有可能使用一個簡單的定位對象是這樣的:

class Location 
    attr_accessor :city, :state 

    def initialize(options={}) 
    @city = options[:city] 
    @state = options[:state] 
    end 
end 

class User < ActiveRecord::Base 
    serialize :location 
    serialize :ideal_location 
    serialize :birthplace 
end 

@user.ideal_location = Location.new(:city => "Cincinnati", :state => "OH") 
@user.birthplace = Location.new(:city => "Detroit", :state => "MI") 
@user.save! 

@user.ideal_location.state # => "OH" 

其他解決方案,我可以看到的是使用您現有的位置ActiveRecord的模型,只是使用與用戶的關係,定義關係「型」,像這樣:

class User < ActiveRecord::Base 
    belongs_to :location, :dependent => :destroy 
    belongs_to :ideal_location, :class_name => "Location", :dependent => :destroy 
    belongs_to :birthplace, :class_name => "Location", :dependent => :destroy 
end 

class Location < ActiveRecord::Base 
end 

您需要做的工作是在用戶模型中包含location_id,ideal_location_id和birthplace_id屬性。

+0

您是對的,子類僅描述位置關係給用戶。 感謝您指向有關值對象與實體的文章。你是對的,用我現在的解決方案,我將最終複製位置表中的很多行。 不幸的是,價值對象的情況將不會解決,因爲位置模型實際上有更多的行爲,需要通過SQL訪問。 您的belongs_to方法似乎在頭部受到了攻擊,我不確定爲什麼我首先通過使用has_one來避免它。 – 2010-02-19 14:06:33

0

嘗試增加before_save鉤

class Location 
    def before_save 
    self.type = 1 
    end 
end 

,同樣對於其他類型的位置

+0

,我不希望有其他類型的位置是點。感謝您閱讀我發佈的內容。 – 2010-02-18 13:56:26

+0

我試圖建議如何讓你失敗的例子(與location.type =>零)的工作。 – dan 2010-02-18 16:08:03

0

可以使用模塊封裝定位對象的行爲,並使用一些宏創建的關係:

has_one <location_class>,: conditions => [ "type =?" LOCATION_TYPES [: location]],: dependent =>: destroy,: as =>: locatable 

你可以在你的模塊中使用類似這樣的東西:

module Orders 
    def self.included(base) 
    base.extend(ClassMethods) 
    end 

    module ClassMethods 
    def some_class_method(param) 
    end 

    def some_other_class_method(param) 
    end 

    module InstanceMethods 
     def some_instance_method 
     end 
    end 
    end 
end 

Rails guides: add-an-acts-as-method-to-active-record

+0

模塊的一些要點示例: Without acts_as:http://gist.github.com/307290 With acts_as:http://gist.github.com/307296 – nanda 2010-02-18 03:15:22

2

爲什麼不使用named_scopes?

喜歡的東西:

class User 
    has_many :locations 
end 

class Location 
    named_scope :ideal, :conditions => "type = 'ideal'" 
    named_scope :birthplace, :conditions => "type = 'birthplace" # or whatever 
end 

然後在你的代碼:

user.locations.ideal => # list of ideal locations 
user.locations.birthplace => # list of birthplace locations 

你最好還是要處理的設置上創造的類型,我想。

0

也許我失去了一些重要的東西在這裏,但我想你能說出你的關係是這樣的:

class User < ActiveRecord::Base 

    has_one :location, :dependent => :destroy 
    #ideal_location_id 
    has_one :ideal_location, :class_name => "Location", :dependent => :destroy 
    #birthplace_id 
    has_one :birthplace, :class_name => "Location", :dependent => :destroy 

end 

class Location < ActiveRecord::Base 
    belongs_to :user # user_id 
end 
+0

從Rails API:has_one指定與另一個類的一對一關聯。只有在其他類包含外鍵時才應使用此方法。如果當前類包含外鍵,則應該使用belongs_to。 這樣的事情可能會使用belongs_to來代替,chrisdinn只是發佈了類似的東西,但與belongs_to,我將不得不評估他的職位。謝謝。 – 2010-02-19 13:59:05