-1

I have a login page using method POST (no ajax), it works properly. But when I press back button after login it says "Confirm form re submission"

How can I fix this in CodeIgniter??

Kallol Medhi
  • 457
  • 1
  • 5
  • 17
  • this could help you: https://stackoverflow.com/questions/3923904/preventing-form-resubmission – Vickel Sep 13 '19 at 11:15
  • @Vickel my problem is different. it only shows resubmission when i press back, when i refresh it works fine – Kallol Medhi Sep 13 '19 at 12:21
  • does your login page redirect? hard to tell without any code here. atleast post your controller and relevant login code. – Alex Sep 13 '19 at 20:16

1 Answers1

0

You can do this by using the Post/Redirect/Get Pattern.

The simple explanation of this pattern is that you Post to a controller/method that processes the inputs and which then Redirects to another controller/method. Redirects always produce a Get request.

For your needs, the login form (at user/login) could use an action like `user/process_login'

// in the "user" controller
public function process_login
{
    // get inputs and validate them
    $valid = //validated inputs and username + password all are good - or not
    if($valid){
        redirect('user/home_page', 'refresh', 303);
    }

    redirect('user/login', 'refresh', 303);
}

The complicating factor is if you want to repopulate the form inputs and/or show validation error messages. CodeIgniter makes that a bit more complicated because you will need to use a session in order to retrieve those things after being redirected back to 'user/login'.

My solution is a custom class that extends CI_Form_validation. I use it for all form processing in my projects. The file has a lot of comments that I hope provide an explanation of what's happening.

file: /application/libraries/MY_Form_validation.php

<?php
/**
 * Extends CI_Form_validation to allow using the Post/Redirect/Get form processing pattern. 
 * https://en.wikipedia.org/wiki/Post/Redirect/Get 
 * provides a basic overview of the pattern.
 * 
 * A more comprehensive examination is found at
 * http://www.theserverside.com/news/1365146/Redirect-After-Post
 * 
 * IMPORTANT: To use this class CodeIgniter's Session class must be setup and working.
 * 
 * HOW THIS WORKS
 * 
 * The base class (CI_Form_validation) has the protected property $_field_data.
 * That property holds all the information supplied by CI_Form_validation::set_rules() 
 * and all the results gathered by CI_Form_validation::run(). 
 * 
 * $_field_data is the key to re-populating a <form> and showing validation error messages.
 * This class stores $_field_data in session flash data. 
 * 
 * MY_Form_validation::has_failed_validation() is used in the redirect target 
 * to restore $_field_data thereby making it possible to re-populate a <form> and
 * to display error messages after a redirect. 
 * 
 * This class is also a handy place for defining application specific (custom) 
 * validation methods. These methods will NOT require the "callback_" prefix and 
 * can be used just like "standard" CodeIgniter validation methods. 
 * Simply define custom validation methods in this file as needed.
 *  
 * There are two examples of custom methods below - valid_user() and valid_password().
 *
 */
defined('BASEPATH') OR exit('No direct script access allowed');

class MY_Form_validation extends CI_Form_validation
{
    public function __construct($rules = array())
    {
        parent::__construct($rules);
        $this->CI->load->library('session');
    }

    /**
     * run($group = '')
     * 
     * Overrides CI_Form_validation::run() 
     * Sets a $_SESSION item with the contents of $this->_field_data 
     * if CI_Form_validation::run() returns FALSE.
     * 
     * @param string $group The name of the validation group to run
     * @return boolean TRUE on success and FALSE on failure
     */
    public function run($group = '')
    {
        if(parent::run($group))
        {
            return TRUE;
        }

        $_SESSION['validation_field_data'] = $this->_field_data;
        $this->CI->session->mark_as_flash('validation_field_data');
        return FALSE;
    }

    /**
     * has_failed_validation()
     * 
     * Determine if CI_Form_validation::run() returned false.
     * This method should be used in the controller/method that is the 
     * failed validation redirect target. In other words, 
     * used in the method that displays the form.
     * 
     * A return of FALSE should be taken as an indication that no attempt has been 
     * made to validate fields yet.
     * 
     * This method also restores the $_field_data property using session data,
     * 
     * @return boolean TRUE if CI_Form_validation::run() returned false, 
     *                 otherwise it returns FALSE.
     */
    public function has_failed_validation()
    {
        if(isset($_SESSION['validation_field_data']))
        {
            // Validation->run() has failed (returned false). 
            // Restore the $_field_data property stored in session data. 
            $this->_field_data = $_SESSION['validation_field_data'];
            return TRUE;
        }

        return FALSE;
    }

    /**
     * capture()
     * 
     * The CI_Form_validation class does not save $_POST data for an <input> unless 
     * it has a rule. This validation method forces the "capture" of a field's 
     * $_POST data by giving the field a rule that always returns true.
     * 
     * Use the "capture" rule on fields that don't otherwise need validation 
     * but you would still like to be able to use set_value('field_name') to 
     * re-populate the field on a <form>.
     * 
     */
    public function capture()
    {
        return TRUE;
    }

    /*
     * The next two methods have been added to demonstrate how custom validation 
     * methods are easily defined for application specific needs. 
     * 
     * These two are not to be taken as anything but super-trivial, demo-only code. 
     * They provide a no-fuss, hard-coded way to validate the user_name and 
     * password fields for the Pgr_example.
     * 
     * These should be removed in your implementation.
     * 
     */

    /**
     * A "custom" validation method for the example app
     */
    public function valid_user($str)
    {
        if($str === 'Foo')
        {
            return TRUE;
        }

        //Don't really want to tell them the User Name is wrong, so use an empty string
        $this->set_message('valid_user', '');
        return FALSE;
    }

    /**
     * Another "custom" validation method for the Example app
     */
    public function valid_password($str)
    {
        if($str === "Bar")
        {
            return TRUE;
        }

        //Don't really want to tell them the pasword is wrong, so we use an empty string
        $this->set_message('valid_password', '');
        return FALSE;
    }

}

Prg_example.php is a controller to demonstrate using MY_Form_validation to implement the Post/Redirect/Get pattern

<?php
/**
 * A controller to demonstrate usage of MY_Form_validation
 * The index() method shows a typical "Sign In" screen and 
 * uses two custom rule validation methods 
 * defined in the version of MY_Form_validation shown above. 
 * 
 * index() also makes an additional "message" available to the 
 * "view" when sign in fails.
 * 
 */
defined('BASEPATH') OR exit('No direct script access allowed');

class Prg_example extends CI_Controller
{
    public function __construct()
    {
        parent::__construct();
        $this->load
            ->library('form_validation') //Do you know that 'form_validation' loads the Form helper?  It's true.
            ->helper('url');
        $this->form_validation->set_error_delimiters('<span class="error">', '</span>');
    }

    /**
     * Presents a typical Sign In screen. 
     * I have put the HTML for the page here instead of using a "view" file. 
     * Yes, that breaks the MVC pattern, but this IS only an example.
     */
    public function index()
    {
        //Here we use the return to set the extra "message" for the view
        if($this->form_validation->has_failed_validation())
        {
            $message = $this->session->message;
        }

        $usernameField = array(
            'name' => 'username',
            'label' => 'User Name',
            'placeholder' => "Foo is correct",
            'value' => $this->form_validation->set_value('username'),
        );

        $passwordField = array(
            'name' => 'password',
            'label' => 'Password',
            'placeholder' => "Bar is correct",
            'value' => $this->form_validation->set_value('password'),
        );
        ?>

        <!DOCTYPE html>
        <html>
            <head>
                <title>Login Example</title>
                <style>
                    div{ padding-top: .5em; }
                    .error {color: red; }
                </style>
            </head>
            <body>
                <h4>Sign In</h4>
                <span class="error">*</span>Required
                <?= form_open('prg_example/login_process'); ?>
                <div>
                    <label>User Name:<span class="error">*</span></label>
                    <div>
                        <?php
                        echo form_input($usernameField);
                        echo form_error($usernameField['name']);
                        ?>
                    </div>
                </div>
                <div>
                    <label>Password:<span class="error">*</span></label>
                    <div>
                        <?php
                        echo form_password($passwordField);
                        echo form_error($passwordField['name']);
                        ?>
                    </div>
                </div>
                <div>
                    <?= form_submit('submit', 'Submit'); ?>&nbsp;
                    <span class="error"><?= isset($message) ? $message : ""; ?></span>
                </div>
                <?= form_close(); ?>

            </body>
        </html>
        <?php
    }

    /**
     * The "action" for the Sign In form is the POST part of PRG
     */
    public function login_process()
    {
        //A custom error message for required fields
        $required_err = ['required' => '<em>{field}</em> required.'];

        $validation_rules = [
            [
                'field' => 'username',
                'label' => 'User Name',
                 // Cascading multiple rules 'piped' together in a string
                'rules' => 'trim|required|valid_user',
                // An error message is needed for the valid_user() method, 
                // but we don't actually want to display anything.
                'errors' => $required_err
            ],
            [
                'field' => 'password',
                'label' => 'Password',
                //Cascading multiple rules using an array
                'rules' => ['trim', 'required', 'valid_password'],                     
                'errors' => $required_err
            ]
        ];

        $this->form_validation->set_rules($validation_rules);

        if($this->form_validation->run())
        {
            // Hurray! Valid fields. 
            // Do other processing here as required

            // GET the success page via REDIRECT
            redirect('prg_example/login_success', 'refresh', 303);
        }

        //Oops, failed. Setup an additional message for the login screen
        $_SESSION['message'] = "Log in Failed";
        $this->session->mark_as_flash('message');

        // GET the sign in page
        redirect('prg_example', 'refresh', 303);
    }

    /**
     * A place to go when login() succeeds
     */
    public function login_success()
    {
        echo "In a real app you would be logged in now";
    }

}
DFriend
  • 8,869
  • 1
  • 13
  • 26