2011-10-13 14 views
9

Estoy trabajando en una aplicación que tiene los modelos User y Project y User pueden asignarse a múltiples Project s, a través de ProjectUser, con un papel (por ejemplo, desarrollador, diseñador)Rails 3 has_many: + a través de condiciones de unión de mesa/determinación del alcance

Project 
    has_many :project_users 
    has_many :users, :through => :project_users 

User 
    has_many :project_users 
    has_many :projects, :through => :project_users 

ProjectUser (user_id, project_id, role) 
    belongs_to :user 
    belongs_to :project 

puedo llamar @project.users y @user.projects, pero ya que hay variación de papeles, me gustaría ser un poco más específico con las relaciones. Idealmente, quiero ser capaz de hacer lo siguiente:

@project.developers 
    # returns @project.users, but only where ProjectUser.role = 'Developer' 

@project.designers << @user 
    # creates a ProjectUser for @project, @user with role 'Designer' 

@user.development_projects 
    # returns projects where @user is assigned as a 'Developer' 

@user.design_projects << @project 
    # creates a ProjectUser for @project, @user with role 'Designer' 

que actualmente tienen el siguiente código:

has_many :developers, :through => :project_users, :source => :user, 
         :class_name => "User", 
         :conditions => ['project_users.role = ?','Developer'] 

Pero esto sólo es realmente encargado de recuperar de una sola vía, y no me da mucho más: no puedo construir ni asignar ni nada.

Estoy intentando algo de lógica más compleja que creo que podría funcionar, pero agradecería algunos consejos:

has_many :developer_assignments, :source => :project_user, 
           :conditions => { :role => 'Developer' } 
has_many :developers, :through => :developer_assignments # class_name? 

¿Alguna sugerencia? ¡Gracias!

Respuesta

1

Parece que lo que estás buscando es una combinación de RoR single table inheritance y named scopes.

Eche un vistazo a los siguientes article para obtener un buen ejemplo de las asociaciones polimórficas. Esto debería ayudar con la consecución de los siguientes:

@project.developers 
    # returns @project.users, but only where ProjectUser.role = 'Developer' 

@project.designers << @user 
    # creates a ProjectUser for @project, @user with role 'Designer' 

Scopes le dará una forma limpia para implementar @user.development_projects pero puede haber más trucos necesarios para obtener el operador <<.

12

has_many acepta un bloque que puede definir/anular métodos para la asociación. Esto le permitirá crear un método personalizado para <<. He creado un pequeño ejemplo para ti, puedes crear compilación de una manera similar.

# Project.rb 
has_many :developers, :through => :project_users, :source => :user, 
     :conditions => "project_users.role = 'developer'" do 
     def <<(developer) 
      proxy_owner.project_users.create(:role => 'developer', :user => developer) 
     end 
     end 

Ahora puede agregar un nuevo desarrollador para su proyecto con: @project.developers << @user conforme a lo solicitado. @project.developers le ofrece a todos los desarrolladores.

Si tiene muchas funciones, podría ser útil crear estas declaraciones has_many dinámicamente.

# Project.rb 
ROLES = ['developer','contractor'] 

ROLES.each do |role|   
    self.class_eval <<-eos 
    has_many :#{role.downcase}s, :through => :project_users, :source => :user, 
      :conditions => "project_users.role = '#{role}'" do 
      def <<(user) 
       proxy_owner.project_users.create(:role => '#{role}', :user => user) 
      end 
      end 
    eos 
end 

Mirando hacia atrás en todo lo anterior no parece como los rieles de manera de hacer las cosas. El alcance debería permitir la creación y la creación de comandos sin redefinir todo.

Espero que esto ayude!

+0

Gracias por su respuesta. Resuelve mi problema, pero no lo hace de la manera en que yo esperaba, mediante el uso de ámbitos en el modelo 'ProjectUsers'. Declarar ': conditions =>" project_users.role = '# {role}' "' no parece muy complicado, ya que me hubiera gustado llamar algo como ': conditions => {: scope =>: developer}' . Todavía estoy seguro de que esto es posible de alguna manera. De cualquier forma, te otorgaré la recompensa por tu esfuerzo, aunque esta respuesta no estará marcada como correcta. ¡Gracias por tu contribución! – Jeriko

+0

Gracias. Realmente esperaba 'has_many: designers,: through =>: project_users,: source =>: user,: conditions => {: project_users => {: role =>: designer}}' trabajar de forma automática, pero aparentemente anidado hashes no tienen alcance con compilar y crear. Esto fue lo mejor que se me ocurrió. – HectorMalot

+0

¿Qué es 'proxy_owner' en el de arriba? ¿Es esa referencia al otro lado de la assiociation, similar a 'proxy_association' que obtienes con': extend'? – sbeam

0

¿Ha intentado utilizar scopes todavía? No le permite hacer < <. Pero simplifica las consultas.

Probar:

Project 
    scope :developers, lambda { 
    includes(:project_users).where("project_users.role = ?", "developer") 
    } 

Usted será capaz de obtener todos los desarrolladores que utilizan: @project.developers

Cuestiones relacionadas