2011-10-13 31 views
9

tôi đang làm việc trên một ứng dụng mà có các mô hình UserProject, và User có thể được gán cho nhiều Project s, qua ProjectUser, với một vai trò quan (ví dụ như nhà phát triển, thiết kế).Rails 3 has_many: thông qua + điều kiện tham gia bảng/Phạm vi

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 

tôi có thể gọi @project.users@user.projects, nhưng kể từ khi có vai trò khác nhau, tôi muốn cụ thể hơn một chút với các mối quan hệ. Lý tưởng nhất, tôi muốn để có thể làm như sau:

@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' 

Tôi hiện đang có đoạn mã sau:

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

Nhưng điều này chỉ thực sự hiện lấy một chiều, và không cho tôi nhiều thứ khác - tôi không thể xây dựng hay phân công hay gì cả.

Tôi đang cố gắng một số logic phức tạp hơn mà tôi nghĩ có thể làm việc, nhưng sẽ đánh giá cao một số gợi ý:

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

Bất kỳ lời đề nghị? Cảm ơn!

Trả lời

1

Có vẻ như những gì bạn đang tìm kiếm là sự kết hợp giữa số single table inheritancenamed scopes của RoR.

Hãy xem article sau đây để có ví dụ điển hình về các liên kết đa hình. Điều này sẽ giúp cho bạn đạt được những điều sau đây:

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

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

Scopes sẽ cung cấp cho bạn một cách sạch để thực hiện @user.development_projects nhưng có thể có nhiều thủ đoạn gian trá cần thiết để có được các nhà điều hành <<.

12

has_many chấp nhận một khối có thể xác định/ghi đè các phương thức cho liên kết. Điều này sẽ cho phép bạn tạo phương thức tùy chỉnh cho <<. Tôi đã tạo một ví dụ nhỏ cho bạn, bạn có thể tạo bản dựng theo cách tương tự.

# 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 

Bây giờ bạn có thể thêm nhà phát triển mới vào dự án của mình với: @project.developers << @user theo yêu cầu. @project.developers cung cấp cho bạn tất cả các nhà phát triển.

Nếu bạn có nhiều vai trò, có thể hữu ích khi tạo các câu lệnh has_many động này.

# 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 

Nhìn lại tất cả mọi thứ ở trên nó không có vẻ như đường ray cách làm việc. Phạm vi này sẽ làm cho nó có thể để có được xây dựng và tạo ra các lệnh làm việc mà không cần xác định lại tất cả mọi thứ.

Hy vọng điều này sẽ hữu ích!

+0

Cám ơn câu trả lời của bạn. Nó giải quyết vấn đề của tôi, nhưng không làm như vậy theo cách tôi đã hi vọng - bằng cách sử dụng phạm vi trên mô hình 'ProjectUsers'. Khai báo ': conditions =>" project_users.role = '# {role}' "' không có vẻ rất railsy, ​​vì tôi có thể thích gọi một cái gì đó như ': conditions => {: scope =>: developer}' . Tôi vẫn chắc chắn điều này là có thể bằng cách nào đó. Dù bằng cách nào, tôi sẽ trao cho bạn tiền thưởng cho nỗ lực của bạn, mặc dù câu trả lời này sẽ không được đánh dấu là chính xác. Cảm ơn vì đầu vào của bạn! – Jeriko

+0

Cảm ơn. Tôi đã thực sự mong đợi 'has_many: designer,: through =>: project_users,: source =>: user,: conditions => {: project_users => {: role =>: designer}}' để làm việc tự động, nhưng rõ ràng là điều kiện lồng nhau băm không được xây dựng và tạo ra. Đây là điều tốt nhất tôi có thể nghĩ ra. – HectorMalot

+0

'proxy_owner' ở trên là gì? là tham chiếu đến phía bên kia của phân phối, tương tự như 'proxy_association' mà bạn nhận được với': extend'? – sbeam

0

Bạn đã thử sử dụng scopes chưa? Nó không cho phép bạn làm < <. Nhưng nó đơn giản hóa việc truy vấn.

Hãy thử:

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

Bạn sẽ có thể nhận được tất cả các nhà phát triển sử dụng: @project.developers

Các vấn đề liên quan