在 Laravel 中跨多个步骤持久化表单数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22371630/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Persist Form Data Across Multiple Steps in Laravel
提问by Jeemusu
When I've made multistep forms in the past I would generally store the form data in the session before returning it to the view, that way the data persists if the user refreshes the page or clicks the browser's native back buttons.
过去,当我制作多步表单时,我通常会在将表单数据返回到视图之前将其存储在会话中,这样,如果用户刷新页面或单击浏览器的本机后退按钮,数据就会保持不变。
Transferring my past logic to Laravel I built the following form consisting of three stages:
将我过去的逻辑转移到 Laravel 我构建了以下包含三个阶段的表单:
[Input -> Confirm -> Success]
[Input -> Confirm -> Success]
Routes.php
路由.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
<?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
创建.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>
create-confirm.blade.php
创建-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>
The above works fine, however I am wondering if this is the best way to approach multi-step forms in Laravel?
以上工作正常,但是我想知道这是否是在 Laravel 中处理多步表单的最佳方式?
回答by Andrew Willis
When I have created multi-part forms, I have always done it in a way so that the user can always come back and finish the form later, by making each form persist what it has to the database.
当我创建多部分表单时,我总是以一种方式完成它,以便用户可以随时回来并完成表单,方法是让每个表单将其拥有的内容持久化到数据库中。
For instance
例如
Step 1 - Account Creation
第 1 步 - 创建帐户
I would have the user create their authentication details at this step, create the user account (with password) here and also log the user in, redirecting to the dashboard. There I can do a check to see if the user has a profile and if they don't, redirect them to the profile creation form.
我会让用户在这一步创建他们的身份验证详细信息,在此处创建用户帐户(带密码)并登录用户,重定向到仪表板。在那里我可以检查用户是否有个人资料,如果没有,则将他们重定向到个人资料创建表单。
Step 2 - Profile Creation
第 2 步 - 创建配置文件
Because we have an authenticated user, the profile creation form can save its data to the currently logged in user. Subsequent sections follow the same process but check the existence of the previous step.
因为我们有一个经过身份验证的用户,所以配置文件创建表单可以将其数据保存到当前登录的用户中。后续部分遵循相同的过程,但检查上一步是否存在。
Your question seems to be about confirming whether a user wishes to create an account. What I would do in your situation would be, on the form you created to confirm the user account, I would keep the user's data in hidden input fields.
您的问题似乎是关于确认用户是否希望创建帐户。在您的情况下,我会做的是,在您创建的用于确认用户帐户的表单上,我会将用户的数据保存在隐藏的输入字段中。
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>
Although displaying the user's chosen password back to them on this page seems to be a bit superfluous when you ask them to confirm their password on the previous page, plus some users might question why their password is being shown in plaintext on the screen, especially if they are accessing the site from a public computer.
尽管当您要求他们在上一页确认他们的密码时,在此页面上向他们显示用户选择的密码似乎有点多余,但有些用户可能会质疑为什么他们的密码以明文形式显示在屏幕上,特别是如果他们正在从公共计算机访问该站点。
The third option I would suggest would be to create the user account and soft-delete it (Laravel 4.2 Docs/ Laravel 5 Docs), returning the user's account number to the new 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>
then undo the soft-delete when the user confirms their account. This has the added bonus that you could track people trying to sign up multiple times for an account and not completing the process and see if there's a problem with your UX.
然后在用户确认其帐户时撤消软删除。这有一个额外的好处,您可以跟踪尝试多次注册帐户但未完成该过程的人,并查看您的用户体验是否存在问题。
Conclusion
结论
Of course, you could also still do it the way you always have with a session, all I have tried to do here is show you some other ways you can approach it, as with everything to do with the best wayof doing something, this is a highly opinionated subject and is likely to get many opposing views on how it should be done. The best way to do it is the way that works best for you and your users... mainly your users.
当然,您仍然可以按照会话中一贯的方式来做,我在这里尝试做的只是向您展示一些其他的方法来处理它,就像所有与做某事的最佳方式有关的事情一样,这是一个高度自以为是的主题,很可能会在如何完成的问题上得到许多反对意见。最好的方法是最适合您和您的用户的方式……主要是您的用户。
回答by Daniel Antos
There are two ways to do it (that i can think of). I prefer second one.
有两种方法可以做到(我能想到的)。我更喜欢第二个。
- Client side - everything can be handled by javascript. Basic validation (if field is email, if field has enough characters etc.) would be checked with javascript. After confirmation, AJAX request would go through server side validation and if anything went wrong you could highlight invalid inputs. "check if email is available" button (via AJAX) would be great too.
- Server side - pretty much what you did but I would move it to service - it would make it much cleaner.
- 客户端 - 一切都可以由 javascript 处理。基本验证(如果字段是电子邮件,如果字段有足够的字符等)将使用 javascript 进行检查。确认后,AJAX 请求将通过服务器端验证,如果出现任何问题,您可以突出显示无效输入。“检查电子邮件是否可用”按钮(通过 AJAX)也很棒。
- 服务器端 - 几乎是你所做的,但我会将它移到服务端 - 它会让它更干净。
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');
}
Adding "forget me" action would be nice (especially if form requires more private data).
添加“忘记我”操作会很好(特别是如果表单需要更多私人数据)。
Why getCreate()
has Session::forget()
? If someone goes back to change something and accidently leaves your site his data will be lost.
为什么getCreate()
有Session::forget()
?如果有人回去更改某些内容并意外离开您的站点,他的数据将丢失。
回答by Mostafa A. Hamid
1st) Create a custom hidden field in the form containing a random md5 character set to submit it with the form... (it can be the timestamp, the user ip address, and country concatenated together as 3 md5 strings separated by whatever character , or #, so it can be working as a token of the form)
1) 在包含随机 md5 字符集的表单中创建一个自定义隐藏字段以将其与表单一起提交...(它可以是时间戳、用户 IP 地址和国家/地区,连接在一起作为 3 个 md5 字符串,由任何字符分隔,或#,所以它可以作为表单的标记)
2nd) pass the hidden field into your controller and validate it after getting the user input from the form by generating the same values in your controller, encrypting these values as md5 too, then concatenate them all together, and compare the values that is coming from the user input form with the values you are generating in your controller.
2) 将隐藏字段传递到您的控制器中,并在通过在您的控制器中生成相同的值从表单获取用户输入后对其进行验证,也将这些值加密为 md5,然后将它们连接在一起,并比较来自的值带有您在控制器中生成的值的用户输入表单。
3rd) Put the values of the form in your controller in a session then regenerate the session id every visit to every view the user is going to visit.
3) 将表单的值放在您的控制器中的会话中,然后在每次访问用户将要访问的每个视图时重新生成会话 ID。
4th) update the timestamp in your session according the timestamp the user is visiting every page.
4)根据用户访问每个页面的时间戳更新会话中的时间戳。
回答by Rápli András
Just because you know Laravel, does not mean you have todo everything in Laravel.
仅仅因为您了解 Laravel,并不意味着您必须在 Laravel 中做所有事情。
Multi-step forms should never involve server-side magic. The best and easiest you can do is to hide certain steps with display:none;
and switch to the next step using javascript toggling visibilities only.
多步骤表单永远不应该涉及服务器端魔法。您可以做的最好和最简单的方法是display:none;
使用 javascript 仅切换可见性来隐藏某些步骤并切换到下一步。