2016-01-11 18 views
0

所以我使用rails和CanCanCan一起使用。 我有即有機會獲得0或更多的項目 每個項目都有或多個子項目的用戶。 這兩個項目和子項目都有經理 如果您是項目經理,則可以查看其所有子項目 如果您是子項目的經理,還可以看到項目(但不是任何其他項目中的其他子項目)在CanCanCan中加載accessible_by作用域

class Project < ActiveRecord::Base 
     has_many :sub_projects 
     has_many :ordered_sub_projects, ->() { order('name') } 
     has_many :project_managers 
end 

class ProjectManagers < ActiveRecord::Base 
     belongs_to :user 
     belongs_to :sub_project 
end 

class Subproject < ActiveRecord::Base 
     belongs_to :project 
     has_many :subproject_managers 
end 

class SubProjectManagers < ActiveRecord::Base 
     belongs_to :user 
     belongs_to :sub_project 
end 

class Ability 
     def initialize(user) 
      can? :read, Project, { managers: user.id } 
      can? :read, Project, { sub_projects: { subproject_managers: user.id } } # To allow to see projects if you are manager of subproject 

      can? :read, SubProject, { subproject_managers: user.id } 
      can? :read, SubProject, { project: { project_managers: user.id } } 
     end 
end 

現在我要顯示所有項目給定用戶,我可以做:(使用

Projects.all.accessible_by(ability) 

但我的概述頁面上我也想快速鏈接添加到所有(允許)子項目引導下拉菜單)。 原來我用下面的等效在我看來代碼:

Projects.all.accessible_by(ability).order(:name).each do |project| 
    project.sub_projects.accessible_by(ability).order(:name).each do |sub| 
    add_link sub.name, sub 
    end 
end 

但顯示許多項目時,這造成了巨大的N + 1個問題。 [1] 所以現在我改變了。

Projects.all.accessible_by(ability). 
      preload(:ordered_subprojects).order(:name).each do |project| 
     project.ordered_subprojects.each do |sub| 
     add_link sub.name, sub if can?(:read, sub) 
     end 
end 

這是更快但不太乾淨。我還懷疑可以嗎?在這種情況下,方法也相對較慢。

理想情況下,將有可能與eagerload加載正確的accessible_by相關的has_many。不幸的是我沒有看到這樣做的方法

事實上,它更糟糕,因爲每個子項目也有團隊在其中與子項目類似的訪問semantas(每個團隊有一個或多個mangerrs,如果你可以看到團隊,你可以看到子項目和項目)。快速鏈接到團隊也添加到項目索引頁面上。

回答

0

您可以使用類似於預先加載裏面做技術 - 通過project_id一次獲取所有可訪問的子項目,然後將它們分組。

像這樣:

subprojects_hash = Subproject.accessible_by(ability).group_by(&:project_id) 
Projects.all.accessible_by(ability).each do |project| 
    # do smth with project 
    if subprojects_hash[project.id] 
    # and subprojects... 
    end 
end