2010-10-18 35 views
10

Estoy aprendiendo RoR viniendo de muchos años de C# y MSSQL.Ruby On Rails Modelo de usuario para múltiples tipos

Escogí un proyecto para construir un sitio web para mi hermano, que es administrador de la propiedad de alquiler. Pensé que esto debería ser bastante fácil ya que las modelos deberían ser sencillas, pero creo que podría estar pensando demasiado o estoy teniendo problemas para dejar de lado el "viejo" camino. De todos modos aquí está el problema. Estoy empezando con solo dos modelos (Usuario y Propiedad). El modelo de propiedad es fácil, el usuario no tanto. Pensé que tenemos tres tipos de usuarios en el sistema. Inquilinos, propietarios y gerentes (mi hermano será el único administrador, pero pensé que lo diseñaría para que creciera) Gestiona propiedades para varios propietarios, cada uno de los cuales puede poseer muchas propiedades. Cada propiedad tendrá un propietario, un inquilino y un gerente.

Los inquilinos podrán iniciar sesión y simplemente ver la propiedad que alquilan para completar una solicitud de mantenimiento o algo por el estilo ... (en este momento no es necesario siquiera dar al inquilino acceso al sistema pero creo que sería un buen ejercicio)

Lo mismo ocurre con los propietarios, ninguno de ellos realmente necesita acceso al sistema (contratan a mi hermano para que no tengan que participar) pero pensé que podría ser bueno y de nuevo una buen ejercicio.

He utilizado el Nifty_generator para generar un usuario, que apenas da de correo electrónico, contraseña, etc. He ampliado de la siguiente manera ...

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 

Este es el código para crear la tabla Propiedades

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 

Agregué lo siguiente al modelo de usuario que fue generado por el generador 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" 

Luego añade esto al modelo de propiedad ....

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 

Mi pregunta es, ¿Esto parece una forma aceptable de modelar la situación que he descrito?

¿Debo utilizar la herencia de tabla única y crear un modelo de inquilino? un modelo de gerente; y un modelo de propietario? El problema que vi al hacer eso fue que un solo usuario podría ser tanto un gerente como un propietario. Esto podría resolverse teniendo en ese momento una tabla de roles para el usuario donde un usuario tenga muchos roles y un rol que tenga muchos usuarios. También miré una tabla de perfil con una coincidencia uno a uno con la tabla de usuarios y la hice polimórfica, pero no pensé que esta situación realmente lo requiera y no resolvió el problema de que un usuario puede ser propietario. y un gerente .....

Esto es cuando empecé a pensar que tal vez estaba pensando demasiado en el problema y se me ocurrió lo que ves aquí.

Acepto cualquier comentario constructivo que pueda tener. Tenga en cuenta que nunca construí nada en Rails y esto es todo un primer intento, hace una semana ni siquiera instalé los rieles en mi computadora.

No sé si esto importa pero pensé que el administrador/administrador sería responsable de crear usuarios. Este no será un tipo de sitio de autoinscripción. El gerente agregará nuevos propietarios cuando firme un nuevo propietario, y lo mismo aplicará a los inquilinos. Esto hará que sea más fácil determinar el tipo de usuario que está creando.

Gracias por cualquier idea que pueda tener.

Respuesta

8

FWIW, esto se ve bien para mí. Podría consultar declarative_authorization para administrar sus roles, lo que podría terminar involucrado, particularmente desde el punto de vista de la interfaz de usuario. Administrar usuarios con múltiples roles parece más apropiado que STI en esta situación, porque, como dices, un usuario puede ser administrador e inquilino, etc.

Un enfoque para mantener el código para el administrador, inquilino y propietario por separado, mientras permitiendo el uso de los tres roles en una sola instancia, podría ser incluir módulos dinámicamente que representen esos roles en función de los roles que tenga cualquier usuario. Aún tendría una clase base de Usuario, pero en lugar de usar STI, lo que lo limitaría a una única estructura de herencia, podría mezclar módulos según las funciones que tiene cada usuario, lo que podría simplemente pivotar en qué propiedades pertenecen a un determinado usuario durante la inicialización.

Para esto podría crear una fábrica de usuario que pueda inspeccionar al usuario para las diversas funciones que desempeña y ampliar el singleton class de ese usuario con el módulo correspondiente: extender con el módulo inquilino para usuarios que tienen propiedades de inquilino, ampliar con el módulo gestor para los usuarios que han conseguido propiedades, etc.

Desde el punto de vista de declarative_authorization, usted podría declarar los role_symbols basa igualmente en si asociaciones como managed_properties fueron pobladas, por ejemplo:

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 

o algo similar. Probablemente quiera establecer los roles Y incluir el módulo correspondiente al mismo tiempo. Ruby and Rails le ofrece muchas opciones a través del metaprogramming techniques para decorar dinámicamente con la funcionalidad que necesitan los modelos individuales. Mi enfoque puede o no ser apropiado para su aplicación, pero hay muchas otras maneras en que puede abordar el problema y mantener su código limpio y SECO.

En general, me parece que su modelo de datos es sólido, y su instinto de no utilizar STI para gestionar múltiples roles es correcto. No me parece que lo haya pensado demasiado, creo que está en el camino correcto. En realidad, es un bonito Rails-y para un primer pase.

EDITAR: Sabes, cuanto más lo pienso, no estoy seguro de cuál es realmente el beneficio de mantener la funcionalidad del administrador/inquilino/propietario en módulos separados. En mi anterior encarnación como chico de Java/C# me habría interesado por el SRP/IOC y la separación total de las preocupaciones. Pero en Ruby and Rails no es tan importante, ya que está tipado dinámicamente y el acoplamiento no es tan grande, o al menos del mismo tipo, preocupante que se trata de entornos de tipo estático. Puede estar perfectamente bien simplemente poniendo todas las funcionalidades de roles individuales en el modelo de Usuario único y no preocuparse con los módulos, al menos no todavía.

Estoy fuera de peligro aquí, y agradecería la opinión de los demás. Para mí, uno de los beneficios de las clases de Ruby, a diferencia de las clases/paquetes de Java o las clases/ensamblajes de .NET, es que siempre puede refactorizar según sea necesario y no preocuparse por qué clase, paquete, espacio de nombres, dll o jar es acoplado a otro, etc. No digo que SRP no sea importante en Ruby, en absoluto. Pero no estoy tan paranoico sobre eso como solía ser.

EDIT: Paul Russell hace un excelente punto. Creo que deberías considerar seriamente permitir que varios inquilinos/administradores/propietarios por propiedad. En Rails esto podría expresarse a través de una tabla relacional y un has_many: a través de la asociación, más STI para describir los diferentes tipos de relaciones. También creo que será necesario invertir la relación entre el Usuario (como Inquilino) y la Propiedad. Una propiedad puede tener más de un inquilino, pero un inquilino no puede vivir en más de una propiedad. (¿O quizás pueden? No parece correcto, pero ...)

Tal vez algo así como (esto es muy rápido y sucio, así que perdona cualquier llamada perdida detalles, por favor):

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 

Como dije, esto es rápido y sucio, un primer paso alto nivel. Tenga en cuenta que ahora hemos creado roles de Gestión y Propiedad que pueden contener la funcionalidad específica del Administrador y del Propietario, eliminando el dilema anterior en cuanto a si almacenar esa funcionalidad en módulos separados.

** Tenga en cuenta también que he invertido el rol de Tenant/Property también, creo que esto fue un cambio necesario en su dominio. Obviamente, una residencia puede tener más de un inquilino. Me parece (por el momento) que puede mantener la funcionalidad específica del inquilino en el modelo de Usuario.

+0

+1 decl_auth se ve bien! – SingleNegationElimination

3

Lo que me sorprende es que su modelo actual prohíbe tener varios inquilinos o propietarios en una propiedad determinada, y supone que estas relaciones son permanentes.

En el Reino Unido al menos, es muy común tener la situación en que una propiedad es propiedad de una pareja casada, y por lo tanto podría tener varios propietarios (mi esposa y yo estamos en este puesto, por ejemplo). Del mismo modo, para ciertos tipos de alquiler, es muy común que haya varios inquilinos en una sola propiedad.

Si crees que estoy aquí, vale la pena considerar la introducción de un objeto modelo 'user_property_role' entre el usuario y la propiedad. Entonces, tendrías una relación has_many desde el usuario y la propiedad hasta user_property_role. Si esta función tiene un campo de "tipo de relación" que puede configurar, p. 'propietario', podría utilizar has_many: through y el ámbito nombrado (en el objeto user_property_role) para hacer, p. property.users.landlords o property.users.tenants.

Adoptar este enfoque también le permitiría hacer cosas como dar fechas de "inicio" y "fin" de estas relaciones, registrando el hecho de que una propiedad tiene varios inquilinos a lo largo del tiempo, por ejemplo, o que una propiedad puede ser administrada por personas diferentes a lo largo del tiempo De nuevo, debería poder compilar esto en un conjunto de ámbitos con nombre para que pueda hacerlo, p. property.users.current.tenants o incluso user.properties.current.tenants.

Espero que ayude, y no solo añada confusión.

+0

Sí, este es un gran punto. La relación entre los usuarios y las propiedades es de varios a varios, no muchos a uno. Creo que esto es válido tanto para EE. UU. Como para el Reino Unido. –

+0

@Paul Russell, ¿eso es posible con los alcances? Intenté algo similar (User has_many: accounts, through:: roles) que da un error de método faltante. Entonces 'def self.managed where (: type ==" Ownership ") end' en el modelo de Role no parece funcionar! No puedo llamar a user.accounts.managed porque Manage está en el modelo de rol, no en el modelo de cuenta. – Mohamad