1

Ruby on Rails 3.2.13 と 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

Character モデル内で、現在のユーザーに表示されているすべての文字を取得したいと考えています。これは、次の条件によって決定されます。

  1. キャラクターが現在のユーザーに属している OR
  2. キャラクターが公開されている OR
  3. 現在のユーザーがキャラクターのユーザーとグループを共有している

結果はActiveRecord::Relation.

最初の 2 つの条件を一致させるのは簡単です。

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

3 番目の条件の場合、次のクエリは正しい結果を返しますが、おそらく最善の方法ではありません。

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::Relationさらに、両方のクエリの結果を含む を生成する 2 つの結果を連結する方法を見つけることができません(merge両方のクエリに一致する要素を生成し、 の代わりにを+返します)。ArrayActiveRecord::Relation

単一の Squeel クエリでこれを処理する方法についてのヘルプは大歓迎です。

4

1 に答える 1

0

問題を少し再構築して、has_and_belongs_to_many関連付けを関連付けに置き換えてみましょう。次のように、モデルに別の関連付けhas_many, throughを追加します。has_many, throughCharacter

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の間の関係を表したものです。基本的には、 を使用すると非表示になる結合テーブルです。私はその関係を見ることを好みます (特にそれが重要な場合)。UserGrouphas_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

  • 公開キャラ全員。
  • すべてのユーザーの文字。
  • ユーザーのグループに属するすべての文字。

そして、これらのセットから個別の文字リストのみを取得します。

Rails コンソールから:

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

行きたいところに行けると思います。

于 2013-08-13T15:39:00.513 に答える