モデルを次のように設定しました(再販業者のために保存したい情報は、そのテーブルのすべてのフィールドをミラーリングし、既存のデータ構造を使用するためにDRYと一致しているように見えたため、連絡先での自己関連付け):
class Contact < ActiveRecord::Base
attr_accessible :reseller_id
has_and_belongs_to_many :users
has_many :reseller_clients, :class_name => "Contact", :foreign_key => "reseller_id"
belongs_to :reseller, :class_name => "Contact"
end
class User < ActiveRecord::Base
attr:accessible :name
has_and_belongs_to_many :contacts
end
cancanを使用して、自分の連絡先を管理できるリセラーログインが必要です。ユーザーと再販業者の間のマッピングはHABTMであるため、これは次のようにすることで実現できcan :manage Contact, :users => {:id => user.id}
ます。
また、リセラーログインで、次のロジックでmanaged_accountsによって記述されたセットに一致するすべての連絡先を管理できるようにしたいと思います。
reseller_contacts = user.contacts
managed_accounts = []
reseller_contacts.each do |i|
managed_accounts << i.reseller_clients
end
managed_accounts.flatten!
私の現在の能力クラスには次のものがあります。
class Ability
include CanCan::Ability
def initialize(user)
if user.role? :reseller
# Allow resellers to manage their own Contact
can :manage, Contact, :users => {:id => user.id} # This works correctly at present
# Allow resellers to manage their client Contacts
can :manage, Contact, :reseller => {:users => {:id => user.id}} #This doesn't work
end
end
end
そのままの状態で受け取るエラーは次のとおりです。
Mysql2::Error: Unknown column 'contacts.users' in 'where clause': SELECT `contacts`.* FROM `contacts` INNER JOIN `contacts` `resellers_contacts` ON `resellers_contacts`.`id` = `contacts`.`reseller_id` INNER JOIN `contacts_users` ON `contacts_users`.`contact_id` = `resellers_contacts`.`id` INNER JOIN `users` ON `users`.`id` = `contacts_users`.`user_id` INNER JOIN `contacts_users` `users_contacts_join` ON `users_contacts_join`.`contact_id` = `contacts`.`id` INNER JOIN `users` `users_contacts` ON `users_contacts`.`id` = `users_contacts_join`.`user_id` WHERE ((`contacts`.`users` = '---\n:id: 6\n') OR (`users`.`id` = 6))
カンカンについての私の理解は、それが許可されているものと許可されていないものを連絡先ごとにチェックするということです。ブロック内でやりたいことができれば、次のように表示されます(リセラー自身の連絡先とリセラーのクライアントであるすべての連絡先の両方をカバーします)。
can :manage, Contact do |contact|
user.contacts.exists?(contact.reseller_id) || user.contacts.exists?(contact.id)
end
ただし、これにブロックを使用することはできません@contacts = Contact.accessible_by(current_ability)
。コントローラーのインデックスアクションで使用しようとすると、次のようになります。
The accessible_by call cannot be used with a block 'can' definition. The SQL cannot be determined for :index Contact(id: integer, first_name: string, last_name: string, postal_addr_line_1: string, postal_addr_line_2: string, postal_addr_line_3: string, postal_addr_city: string, postal_addr_post_code: string, postal_addr_country: string, billing_addr_line_1: string, billing_addr_line_2: string, billing_addr_line_3: string, billing_addr_city: string, billing_addr_post_code: string, billing_addr_country: string, contact_email: string, company_name: string, phone_home: string, phone_work: string, phone_mobile: string, split_bills: boolean, created_at: datetime, updated_at: datetime, reseller_id: integer)
編集:
ほとんど解決しましたが、今は能力を組み合わせるという問題があります。
アビリティモデルの作業部分を次のように変更しました。
reseller_contacts = user.contacts
managed_accounts = []
reseller_contacts.each do |i|
i.reseller_clients.each do |rc|
managed_accounts << rc.id
end
end
can :manage, Contact, :id => managed_accounts
can :manage, Contact, :users => {:id => user.id}
can :create, Contact
ここでの唯一の問題は、最初のcan :manage
行が2番目の行で上書きされることです。私はそれらが置き換えではなく、付加的であるべきだという印象を受けました。さらに調査が必要ですが、この質問自体は上記で解決されたと思います。次に、両方のcan :manage
行を適用する方法を検討する必要があります。