I'm developing a quiz and want to check if a question is answered already. Then I'd like to print something and/or disable the form after submitting. The page will be redirected back to show the new option if the user has any observation or reservation. If the form redirects back how can I check or disable the form so a user can't submit the form again? I am using the following method to redirect back.
<?php
return redirect()->back();
If I pass a variable using the with() method I get no success. The system doesn't recognize my variable. I also tried...
<?php
return redirect()->back()->withDisable('disable');
you can use flash data in laravel for this kind of task.
What is Flash Data?
Flash data helps you to store items in the session only for the next request.Data stored in the session using this method will only be available during the subsequent HTTP request, and then will be deleted.
$request->session()->flash('disabled', 'true');
However, If you need to keep your flash data around for several requests
$request->session()->keep(['disabled', 'true']);
to access your session/flash data you can simply do
$request->session()->all() //all flash session data
$request->session()->get('disabled') // for specific
hope that helps
Just with should do it. For example:
return redirect()->back()->with('status', 'disable');
This is saved in the session data, so you could check for it with:
#if (session('status'))
...
From: https://laravel.com/docs/5.5/redirects#redirecting-with-flashed-session-data
The way I made it work is create a dedicated controller that handles my forms that repeat on multiple pages i.e. somedomain.com/form/callmeback/ and so far so good. However, once the validator has done its thing I need to either return to the page from which the form was submitted, with a list of errors to display or send the message and then return to the originating form page with a success message.
What would be the "best" way to accomplish that?
So far my thoughts are lingering on using $_SERVER['HTTP_REFERER'] or a hidden field with a current_url() as its value, and then just do header('Location:'.$_POST['ref']) but that would not allow me to post back validation errors.
[EDIT]
In the end I've solved my problem using codeIgniter session flash data functionality
//redirect back to source
if($_SERVER['HTTP_REFERER'] && strpos($_SERVER['HTTP_REFERER'], base_url()) !== false) {
//do form handling stuff here
$this->session->set_flashdata('callmeback_errors', validation_errors());
header('Location:' . $_SERVER['HTTP_REFERER']);
} else {
//invalid referer, do nothing say nothing, pretend the page doesn't exist
show_404();
}
thanks for your ideas :)
Flash session is a good idea but i m not sure if it will be working properly with validation_errors() function,
but i have a different thought, what about instead of dedicated controller and dedicated helper that do the same functionality including so you wont have to change the controller and just send the POST array to helper function, hope this helps
Serialize your errors to the session and then remove them on the next request like "flash" message. Ive done similar before with Symfony.
So I have a web app that i'm working on with a form that requires all of the fields to be populated before submitting. If you try to submit the app without a field being populated, it loads the page again with the errors. Once you fill all the fields in and click submit, it does a redirect to the same page and shows a message which is generated from flashdata. See simplified example below.
Welcome controller:
function show_view()
{
$this->load->view('form');
}
function process_form()
{
// make the 'quality' field required
$this->form_validation->set_rules('quality', 'Quality', 'required');
if($this->form_validation->run() == FALSE) //if the fields are NOT filled in...
{
echo form_error('quality');
$this->load->view('form'); //reload the page
}
else // if the fields are filled in...
{
// set success message in flashdata so it can be called when page is refreshed.
$this->session->set_flashdata('message', 'Your rating has been saved');
redirect(welcome/show_view);
}
}
Now to illustrate my issue, lets say I'm on the 'home' view and I navigate to the 'form' view. If i populate the 'quality' field and click submit, i get redirected back to the 'form' view, with a success message. If i click the back button on the browser, it takes me back to the 'home' view. EVERYTHING WORKS AS EXPECTED
Now lets say i'm on the 'home' view and I navigate to the 'form' view. If i click the submit button without populating the 'quality' field, the 'form' view is reloaded again and it displays the error message. If i then populate the 'quality' field and click submit, i get redirected back to the 'form' view with a success message. The problem is, if i click the back button on the browser, it now takes me back to the form page with the error, and I have to click the back button again to get back to the 'home' view.
What is the best coding practice so that if a user submits the form with errors, it will display the errors, and if they fix the errors and submit the form again it will display the success message and if they click back on the browser, it will take them back to the 'home' view??
The problem is that you're using two separate functions to do form handling. The form validation class docs don't really explain it well, and it took me awhile to realize it but the form_validation->run() returns false if there is an error, but also if it is a GET request, and subsequently accounts for the GET request in the related functions like the form_error(), and validation_errors(), set_value(), etc.
The best practice in CI (and in general) is to do this:
class Welcome extends CI_Controller{
function home(){
$this->load->view('home');
}
function form()
{
// make the 'quality' field required
$this->form_validation->set_rules('quality', 'Quality', 'required');
// If the fields are NOT filled in...
// or if there isn't a POST! (check the Form_validation.php lib to confirm)
if ( $this->form_validation->run() === FALSE)
{
// This form_error() function actually doesn't do anything if there
// wasn't a form submission (on a GET request)
echo form_error('quality');
$this->load->view('form'); // load or reload the page
}
else // if the fields are filled in...
{
// set success message in flashdata so it can be
// called when page is redirected.
$this->session->set_flashdata('message', 'Your rating has been saved');
redirect('welcome/home','location', 303);
exit;
}
}
then in the view have the form action="welcome/form"
Basically all of the form error functions and all the stuff related to form validation have checks to see if the form validator actually ran... here is an example from the form_error function in the form helper file
function form_error($field = '', $prefix = '', $suffix = '')
{
if (FALSE === ($OBJ =& _get_validation_object()))
{
return '';
}
return $OBJ->error($field, $prefix, $suffix);
}
When their isn't a POST, it shows as normal, and has the natural page flow you are looking for.
Unrelated to the question, but confusing/noteworthy about the form validation class... if you use the filters like xss_clean, prep_url, etc. in the parameters field, it actually repopulates the $_POST array for you, so you don't really need to do anything extra.
Sometimes it's worth taking a look at the internals of the CI source, there's some clever stuff in there which isn't entirely obvious.
What I expect is for the form to redirect me to a resource listing page, where resource is the entity the user was submitting initially.
If the user is adding a new entry to a resource, they expect to see the entry in the listing once the form has been submitted successfully.
If the form is on the same page, you should still do a location based redirect instead of a http refresh. The status code for this use case is 303 See Other.
via Wikipedia HTTP 303 Status Code
Here is what I would do given your scenario.
function home(){
$this->load->view('home');
}
function show_view()
{
$this->load->view('form');
}
function process_form()
{
// make the 'quality' field required
$this->form_validation->set_rules('quality', 'Quality', 'required');
if($this->form_validation->run() === FALSE) //if the fields are NOT filled in...
{
echo form_error('quality');
$this->load->view('form'); //reload the page
}
else // if the fields are filled in...
{
// set success message in flashdata so it can be called when page is redirected.
$this->session->set_flashdata('message', 'Your rating has been saved');
redirect('welcome/home','location', 303);
exit;
}
}
I'm using Symfony for a while and I think that Symfony's solution is optimal. The way it works is that you have routings (I think that CI has routings too: CI routings) and in your controller you can do something like this in your "create" method:
if the form is valid
set flash notice message
redirect to (your homepage or something else defined in your routing file)
else
set flash error message
set the current view to form
Your form's action is the same controller with the "create" action (which can be only reached via a POST request). The form which you can reach with GET requests is the "new" method. So if you click submit on your form you go to the create method, but the new method generates your form for the first time. If your form does not validate you stay there with flash error messages. If you click back you get a new form, but if the form validates your action (method) redirects to a custom page you set before in the if statement above.
This method works with ajax too, you can check in your action if the request is an XHTTP one, you just return if the form validates or not and you can handle things in javascript. I think that this is the best way to handle forms and if your user does not have javascript enabled he can still use the "standard" way. And by the way: "create" and "new" uses the same template (view). I hope that i made things clear.
The only reliable way to tell the browser to ignore/remove pages in its history is with the javascript command:
location.replace(url);
this does a client-side redirect but replaces the current location in your browser history. Or alternatively not to going to a new page at all (ajax call).
If you use POST as the method in your form they will get an error saying they have to resubmit information to the server to go back, which will scare most people off from using the back button.
You could use a nonce pattern where you generate a timestamp or other unique id, put it in the session and a hidden field in your show_view function when you create the form and then check for a match it on your process function, and remove it from the session if there is a match. That way if they try to submit the same form twice (by clicking the back button) you can detect it by seeing there is not a match and redirect them to the errorless show_view or home page or wherever else you want.
You'll also want to make sure your expires and cache-control headers are forcing the web browser to hit the server everytime vs just using its local cache. http://www.web-caching.com/mnot_tutorial/notes.html#IMP-SERVER
For bettor or worse, most web sites ignore the back button issues such as these
The DOM window object provides access to the browser's history through the history object. It exposes useful methods and properties that let you move back and forth through the user's history, as well as -- starting with HTML5 -- manipulate the contents of the history stack.
https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history
Another thing would be to submit the form with ajax! and populate the errors or success message based on response...(XML,JSON..) of course this is not exactly what you were looking for but it has a lot of advantages in user experience which is what you are trying to improve.
If at all possible avoid a redirect and have the page show the same content as where its being redirected. That way browsers won't count multiple submissions as different pages.
Also the new HTML5 javascript has history state change or whatever its called. So you can define in your program how the site should behave to page changes through the back button and ... soon you will see a lot of back buttons integrated into the web GUI as well.
The easiest way to do what you're proposing is to submit the form values to your PHP validator using an HTTPRequest (AJAX) and then show the errors using JavaScript. This way you won't have to do any redirecting, and the back button will still take you home.
If you insist on using redirections, I have a few ideas in mind, but none of them are elegant. Perhaps someone else has an idea for this?
Although I really would recommend using AJAX form submission as a solution for this problem.
As far as I know - and can find - there is no reliable way to catch the history back event, and all the solutions are Javascript based so you might as well build an AJAX form.
I'd say the only possible non-javascript solution would be either an iframe (which nobody likes), or what BraedenP suggests.
Personally, if you really want this functionality, I'd build an AJAX form and just not have it work this way for non-javascript users (they should be used to having a slightly less streamlined experience).
It's always good practice to add a breadcrumb trail or back button on your page, so this could help as well.
I.e. would you recommend me to use one controller method like this:
function save()
{
if(!is_bool($this->input->post('')))
{
$post_data = $this->input->post('');
$this->mymodel->save($post_data);
}
$this->load->view('myview');
}
Or would you recommend writing it using two methods?
function save()
{
if(!is_bool($this->input->post('')))
{
$post_data = $this->input->post('');
$this->mymodel->save($post_data);
}
redirect('controller/method2')
}
The redirect is the crucial difference here. It prohibits resubmissions from update for example.
How do you do it? Is there another better way?
You should always redirect on a successful form post.
You should always redirect on a successful form post.
Absolutely. For anyone wondering why this is the case, here are a couple of the reasons:
Avoid "duplicate submissions". Ever had that when you innocently click refresh or hit the back button and wham, everything has resubmitted?
Being friendly to bookmarks. If your user bookmarks the page, presumably you want them to return where they created it, rather than a blank form (a redirect makes them bookmark the confirmation/landing page.
Further reading:
http://en.wikipedia.org/wiki/Post/Redirect/Get
As Aren B said, redirection is a good idea, but what I would change in your code is that validation of the post data should be done with the form validation functionallity. It is not only more reauseable but the code will get shorter.
If you want to handle AJAX requests, you would need to return something else than a via or a redirection.
An action within a controller generates the next id from the database and displays it on screen as reference. How can I prevent the action being called again if the user clicks refresh.
The post-redirect-get pattern with Zend Framework would generally involve leaving the action of the form empty (so it posts to itself) and then redirecting when you don't want to display the form again (so upon success).
public function newAction() {
$form = new Form_Foo();
if($this->_request->isPost()) {
if($form->isValid($this->_request->getPost()) {
//save or whatever
return $this->_redirect('path/to/success');
}
// else fall through
}
$this->view->form = $form;
}
if ($this->isPost()) {
// Check validation
if ($error) {
$dataToMove = array();
// $dataToMove is array that you want to pass with redirect
// It can be an array of errors or form data that user has entered
// Use FlashMessenger helper to pass data to redirection via Zend_Session
$this->_helper->getHelper('FlashMessenger')->addMessage($dataToMove);
// And redirect page to form url
$this->_helper->getHelper('Redirector')->goToUrl('/form/url/');
}
// If not posted, get data from FlashMessenger
$data = $this->_helper->getHelper('FlashMessenger')->getMessages();
// And assign to view or make that you want
$this->view->formData = $data;
Although this is older post people still come here for answers, so let me help a bit more.
Redirecting form is great and useful but we are still not preventing peple from clicking back button and resubmitting that way.
The solution is to either show the form as popup and make it disapear when done (easily done with jquery) or generate unique id for each transaction and checking if id was previously used.
See article: http://www.boutell.com/newfaq/creating/stoprefresh.html
Hope it helps.
You can do this by implementing a 302 redirect
header('HTTP/1.1 302 Found');
header('Location: displayId.php?id=5');
die();
Assuming you have these pages
form.php
processForm.php
displayId.php
Form.php only displays form and sends data via POST to processForm.php.
Within processForm.php you can parse data and issue the redirect to displayId.php with id you want to display in GET parameter.
This way when user refreshes the page (displayId.php) the form data is not processed again.
I know you're trying to do this in Zend Framework but I'm just saying I'm after the same functionality. Just moved everything to ZF and I'm quite disappointed to see that this functionality isn't built in.
I used to have every form submit to process.php which processed all GET, POST requests and then saved the results (like error and success messages) and redirected you to the new place.
If $_SESSION['post_data'] was set, I would $_POST = $_SESSION['post_data']; and then remove it from the session.
This worked great but now I'm gonna need the same in ZF :D As I say... a little disappointed as I don't believe ANYONE wants a dialog to appear asking about resubmitting data.. what the hell does that mean to your enduser? nothing!