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.
Related
In Codeigniter I used to call the view function after posting data. Like below;
Ex: I have a show_products() function which will display list of products. When a user add a new product I'm posting data into add_product() function. If the process is successful I will not redirect to the products page, instead load the display function inside the add_product() like this:
//Inside the add_product() function
if(success){
$this->show_products();
}
I think, there is no point of reloading the page again. Since we are already in the post function we can straight away set the view after the database insert.
However in laravel I see people redirecting after posting data.
ex:
//Inside the postProduct() function
if(success){
return Redirect::to('products');
}
I tried;
//Inside the postProduct() function
if(success){
$this->getIndex();// this is my product display function
}
but it didn't work.
Do we have a benefit by loading the view in the post function without redirecting every time?
If so how can I achieve the same thing with the laravel?
Thanks a lot!
It's not about the Laravel, instead, it's about a good or right way of doing things. In other words, it's a good programming practice to redirect to another route/page after you successfully submit a form.
Even in CodeIgniter or plain Php I do like this approach and encourage other developers to do that. So, the question is why this redirect is better than directly calling another method from the same request to show another view/page ?
This is the life cycle of the process:
You post a form to a route/action page.
You validate the submitted data and upon successful validation you insert the submitted data in to your database, otherwise you redirect back to that form with errors and old user inputs.
So. assume that, you have submitted a form and done saving the data into database successfully. After you save it you done something like this:
return View::make('...')->with('success', 'Data saved!');
In this case, your user can see the success message on the screen but what if the user, presses the f5 key from the keyboard to refresh the page (probably, accidentally), the form will be submitted to the same action again and the whole process will be repeated again.
So, if you had a redirect after form submission then, refreshing the page won't make any request to that form again.
Google search result on form resubmit on refresh., check the links, may be first one/two, you'll get better idea about the problem and the benefits of redirection after form submission.
in Codeigniter to redirect page we have redirect() function.
if(success){
redirect('products');
}
You don't have to return Redirect. The reason people use it quite often in larvel is because it's comfy.
You can return something else, eg. a view:
return View::make('home.index')->with('var',$var);
In Laravel, to redirect after doing a POST, you could return a redirect with a named route:
return redirect()->route('my-route-name');
Or if you are within the controller that has the route method you want (eg. the index method, you could do this as well:
return self::index();
I am working on a basic HTML/PHP form for user registration. It works fine but have a problem I would like to solve. I've noticed that during testing that when I press submit and the passwords don't match, I'm taking to the error page (by design), then redirected back to registration where I have to enter everything all over again. Is there a way to keep the fields populated with the user's input so that they can just go back and correct what needs to be fixed instead of having to re-enter everything all over again?
Best way would be to use AJAX so they never have to leave the page in the first place.
Failing that, using history.back() to send the user back should keep the form info there.
Failing that, save their form data in a $_SESSION variable and use that to repopulate the form.
You can save submitted data in session:
-Init a php session by <?php session_start(); ?>, this function must appear BEFORE the tag
-store a variable in session like this: $_SESSION['myVar']=$myVar;
-retrieve it back (in another page) by : $myVarFromSession = $_SESSION['myVar'];
-Finally, destroy the session and its content : <?php session_destroy(); ?>
Quick — Dirty
Send back the username/password in a query string (?username=...&password=...)
Set SESSION variables before redirecting
Better — Encapsulate Logic into Classes
I strongly recommend encapsulating your various moving parts in classes that handle the request, form, validation, error message, and rendering/routing logic. You will find this infinitely easier than trying to manually throw error messages/data back and forth between scripts. If your site/app is big, or if you want to follow best practices and become a better developer, classes are the way to go.
Take a look at how different frameworks handle this problem. Some good ones are Yii, Laravel, and Symfony2. Right out of the box they'd be able to quickly and easily solve this problem.
Sample Code
class LoginForm
{
public $password;
public $username;
public function validate()
{
// Perform validation
}
}
class HttpRequest
{
public function getIsPostRequest()
{
return 'POST' === $_SERVER['REQUEST_METHOD'];
}
public function getPost($name, $default=null)
{
return isset($_POST[$name]) ? $_POST[$name] : $default;
}
}
// This code processes the request to your login page.
// View::renderFile() renders a "view", which is a mix
// of HTML/PHP code that gets served back to the browser.
$request = new HttpRequest();
$form = new LoginForm();
if ($request->getIsPostRequest()) {
$form->username = $request->getPost('username');
$form->password = $request->getPost('password');
if ($form->validate()) {
// Login the user...
}
}
View::renderFile('login.php', array(
'form' =>$form,
));
Storing the user's password in the session or passing it in the query parameters is a security no-no. You should display the error on the same form page to avoid redirecting and losing that data. That way the error is still displayed and the script still has the user's input data from the $_POST. Of course, Dan Schmidt's recommendation to use a framework is excellent. The purpose of the framework is to save you from the headache you're experiencing right now.
If you insist on redirecting to an error page, then you can store the username in the session, but I highly recommend against storing the password as mentioned before.
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!