【问题标题】:adding multiple profile types for users为用户添加多种配置文件类型
【发布时间】:2016-01-22 07:37:20
【问题描述】:

我正在设计一个 Rails 应用程序,用户可以在其中登录并创建个人资料。 我希望用户可以选择 3 种个人资料类型(提供者、寻求者、专业人士)。

我已使用设计 gem 进行用户身份验证。 我有一个配置文件模型,并且必须遵循关联。

用户.rb has_one :profile

个人资料.rb 属于_to:用户

目前用户可以创建通用配置文件,但我希望对其进行更改,以便每种配置文件类型都有不同的输入字段。

最基本的方法是什么?

【问题讨论】:

  • 这里的关键字是“模型继承”。可能你想要single table inheritance 解决方案

标签: ruby-on-rails single-table-inheritance sti profiles


【解决方案1】:

您可以使用 STI(单表继承),但我通常会避免使用 - 您必须问自己的问题是:“我真的需要为每个配置文件类型定义一个新模型吗?”

实现此目的的最简单方法之一是设置一个kind 属性,然后为每个Profile#kind(您不能使用为STI 保留的类型关键字)相应地赋予其自己的关联:

class User
  has_many :profiles
  has_one :provider_profile, -> {where(kind: "provider")}, class_name: "Profile"
  has_one :seeker_profile, -> {where(kind: "seeker")},  class_name: "Profile"
  has_one :professional_profile, -> {where(kind: "professional")},  class_name: "Profile"

end

【讨论】:

  • 如果您能简短地提及 STI 的利弊,那就太好了
  • 同上,我看到很多人提到 STI 是个坏主意,但我们已经大量使用它们
  • 优点:从概念上讲容易掌握,方法很糟糕……缺点:组合优于继承和 SRP。简而言之,如果我不得不为每种类型创建模型和专用 UI​​,我更愿意重新考虑我的域模型并为这些模型提供自己的数据(也称为表),而不是共享它并担心所有可能的反模式正在蔓延......
【解决方案2】:

STI 的另一个选项是enum

#app/models/profile.rb
class Profile < ActiveRecord::Base
   enum state: [:provider, :seeker, :professional]
end

这为您提供了一个 int 列(在本例中为 state),它表示对象是否具有特定属性(例如 provider? / seeker? 等)。

它将提供与STI 类似的一组功能,除了为您提供一个可供调用的模型(而不是使用 STI 模式时您会得到的3)。


性传播感染

STI 很好如果你有需要调用多个模型。

大多数时候,你不会。有一个good writeup about it here.

如果在您的情况下使用STI,您最终会得到:

#app/models/profile.rb
class Profile < ActiveRecord::Base
   belongs_to :user
end

#app/models/seeker.rb
class Seeker < Profile
end

#app/models/professional.rb
class Professional < Profile
   def add_client
      ...
   end
end

需要注意的重要一点是,尽管这在您的模型中看起来很漂亮,但这意味着您的前端需要调用:

#config/routes.rb
resources :professionals, only: [:new, :create]

#app/controllers/professionals_controller.rb
class ProfessionalsController < ApplicationController
   def index
      @professional = Professional.find params[:id]
   end
end

如果您打算致电Profile.find_by type: "professional",请忘记它。那是antipattern,效率很低。

--

判断你是否真的需要遵循 STI 模式的方法很简单——你需要额外的方法/属性为每个subclass吗?

如果没有,那么您可以使用枚举:

#app/models/user.rb
class User < ActiveRecord::Base
   has_one :profile
   before_create :build_profile #-> creates blank profile with each new user
   accepts_nested_attributes_for :profile
end

#app/models/profile.rb
class Profile < ActiveRecord::Base
   belongs_to :user
   enum state: [:provider, :seeker, :professional] #-> defaults to "provider"
end

我个人会在控制器中使用enum 进行调节:

#app/controllers/profiles_controller.rb
class ProfilesController < ApplicationController
   def edit
      @profile = current_user.profile
      if @profile.professional?
        ...
      elsif @profile.seeker?
        ...
      end
   end
end

--

了解其工作原理的最佳方法是查找 object orientated programming,简而言之就是 Ruby/Rails。

OOP 使用来创建"objects"...

这些对象中的每一个都作为 instances 调用(这是术语“实例变量”和“实例方法”的来源) - 这些实例由您的应用程序保存在内存中。

OOP 程序的工作方式是接受用户的输入,确定对象之间的交互,然后输出结果。这就是所有现代游戏的运作方式。

因此,当您查看 Rails 时,您必须从您将要调用的 对象 的角度来看待它。你真的想拉Professional 对象,还是只是你正在使用的Profile

【讨论】:

    猜你喜欢
    • 2012-05-09
    • 2016-06-30
    • 2014-04-21
    • 1970-01-01
    • 1970-01-01
    • 2013-02-08
    • 1970-01-01
    • 2018-04-14
    • 1970-01-01
    相关资源
    最近更新 更多