blog.kotamiyake.me

為せば成る、為さねば成らぬ何事も

中間テーブルに権限属性を持たせて、権限をコントロールするということがよくあります。以下のコードはシンプルな構成例です。

class User < ApplicationRecord
  has_many :memberships, dependent: :destroy
  has_many :accounts, through: :memberships
end

class Account < ApplicationRecord
  has_many :memberships, dependent: :destroy
  has_many :users, through: :memberships
end

# user_id :integer
# account_id :integer
# admin :boolean
class Membership < ApplicationRecord
  belongs_to :user
  belongs_to :account
end

そんな時、今ままでは以下のようにチェックしていました。

class User < ApplicationRecord
  def admin?(account)
    memberships.find_by(account_id: account.id).admin
  end
end

user = User.first
account = user.accounts.first
user.admin?(account)

これだとわざわざ権限をチェックするために、毎回中間テーブルを参照する必要があります。これでは権限チェックのたびにクエリが走って効率が悪いことこの上ありません。

そこで関連テーブルの取得と同時に中間テーブルの権限属性を引っ張ってくることはできないかと考えました。

考えた結果、何ということはない、単純にselect文の中に中間テーブルの属性を設定してあげれば関連先のテーブルで当該属性を取得できました。

accounts = current_user.accounts.select("accounts.*, memberships.admin AS admin")
accounts.first.admin
=> 1

注意する点は中間テーブルから引っ張ってきたbooleanの属性はtrueまたはfalseではなく、1または0となることです。単純にaccount.admin?とするだけであれば、問題なくtrue, falseの判定ができますが中身のデータを使って何かをするようなときには気をつけなくてはいけません。