2010-10-18 60 views
10

我正在學習來自多年c#和MSSQL的RoR。Ruby On Rails多種類型的用戶模型

我選擇了一個項目爲我的兄弟誰是租賃物業經理建立一個網站。我認爲這應該是相當容易的,因爲模型應該是直截了當的,但是它認爲我可能會過度思考一切,或者我無法放棄'舊'方式。無論如何,這是問題。我開始時只有兩個模型(用戶和財產)。屬性模型很簡單,用戶不多。我認爲我們在系統中有三種類型的用戶。租戶,業主和經理(我的兄弟將是唯一的經理,但我想我會設計它成長)他管理的財產,他們每個人都可以擁有許多財產。每個物業將有一個業主,一個租戶和一個經理。

租戶將能夠登錄,只是看到他們租用的財產,可能會填寫維護請求或類似的東西......(在這一點上沒有真正的要求,即使租戶登錄系統,但我認爲它將是一個很好的鍛鍊)

同樣的事情,所有者都沒有真正需要訪問系統(他們僱用我的兄弟,所以他們不必參與),但我認爲這可能是很好的,又一次好的練習。

我用Nifty_generator生成一個用戶,它只是給了電子郵件,密碼等我如下擴展了它...

class AddProfileDataToUsers < ActiveRecord::Migration 
    def self.up 
    add_column :users, :first_name, :string 
    add_column :users, :last_name, :string 
    add_column :users, :address1, :string 
    add_column :users, :address2, :string 
    add_column :users, :city,:string 
    add_column :users, :state, :string 
    add_column :users, :zip, :string 
    add_column :users, :phone, :string 
    add_column :users, :email, :string 
    add_column :users, :user_type, integer 
    end 

    def self.down 
    remove_column :users, :first_name 
    remove_column :users, :last_name 
    remove_column :users, :address1 
    remove_column :users, :address2 
    remove_column :users, :city 
    remove_column :users, :state 
    remove_column :users, :zip 
    remove_column :users, :phone 
    remove_column :users, :email 
    remove_column :users, :user_type 
    end 
end 

下面是創建屬性表的代碼

class CreateProperties < ActiveRecord::Migration 
    def self.up 
    create_table :properties do |t| 
     t.string :address 
     t.string :city 
     t.string :type 
     t.integer :beds 
     t.float :baths 
     t.float :price 
     t.float :deposit 
     t.string :terms 
     t.string :laundry 
     t.datetime :date_available 
     t.integer :sqft 
     t.integer :owner_id 
     t.integer :manager_id 
     t.integer :tenant_id 
     t.timestamps 
    end 
    end 

    def self.down 
    drop_table :properties 
    end 
end 

添加以下到先前由nifty_authentication發生器產生的用戶模型

class User < ActiveRecord::Base 

    #other stuff in the user model up here...... 
    validates_length_of :password, :minimum => 4, :allow_blank => true 

    #this is the stuff that I have added to the user model 
    has_many :managed_properties, :class_name => "Property", :foreign_key => "manager_id" 
    has_many :owned_properties, :class_name => "Property", :foreign_key => "owner_id" 
    has_one :rented_property, :class_name => "Property", :foreign_key => "tenant_id" 

然後我加入這屬性模型....

class Property < ActiveRecord::Base 
    belongs_to :manager, :class_name => "User" #picked up by the manager_id 
    belongs_to :owner, :class_name => "User" #picked up by the owner_id 
    belongs_to :tenant, :class_name => "User" #picked up by the tenant_id 
end 

我的問題是,這是否看起來像建模我所描述的情況的一種可接受的方式?

我應該使用單表繼承和創建租戶模型;經理模式;和所有者模型?我所看到的問題是,單個用戶可能既是經理又是所有者。這可以通過爲用戶提供角色表來解決,其中用戶具有多個角色並且角色擁有多個用戶。我還查看了一個與用戶表進行一對一匹配的配置文件表,並製作了這個多態的表格,但我並不認爲這種情況真的需要這樣做,它並沒有解決用戶可以成爲擁有者的問題和一個經理.....

這是當我開始認爲,也許我是在思考問題,並提出了你在這裏看到的。

我歡迎您可能有的任何建設性意見。請記住,我從來沒有真正在Rails中構建過任何東西,這是第一次嘗試,一週前我甚至從未在我的計算機上安裝過rails。

我不知道這是否重要,但我認爲管理員/經理將負責創建用戶。這不會是一個自我註冊類型的網站。經理在簽訂新業主時會增加新的業主,而租戶也是如此。這將更容易確定他正在創建的用戶的類型。

感謝您的任何洞察力。

回答

8

FWIW,這對我來說很好。我可能會看declarative_authorization來管理你的角色,這可能最終會有所牽扯,特別是從UI的角度來看。在這種情況下,管理擁有多個角色的用戶似乎比STI更合適,因爲如您所說,用戶既可以是經理,也可以是租戶等。

保持管理員,租戶和所有者的代碼不同的一種方法,同時允許在單個實例中使用所有三個角色,可以根據任何給定用戶的角色動態地包含表示這些角色的模塊。你仍然有一個基類的用戶,但不是使用STI,這會限制你到一個單一的繼承結構,你可以根據每個用戶擁有的角色混合模塊,這可能簡單地根據屬性屬於某個給定的屬性用戶在初始化期間

爲此,我可能創建一個用戶工廠,可以檢查用戶所發揮的各種角色,並使用相應的模塊擴展該用戶的singleton class:對租戶屬性爲tenant的用戶擴展,用戶管理模塊,可設法性能等

從declarative_authorization的角度來看,你可以聲明同樣基於像managed_properties協會是否填充role_symbols,例如:

def role_symbols 
    @roles ||= {:manager => :managed_properties, 
    :tenant => :rented_property, 
    :owner => :owned_properties}.map do |k,v| 
     k if !send(v).blank? 
    end.compact 
end 

或類似的東西。您可能需要設置角色並同時包含相應的模塊。 Ruby和Rails通過metaprogramming techniques爲您提供了多種選擇,可以動態修飾各個模型所需的功能。我的方法可能適用於您的應用程序,也可能不適合您的應用程序,但您可以通過無數其他方式來解決問題並保持代碼清潔和乾爽。

總之在我看來,你的數據模型是健全的,你的本能不使用STI來管理多個角色是正確的。它並不像我認爲你推翻了它 - 我認爲你在正確的軌道上。實際上,Rails-y是第一遍。

編輯:你知道,我想它的越多,我不知道維持經理/租戶/業主在單獨的模塊功能的好處確實是。在我以前作爲Java/C#人的化身中,我會一直關注的是所有關於SRP/IOC和完全分離的問題。但是,在Ruby和Rails中,它並不是什麼大事,因爲它是動態類型的,耦合並沒有那麼大,或者至少是靜態類型環境下的同類問題。將所有單獨的角色功能放在單個用戶模型中,而不用擔心模塊,至少目前還不行。

我在這裏籬笆,並歡迎來自其他人的意見。對我來說,與Java類/包或.NET類/程序集相反,Ruby類的好處之一是可以隨時重構,而不用擔心類,包,命名空間,dll或jar是什麼耦合到另一個等等。我不是說SRP在Ruby中不重要,完全不是。但我並不像過去那樣偏執。

編輯:保羅·拉塞爾提出了一個很好的觀點。我認爲你應該認真考慮允許多個租戶/經理/房東每個財產。在Rails中,這可以通過關係表和has_many表示:通過關聯,加上STI來描述不同類型的關係。我也認爲有必要扭轉用戶(作爲承租人)和財產之間的關係。物業可以有多個租戶,但租客不能住在一個以上的房產。 (或者他們可以......看起來不對,但是......)

也許像(這是非常快速和骯髒的,所以請原諒所有丟失的細節請):

class PropertyRole < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :property 
end 

class Managership < PropertyRole 
    # Manager functionality here 
end 

class Ownership < PropertyRole 
    # Owner functionality here 
end 

class User < ActiveRecord::Base 
    belongs_to :residence, :class_name => 'Property', :foreign_key => 'residence_id' 
    has_many :managerships 
    has_many :ownerships 

    has_many :owned_properties, :through => :ownerships, :classname => 'Property' 
    has_many :managed_properties, :through => :managerships, :classname => 'Property' 
end 

class Property < ActiveRecord::Base 
    has_many :tenants, :class_name => 'User', :foreign_key => 'residence_id' 
    has_many :managerships 
    has_many :ownerships 
    has_many :owners, :class_name => 'User', :through => :ownerships 
    has_many :managers, :class_name => 'User', :through => :managerships 
end 

就像我說的,這是快速和骯髒的,高層次的第一階段。請注意,我們現在創建了可以包含管理器和所有者特定功能的管理權和所有權角色,從而消除了是否將這些功能分散在單獨模塊中的前兩難局面。

**請注意,我也倒轉了Tenant/Property角色 - 我認爲這是對您的域進行必要的更改。顯然一個住宅可以有多個租戶。在我看來(目前)您可以在用戶模型上保留特定於租戶的功能。

+0

+1 decl_auth看起來不錯! – SingleNegationElimination 2010-10-18 05:59:31

3

讓我感到震驚的是,你現在的模型禁止多個租戶或房東在一個給定的財產,並假定這些關係是永久的。

在英國,至少有一種情況是房產屬於已婚夫婦,因此可能有多個房東(比如我的妻子和我在這個位置)。同樣,對於某些類型的出租來說,在單一房產中有多個租戶是非常普遍的。

如果你認爲我在這裏,值得考慮在用戶和屬性之間引入一個'user_property_role'模型對象。然後,您將擁有user_property_role和user_property_role的has_many關係。如果此角色具有可以設置爲的「關係類型」字段'landlord',那麼你可以使用has_many:through和named scopes(在user_property_role)對象來做例如property.users.landlords或property.users.tenants。

採取這種方法也可以讓你做這樣的事情,例如給這些關係的'開始'和'結束'日期,記錄一個事實,即一個屬性有多個租戶隨着時間的推移,或者一個屬性可能由隨着時間的推移不同人再次,您應該可以將其構建到一組命名範圍中,以便您可以執行property.users.current.tenants或甚至user.properties.current.tenants。

希望有幫助,並不只是增加你的困惑!

+0

是的,這是一個很好的觀點。用戶和屬性之間的關係很多,但不是多對一。我認爲這適用於美國和英國。 – 2010-10-18 06:20:59

+0

@Paul Russell,這可能與範圍有關嗎?我嘗試了類似的方法(用戶has_many:accounts,通過:: roles),它給出了一個缺少錯誤的方法。所以'def self.managed在角色模型中的where(:type ==「Ownership」)end'似乎不起作用!我無法調用user.accounts.managed,因爲managed是在Role模型中,而不是Account模型! – Mohamad 2012-05-04 22:57:44