我有一个欢迎的wizzard,它在第一次登录时构建用户配置文件。问题是它实施起来很乱,但是我已经尝试重构几次并重写它但是不能用更好的东西来创建下面。
在理想的世界中,它将全部在welcome_controller.rb中,但这引起了很大的麻烦,所以现在我改写了profile_controller的更新方法。
关于如何改善这一点的任何想法都会让它变得更加干燥和清洁?很想收到一些关于此的好的意见和想法,或许将所有更新内容转移到欢迎控制器?
WelcomeController:
class WelcomeController < ApplicationController
before_filter :authenticate_user!
before_filter :load_step
layout "welcome"
def sub_layout
"center"
end
def edit
# form updates post to edit since
# profile is non existant yet
params[:step] = "photos" unless params[:step]
@photos = Photo.where(:attachable_id => current_user.id)
@profile = Profile.where(:user_id => current_user.id).first
@photo = Photo.new
if ["photos", "basics", "details", "test"].member?(params[:step])
# force rendering the correct step
case current_user.profile.step
when 1
render :template => "/profiles/edit/edit_photos", :layout => "welcome"
when 2
render :template => "/profiles/edit/edit_basics", :layout => "welcome"
when 3
render :template => "/profiles/edit/edit_details", :layout => "welcome"
when 4
render :template => "/profiles/edit/edit_test", :layout => "welcome"
end
else
render :action => "/profiles/edit/edit_photos"
end
end
def load_step
redirect_to root_path if current_user.profile.complete
case current_user.profile.step
when 1
redirect_to "/welcome" unless params[:controller] == "welcome"
when 2
redirect_to "/welcome/basics" unless params[:controller] == "welcome" && params[:action] == "edit" && params[:step] == "basics"
when 3
redirect_to "/welcome/details" unless params[:controller] == "welcome" && params[:action] == "edit" && params[:step] == "details"
when 4
redirect_to "/welcome/test" unless params[:controller] == "welcome" && params[:action] == "edit" && params[:step] == "test"
end
end
end
ProfileController可:
class ProfileController < ApplicationController
...
def update
@profile = Profile.find(params[:id])
@tags = Session.tag_counts_on(:tags)
@profile.form = params[:form]
@match = Match.where(:user_id => current_user.id).first
authorize! :update, @profile
respond_to do |format|
if @profile.update_attributes(params[:profile])
if current_user.profile.complete
format.html { redirect_to "/profiles/#{ current_user.username }/edit/#{ @profile.form }", notice: t('notice.saved') }
else
case current_user.profile.step
when 1
current_user.profile.update_attributes(:step => 2)
format.html { redirect_to "/welcome/basics", notice: t('notice.saved') }
when 2
current_user.profile.update_attributes(:step => 3)
format.html { redirect_to "/welcome/details", notice: t('notice.saved') }
when 3
current_user.profile.update_attributes(:step => 4)
format.html { redirect_to "/welcome/test", notice: t('notice.saved') }
end
end
else
if current_user.profile.complete
format.html { render action: "/edit/edit_" + params[:profile][:form], :what => @profile.form }
else
case current_user.profile.step
when 1
current_user.profile.update_attributes(:step => 2)
format.html { redirect_to "/welcome/basics", notice: t('notice.saved') }
when 2
current_user.profile.update_attributes(:step => 3)
format.html { redirect_to "/welcome/details", notice: t('notice.saved') }
when 3
current_user.profile.update_attributes(:complete => 1)
format.html { redirect_to root_path }
end
end
end
end
end
...
end
视图位于/ profiles / edit / *
答案 0 :(得分:3)
众所周知,奇才很难做到正确,我从未见过完全满足我的实施。我通常会使用所谓的“表单对象”并为每一步创建一个安静的控制器。
关于这个问题有一个很好的(但付费的)Railscast。
要点是:使用ActiveModel创建一个像常规ActiveRecord模型一样嘎嘎叫的对象。
例如:
class Welcome::BasicInformation
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
def persisted?
false
end
def initialize(user)
@user = user
end
attr_reader :user
delegate :some_field, :some_other_field, to: :user
validates_presence_of :some_field
def save(params)
user.some_field = params[:some_field]
user.some_other_field = params[:some_other_field]
if valid?
user.step = 2
user.save
end
end
def photo
@photo ||= Photo.new
end
def profile
@profile ||= user.profiles.first
end
end
你基本上每一步都要创建一个这样的模型。
然后,您可以为每个步骤创建控制器,并为所有步骤使用专门的ApplicationController:
class Welcome::ApplicationController < ::ApplicationController
layout "welcome"
before_filter :authentice_user!
end
每一步:
class Welcome::BasicInformationsControlller < Welcome::ApplicationController
def new
@step = Welcome::BasicInformation.new(current_user)
end
def create
@step = Welcome::BasicInformation.new(current_user)
if @step.save(params[:welcome_basic_information])
redirect_to welcome_some_other_step_path, notice: "Yay"
else
render :new
end
end
end
为每一步创建一条路线:
namespace :welcome do
resource :basic_information, only: [:new, :create]
resource :some_other_step, only: [:new, :create]
end
这只会留下一些自动重定向,例如禁止用户进入他们尚未被允许访问的步骤。由于您为每个步骤使用单独的URL,因此这可能不那么重要。
您可以在表单对象中存储有关访问哪个步骤的信息:
class Welcome::BasicInformation
# ...
def allowed?
user.profile.step == 1
end
end
然后稍微重构一下控制器:
class Welcome::BasicInformationsController < Welcome::ApplicationController
before_filter :allowed?
def new
end
def create
if step.save(params[:welcome_basic_information])
redirect_to welcome_some_other_step_path, notice: "Yay"
else
render :new
end
end
private
def step
@step ||= Welcome::BasicInformation.new(current_user)
end
helper_method :step
def allowed?
redirect_to previous_step_path unless step.allowed?
end
end
这可能不会更短,但我确实感觉它是多么灵活,每个步骤的重点是什么,如何在每个步骤上进行不同的验证等等。每个控制器/模型组合都非常容易理解,对其他人来说是可以理解的。
答案 1 :(得分:1)
我会做一些事情,但首先是一些想法。
那么,我要做什么。
如果我理解正确,将向用户显示的步骤仅取决于current_user上设置的用户#步骤。因此,我认为没有必要传递任何网址变量,您可以从current_user
获取当前/下一步。
代码重构(可能是一些错误,没有测试)
全部在ProfileController
def edit
@profile = Profile.find(current_user.id)
@next_step = current_user.step.to_i + 1 # I imply that there's just single permissable next step
render :template => "/profiles/edit/#{@next_step}", :layout => "welcome"
end
def update
@profile = Profile.find(params[:id])
authorize! :update, @profile
if @profile.update_attributes(params[:profile])
# you should pass step number in params so I get's updated by default.
redirect_to "/welcome/basics", notice: t('notice.saved')
else
end
end