我已经选择了一个项目来为我的一个出租物业经理的兄弟建立一个网站.我认为这应该是相当容易的,因为模型应该是直截了当的,但它认为我可能会考虑一切,或者我有麻烦放弃“老”的方式.无论如何,这里是问题.我只用两个模型(用户和财产)开始.物业模式很简单,用户不是那么多.我认为我们在系统中有三种类型的用户.租户,业主和经理(我的兄弟将是唯一的经理,但我认为我会设计成长)他管理的房地产几个业主,每个拥有许多财产.每个物业都有一个业主,一个租户和一个经营者.
租户将能够登录,只是看到他们出租的财产,可能填写一个维护请求或类似的东西…(在这一点上没有真正的要求甚至给租户登录系统,但我认为这将是一个很好的行使)
同样的事情对业主来说,没有一个真正需要访问系统(他们雇用我的兄弟,所以他们不必参与),但我认为这可能是一个很好的,而且是一个很好的锻炼.
我使用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,:address2,:city,:state,:zip,:phone,:email,: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,:foreign_key => "owner_id" has_one :rented_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中建立任何东西,这是第一次尝试,一周前我从来没有在我的电脑上安装导轨.
我不知道这是否重要,但我认为管理员/经理将负责创建用户.这不会是一个自我注册类型的网站.经理会在新主人注册时增加新的业主,同样的租户也可以.这将使得更容易确定他正在创建的用户的类型.
感谢您有任何见解.
解决方法
保持管理者,租户和所有者分开的代码,同时允许在单个实例中使用所有三个角色的一种方法可能是基于任何给定用户具有的角色来动态地包含代表这些角色的模块.你仍然有一个基类User,但不是使用STI,这将限制一个单一的继承结构,你可以根据每个用户拥有的角色来混合模块,这可能只是把属性属于给定的用户在初始化期间.
为此,我可以创建一个用户工厂,可以检查用户的各种角色,并使用相应的模块扩展该用户的singleton class:使用具有租户属性的用户的租户模块进行扩展,并向用户扩展Manager模块管理属性等
从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中,它并不是一个很大的交易,因为它是动态类型的,耦合并不像在静态类型的环境中那么大,或者至少是同样的.您可能完全可以将所有单个角色功能放在单个用户模型中,而不用担心模块,至少还没有.
我在这里的围栏,欢迎他人的来信.对于我来说,Ruby类的好处之一,与Java类/包或.NET类/组件相反,您可以根据需要随时重构,而不用担心哪个类,包,命名空间,dll或jar是加上另一个等等,我不是说SRP在Ruby中不重要,根本不是.但我不像以前那样像这样偏执.
编辑:保罗·罗素(Paul Russell)表现出色.我认为你应该认真考虑允许每个房产多个租户/经理/业主.在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,:through => :ownerships has_many :managers,:through => :managerships end
就像我说的那样,这是一个快速和肮脏的高级别的第一通.请注意,我们现在已经创建了可以包含管理器和所有者特定功能的管理权限和所有权角色,从而消除了在单独的模块中是否将功能区分开的困境.
**还要注意,我已经颠倒了租户/房地产角色 – 我认为这是您域名的必要变更.显然,一个住宅可以有多个租户.在我看来(目前),您可以在用户模型中保留特定于租户的功能.