在最近的项目中,我使用了
表单对象模式来一步创建Devise用户和公司。这需要绕过Devise的RegistrationsController并创建自己的SignupsController。
get 'signup' => 'signups#new', as: :new_signup
post 'signup' => 'signups#create', as: :signups
class SignupsController < ApplicationController
def new
@signup = Signup.new
end
def create
@signup = Signup.new(params[:signup])
if @signup.save
sign_in @signup.user
redirect_to projects_path, notice: 'You signed up successfully.'
else
render action: :new
end
end
end
引用的注册模型被定义为一个表单对象。
class Signup
include ActiveModel::Model
attr_reader :user
attr_reader :account
attr_reader :membership
attr_accessor :name
attr_accessor :company_name
attr_accessor :email
attr_accessor :password
validates :name, :company_name, :email, :password, presence: true
def save
return false unless valid?
delegate_attributes_for_user
delegate_attributes_for_account
delegate_errors_for_user unless @user.valid?
delegate_errors_for_account unless @account.valid?
if !errors.any?
persist!
true
else
false
end
end
private
def delegate_attributes_for_user
@user = User.new do |user|
user.name = name
user.email = email
user.password = password
user.password_confirmation = password
end
end
def delegate_attributes_for_account
@account = Account.new do |account|
account.name = company_name
end
end
def delegate_errors_for_user
errors.add(:name, @user.errors[:name].first) if @user.errors[:name].present?
errors.add(:email, @user.errors[:email].first) if @user.errors[:email].present?
errors.add(:password, @user.errors[:password].first) if @user.errors[:password].present?
end
def delegate_errors_for_account
errors.add(:company_name, @account.errors[:name].first) if @account.errors[:name].present?
end
def persist!
@user.save!
@account.save!
create_admin_membership
end
def create_admin_membership
@membership = Membership.create! do |membership|
membership.user = @user
membership.account = @account
membership.admin = true
end
end
end
关于表单对象的一篇优秀文章(也是我的工作来源)是这篇 CodeClimate 博客文章 Refactoring。
总的来说,我更喜欢这种方法而不是使用 accepts_nested_attributes_for
,虽然可能还有更好的方法。如果你找到了,请告诉我!
===
编辑:添加了相关模型及其关联,以更好地理解。
class User < ActiveRecord::Base
has_many :memberships
has_many :accounts, through: :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :account
end
class Account < ActiveRecord::Base
has_many :memberships, dependent: :destroy
has_many :users, through: :memberships
has_many :admins, through: :memberships,
source: :user,
conditions: { 'memberships.admin' => true }
has_many :non_admins, through: :memberships,
source: :user,
conditions: { 'memberships.admin' => false }
end
这个模型中的结构是与 thoughtbot 公司的 gem
saucy 一起建模的。据我所知,源代码不在 Github 上,但可以从 gem 中提取它。我通过重新建模学到了很多知识。
@signup.save
处理,该方法创建所有对象及其关联。 - Thomas Klemm