6

私は頭をこれに巻き付けようとして壁に頭をぶつけてきたので、どんなガイダンスも大歓迎です...

次の階層を反映するようにユーザー システムをセットアップしたいと考えています。

User
|- email address
|- password
|- billing information
|- contact information
|- account preferences
|
|=> Agent
|=> - agent-specific information
|=> - has_many Users
|=> - belongs_to Manager
|
|=> Manager
|=> - manager-specific information
|=> - has_many Agents, Users
|
|=> Administrator
|=> - can manage everything

認証と承認を処理するためのDeviseCanCanUserのセットアップを備えたモデルが既にあるので、ロールを使用してユーザーのタイプを特定のアクションなどに制限する方法を知っています。

私が迷っているのは、Rails コードとデータベースの両方でこれらのサブクラスの関係を整理する方法です。上からわかるように、AgentManager、およびAdministratorすべてが に含まれる情報を共有しますUserが、それぞれに追加の機能とそれに関連する情報があります。

STIポリモーフィック アソシエーション、および自己参照アソシエーションについて読んだことがあります。

STI を使用する場合、テーブルには [ / / ] 固有の情報Userすべてのフィールドが含まれている必要がありますよね? それは私のテーブルを巨大にするでしょう、それは私が避けたいことです. 逆に、ポリモーフィックを使用する場合、他のすべてのタイプのサブクラス テーブルですべての共通情報を複製する必要はないでしょうか?AgentManagerAdministratorUserUserUser

さらに混乱を招くことに、上記の質問に対する答えがサブクラス間の関係でどのように機能するかについて頭を悩ませることはできません (たとえば、 Managerhas_manyAgentsですが、両方ともUser...?? のサブクラスです)。

コードの読みやすさとデータの整合性を十分に考慮し、A が最適なアプローチであり、B またはnがなぜ最適なのかを (Rails の初心者のように) 簡単に説明する詳細な回答を通じて、誰かが私にこれをまっすぐに設定してくれることを本当に感謝しています。比較すると、この状況では適切なアプローチではなく、上記の関係を実装するためのコード例が得られます。この問題を解決したいのですが、それよりも重要なのは、なぜその解決策が機能するのかを知りたいということです!

4

2 に答える 2

8

どのアプローチがどのような状況でも最適である理由を簡単に説明できるとは思いません。Stack Overflow に関するかなりの量の質問は、モデル間の関係を設計する方法に関する質問です。これは複雑なトピックであり、適切な解決策には、解決しようとしている問題についての詳細な知識が必要です。それでも、おそらく最初の数回はうまくいかないでしょう。

これまでの最善の方法は、この問題について TDD/BDD を完全に処理し、テスト/仕様に設計を任せることです。より良い方法を見つけたら、リファクタリングを恐れないでください。ほとんどの場合、いくつかの間違ったソリューションを試した後に、正しいソリューションが表示されます。エッジケースを知ることができます。Ward Cunningham が「技術的負債」の例えで述べているように、「最初から何をしていたかを知っているかのように、後でリファクタリングします」。ただし、後でその動作を確認するために、必ず受け入れテストを行ってください。

あなたの問題をより具体的にする。3 番目のオプションは、クラスを完全に分割して、それぞれ独自のテーブルを作成することです。現在のプロジェクトで試してみましたが、気に入っています。ビジネス ドメインで意味がない場合は、 userのようなものを定義する必要はありません。動作が共有されている場合は、ミックスインを使用します。最も重要な注意点は、同じフォームからログインするのは簡単ではないということです。

管理者、リクルーター、サプライヤー、ビジターのモデルがあります。それらはすべて別個のモデルであり、いくつかの動作を mixin と共有しています。それらはすべて、動作する独自の名前空間コントローラーを持っています。たとえば、管理者のすべてのアクションは Backend 名前空間にあります。名前空間付きの ApplicationController もあります。Backend::ApplicationController は単純に を指定しますbefore_filter :authorize_admin!。切り替えも複雑なケースステートメントも何もありません。

慣例には特に注意を払う必要があります。モデル間で同じ名前を使用すると、ミックスインが非常に簡単になります。ActiveSupport::Concern を読んで、ミックスインをさらに使いやすくしてください。私はこのようなミックスインを持っています:

module Account
  extend ActiveSupport::Concern
  included do
    devise :database_authenticatable, :trackable, :recoverable, :rememberable
  end
end

そして私のルートでは:

devise_for :recruiters
devise_for :suppliers
# etc...

そして、app/controllers/backend/application_controller.rb次のようになります。

class Backend::ApplicationController < ::ApplicationController
  layout "backend"
  before_filter :authenticate_admin!
  def current_ability
    @current_ability ||= AdminAbility.new(current_admin)
  end
end

では、結論として。どんなアーキテクチャでも機能します。STI とポリモーフィズムにはそれぞれの役割がありますが、ドメインに従ってアーキテクチャをモデル化するようにしてください。Ruby は非常に柔軟な言語であり、これを活用することができます。Devise と CanCan は優れた gem であり、これらの状況を簡単に処理できます。現在取り組んでいるプロジェクトのソリューションを紹介しました。私にはうまくいきますが、あなたにぴったりかどうかはわかりません。間違った決定をしたと感じた場合は、元のアイデアにパッチを当て続けるのではなく、実験とリファクタリングを恐れないでください。

PS。STI と関係について言えば、それらは一緒にうまく機能します。あるサブクラスから別のサブクラスへの関係を定義できます。すべてが期待どおりに機能します。

于 2010-12-11T17:25:55.250 に答える
0

これを考えすぎてしまうかもしれませんが、あなたがやりたいことが非常に複雑であるとは思えません。

STI は、ユーザーをこれらの主要な役割の 1 つだけに制限するため、おそらく最良のアイデアではありません。

メンバーシップ固有の情報を保存する限り、STI の列を追加すると、テーブルが「巨大」になるかどうかはわかりません。null の場合、実際にはリソースを消費していません。そのデータを選択する必要がない場合は、ハッシュを定義して列に格納できるように、単一の列role_informationを使用して使用することを検討してください。serialize :role_information

別のオプションは、応答時にロール メンバーシップをチェックするようにメソッドをオーバーライドすることです。

has_many :agents
def agents
  return nil unless self.has_role(:manager) 
  return self[:agents]
end

「has_role」を、ロールのメンバーシップを確認するために必要なものに置き換えます。

これは設定側でも行うことができます。

特定の機能の承認はコントローラーで行う必要があるため、その時点でロールのメンバーシップを確認する必要があります。

于 2010-12-11T17:31:05.913 に答える