2013-04-07 11 views
1

使用Ruby on Rails的3.2.13和Squeel我有以下型號:Squeel查詢語法來獲取用戶的要素共享同一組

class Group < ActiveRecord::Base 
    has_and_belongs_to_many :users 
end 

class User < ActiveRecord::Base 
    has_and_belongs_to_many :groups 
    has_many :characters, :dependent => :destroy 
end 

class Character < ActiveRecord::Base 
    belongs_to :user 
end 

字符有一個布爾屬性:public

在人物模型,我希望檢索是當前用戶可見的所有字符,由下列條件決定:

  1. 的性格屬於當前用戶或
  2. 性格公共或
  3. 當前用戶共享一組字符的用戶

結果必須是一個ActiveRecord::Relation

匹配的前兩個條件很簡單:

def self.own_or_public user_to_check 
    where{ 
    (user_id == user_to_check.id) | 
    (public) 
    } 
end 

對於第三個條件以下查詢產生正確的結果,但可能不這樣做的最佳方式:

def self.shares_group_with user_to_check 
    user_groups = Group.joins{users}.where{users.id == user_to_check.id} 
    joins{user.groups}. 
    where{ 
     user.groups.id.in(user_groups.select(id)) 
    }.uniq 
end 

此外,我找不到連接兩個結果的方法,產生包含兩個查詢結果的ActiveRecord::Relationmerge產生與兩個查詢匹配的元素,並且+返回Array而不是ActiveRecord::Relation)。

任何關於如何在單個Squeel查詢中處理這個問題的幫助,我們都非常感謝。

+0

天哪,得到了這個風滾草:沒有人知道,沒有人在乎... – McLibboc 2013-04-18 15:05:12

回答

0

讓我們嘗試重組你的問題一點,與has_many, through協會替換has_and_belongs_to_many協會,我們將增加對Character模型的另一個has_many, through協會如下:

class Membership < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :group 
end 

class Group < ActiveRecord::Base 
    has_many :memberships 
    has_many :users, through: :memberships 
end 

class User < ActiveRecord::Base 
    has_many :memberships 
    has_many :groups, through: :memberships 
    has_many :characters 
end 

class Character < ActiveRecord::Base 
    belongs_to :user 
    has_many :groups, through: :user 
end 

Membership模型是關係的表示在UserGroup之間 - 實質上是使用has_and_belongs_to_many時隱藏的連接表。我更喜歡看到這種關係(特別是如果它很重要)。

我們還有一個Character型號到Group與用戶關聯。當我們嘗試加入我們的示波器時,這很有幫助。

充實的Character模型出來,讓我們添加以下內容:

sifter :by_user do |user| 
    user_id == user.id 
end 

sifter :public do 
    public 
end 

使用篩作爲我們的基石,我們可以添加以下獲得可見字符(如你定義它):

def self.get_visible(user) 
    Character.uniq.joins{groups.outer}.where{(sift :public)|(sift :by_user, user)|(groups.id.in(user.groups))} 
end 

此方法採用的User一個實例,並發現以下Character S:

  • 所有公共字符。
  • 所有用戶的角色。
  • 屬於用戶組的所有字符。

然後我們只從這些集合中取出不同的字符列表。

從鐵軌控制檯:

irb(main):053:0> Character.get_visible(User.find(4)) 
    User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 4]] 
    Group Load (0.7ms) SELECT "groups".* FROM "groups" INNER JOIN "memberships" ON "groups"."id" = "memberships"."group_id" WHERE "memberships"."user_id" = 4 
    Character Load (0.9ms) SELECT DISTINCT "characters".* FROM "characters" LEFT OUTER JOIN "users" ON "users"."id" = "characters"."user_id" LEFT OUTER JOIN "memberships" ON "memberships"."user_id" = "users"."id" LEFT OUTER JOIN "groups" ON "groups"."id" = "memberships"."group_id" WHERE ((("characters"."public" OR "characters"."user_id" = 4) OR "groups"."id" IN (2))) 
[ 
    [0] #<Character:0x00000005a16b48> { 
       :id => 4, 
      :user_id => 4, 
       :name => "Testiculies", 
     :created_at => Tue, 13 Aug 2013 14:35:50 UTC +00:00, 
     :updated_at => Tue, 13 Aug 2013 14:35:50 UTC +00:00, 
      :public => nil 
    }, 
    [1] #<Character:0x00000005d9db40> { 
       :id => 1, 
      :user_id => 1, 
       :name => "conan", 
     :created_at => Mon, 12 Aug 2013 20:18:52 UTC +00:00, 
     :updated_at => Tue, 13 Aug 2013 12:53:42 UTC +00:00, 
      :public => true 
    } 
] 

要查找所有字符的特殊User有,添加一個實例方法的User型號:

def get_visible_characters 
    Character.get_visible(self) 
end 

我覺得要,將讓你走。

+0

非常感謝您的回答。但是有兩個疑難問題:'distinct'作用域產生一個'ActiveRecord :: StatementInvalid'錯誤,我用'joins {groups.outer}'替換'joins {groups}'來獲取自己的字符,當用戶不在組。需要更多的測試,但是如果沒有'distinct'scope,它似乎與篩選器一起工作。 – McLibboc 2013-08-15 09:07:56

+0

不同的作用域對我來說使用Postgres,但我要編輯答案並使用'uniq'來代替。那對你有用嗎?並且很好地抓住外部加入組。 – erroric 2013-08-16 18:41:47

+0

我做了一些更多的測試,並且一切正常。即使沒有'uniq'。所以再次感謝您的答覆,非常感謝。 – McLibboc 2013-08-16 19:51:25