在Laravel中跨多个步骤保留表单数据

时间:2014-03-13 07:14:50

标签: php forms laravel

当我过去制作多步表单时,我通常会在将表单数据返回到视图之前将其存储在会话中,这样,如果用户刷新页面或点击浏览器,数据就会持续存在。本地后退按钮。

将我过去的逻辑转移到Laravel我构建了以下三个阶段的形式:

[Input -> Confirm -> Success]

routes.php文件

Route::group(array('prefix' => 'account'), function(){
    Route::get('register', array(
        'before'  => 'guest',
        'as'      => 'account-create',
        'uses'    => 'AccountController@getCreate'
    ));

    Route::post('register', array(
        'before'  => 'guest|csrf',
        'as'      => 'account-create-post',
        'uses'    => 'AccountController@postCreate'
    ));

    Route::get('register/confirm', array(
        'before'  => 'guest',
        'as'      => 'account-create-confirm',
        'uses'    => 'AccountController@getCreateConfirm'
    ));

    Route::post('register/confirm', array(
        'before'  => 'guest|csrf',
        'as'      => 'account-create-confirm-post',
        'uses'    => 'AccountController@postCreateConfirm'
    ));

    Route::get('register/complete', array(
        'before'  => 'guest',
        'as'      => 'account-create-complete',
        'uses'    => 'AccountController@getCreateComplete'
    ));
});

AccountController.php

<?php
class AccountController extends BaseController {

  private $form_session = 'register_form';

  public function getCreate() 
  {
      if(Session::has($this->form_session)) 
      {
          // get forms session data
          $data = Session::get($this->form_session);

          // clear forms session data
          Session::forget($this->form_session);

          // load the form view /w the session data as input
          return View::make('account.create')->with('input',$data);
      }

      return View::make('account.create');
  }

  public function postCreate() 
  {
      // set the form input to the session
      Session::set($this->form_session, Input::all());

      $validation_rules = array(
          'email'         => 'required|max:50|email|unique:users',
          'password'      => 'required|max:60|min:6',
          'password_conf' => 'required|max:60|same:password'                    
      );

      $validator = Validator::make(Input::all(), $validation_rules);

      // get forms session data
      $data = Session::get($this->form_session);

      // Return back to form w/ validation errors & session data as input
      if($validator->fails()) {
        return  Redirect::back()->withErrors($validator);
      } 

      // redirect to the confirm step
      return Redirect::route('account-create-confirm');
  }

  public function getCreateConfirm() 
  {
      // prevent access without filling out step1
      if(!Session::has($this->form_session)) {
        return Redirect::route('account-create');
      }

      // get forms session data
      $data = Session::get($this->form_session);

      // retun the confirm view w/ session data as input
      return View::make('account.create-confirm')->with('input', $data);
  }

  public function postCreateConfirm() 
  {
      $data = Session::get($this->form_session);

      // insert into DB
      // send emails 
      // etc.

      // clear forms session data
      Session::forget($this->form_session);

      // redirect to the complete/success step
      return Redirect::route('account-create-complete');
  }

  public function getCreateComplete() {
      return View::make('account.create-complete');
  }
}

create.blade.php

<form action="{{ URL::route('account-create-post') }}" method="post">

    Email: <input type="text" name="email" value="{{ (isset($input['email'])) ? e($input['email']) : '' }}">
    @if($errors->has('email'))
        {{ $errors->first('email') }} 
    @endif
    <br />

    Password: <input type="text" name="password" value="">
    @if($errors->has('password'))
        {{ $errors->first('password') }} 
    @endif
    <br />

    Password Confirm: <input type="text" name="password_conf" value="">
    @if($errors->has('password_conf'))
        {{ $errors->first('password_conf') }} 
    @endif     
    <br />

    {{ Form::token() }}

    <input type="submit" value="Confirm">

</form>

创建-confirm.blade.php

Email: {{ $input['email']; }}
Password: {{ $input['password']; }}

<form action="{{ URL::route('account-create-confirm-post') }}" method="post">
    {{ Form::token() }}
    <a href="{{ URL::previous() }}">return</a> 
    <input type="submit" name="submit_forward" value="Submit">
</form>

以上工作正常,但我想知道这是否是在Laravel中处理多步骤表单的最佳方式?

4 个答案:

答案 0 :(得分:7)

当我创建多部分表单时,我总是以某种方式完成它,以便用户可以随后返回并稍后完成表单,方法是使每个表单保持对数据库的所有形式。

例如

第1步 - 创建帐户

我希望用户在此步骤创建其身份验证详细信息,在此处创建用户帐户(带密码)并将用户登录,重定向到仪表板。在那里,我可以检查用户是否有个人资料,如果他们没有,请将他们重定向到个人资料创建表单。

第2步 - 配置文件创建

由于我们有经过身份验证的用户,因此配置文件创建表单可以将其数据保存到当前登录的用户。后续部分遵循相同的过程,但检查上一步的存在。

您的问题似乎是确认用户是否希望创建帐户。在您的情况下我会做的是,在您为确认用户帐户而创建的表单上,我会将用户数据保存在隐藏的输入字段中。

Email: {{ $input['email'] }}
Password: {{ $input['password'] }}

<form action="{{ URL::route('account-create-confirm-post') }}" method="post">
    <input type="hidden" name="email" value="{{ $input['email'] }}">
    <input type="hidden" name="password" value="{{ $input['password'] }}">
    {{ Form::token() }}
    <a href="{{ URL::previous() }}">return</a> 
    <input type="submit" name="submit_forward" value="Submit">
</form>

虽然当你要求他们在上一页确认他们的密码时,在这个页面上显示用户选择的密码似乎有点多余,而且有些用户可能会质疑为什么他们的密码在屏幕上以明文显示,特别是如果他们从公共计算机访问该网站。

我建议的第三个选项是创建用户帐户并对其进行软删除(Laravel 4.2 Docs / Laravel 5 Docs),将用户帐号返回到新表单:

Email: {{ $input['email'] }}
Password: {{ $input['password'] }}

<form action="{{ URL::route('account-create-confirm-post') }}" method="post">
    <input type="hidden" name="id" value="{{ $user_id }}">
    {{ Form::token() }}
    <a href="{{ URL::previous() }}">return</a> 
    <input type="submit" name="submit_forward" value="Submit">
</form>

然后在用户确认其帐户时撤消软删除。这还有额外的好处,您可以跟踪尝试多次注册帐户并且未完成流程的人员,并查看您的用户体验是否存在问题。

结论

当然,您仍然可以按照常规会话的方式进行操作,我在这里尝试做的就是向您展示一些其他可以接近它的方法,就像所有与最好的方法一样做某事的方式,这是一个高度自以为是的主题,很可能会就如何做到这一点得到许多反对意见。最好的方法是最适合您和您的用户的方式......主要是您的用户。

答案 1 :(得分:1)

有两种方法可以做到(我能想到)。我更喜欢第二个。

  1. 客户端 - 一切都可以通过javascript处理。将使用javascript检查基本验证(如果字段是电子邮件,如果字段具有足够的字符等)。确认后,AJAX请求将通过服务器端验证,如果出现任何问题,您可以突出显示无效输入。 “检查电子邮件是否可用”按钮(通过AJAX)也很棒。
  2. 服务器端 - 几乎就是你所做的,但我会把它移到服务上 - 它会让它更清洁。
  3.  public function getCreate() {
          if ($this->formRememberService->hasData()) {
               return View::make('account.create')
                    ->with('input', $this->formRememberService->getData());
          }
          return View::make('account.create');
     }
    
     public function postCreate() {
          $this->formRememberService->saveData(Input::all());
          // ...
     }
    
     public function postCreateConfirm() {
          // ...
          $this->formRememberService->clear();
          return Redirect::route('account-create-complete');
     }
    

    添加“忘记我”动作会很好(特别是如果表单需要更多私人数据)。

    为什么getCreate()Session::forget()?如果有人回去更改某些内容并意外离开您的网站,他的数据将会丢失。

答案 2 :(得分:0)

1st)在包含随机md5字符集的表单中创建一个自定义隐藏字段,以使用表单提交它...(它可以是时间戳,用户IP地址和国家连接在一起作为3 md5字符串分隔无论是什么字符,或#,所以它可以作为表格的标记工作)

第二步)将隐藏字段传递到控制器并通过在控制器中生成相同的值从表单获取用户输入后验证它,将这些值加密为md5,然后将它们连接在一起,并比较值来自用户输入表单,其中包含您在控制器中生成的值。

3rd)将表单的值放在会话中的控制器中,然后每次访问用户将要访问的每个视图时重新生成会话ID。

4th)根据用户访问每个页面的时间戳更新会话中的时间戳。

答案 3 :(得分:-2)

仅仅因为你了解Laravel,并不意味着你必须在Laravel中做所有事情。

多步骤表单绝不应该涉及服务器端魔术。您可以做的最好和最简单的方法是使用display:none;隐藏某些步骤,然后使用javascript切换可见性切换到下一步。