Home > Software engineering >  CodeIgniter 4: Call to member function on null where post request is valid in var_dump()
CodeIgniter 4: Call to member function on null where post request is valid in var_dump()

Time:06-07

** Very new to CodeIgniter so please be kind! **

I have an issue with my two user authentication forms: users/register.php and users/login.php where I cannot pass the post input to functions in my model.

As of now, I'm getting the error Call to member function addUser() on null on the registration form and a validation error on the login form that states the username/password don't match any credentials in the database. Both seem to stem from post being null although it is not.

I have done a var_dump on $login which is defined as $login = $this->request->getPost() as well as inspected the request in Firefox Developers Browser to find all the post data correctly displayed. I am stumped. Why can't I pass this array to my model?

Here is a screenshot of the post request for login.php (the same can be said for registration.php and is not included).

Screenshot showing inspector mode in Firefox Developer Browser.

These are my routes:

// Login and Registration
$routes->match(['get', 'post'], 'users/register', 'Users::register');
$routes->match(['get', 'post'], 'users/login', 'Users::login', ["filter" => "noauth"]);

Here is my model UserModel.php in its entirety:

class UserModel extends Model
{
    protected $DBGroup          = 'default';
    protected $table            = 'users';
    protected $primaryKey       = 'username';
    protected $useAutoIncrement = false;
    protected $insertID         = 0;
    protected $returnType       = 'object';
    protected $useSoftDelete    = false;
    protected $allowedFields    = [
        'username',
        'password',
        'id',
        'role',
        'profile_image',
        'profile_views',
        'last_login',
        'about_me',
        'age',
        'gender',
        'occupation',
        'hometown',
        'country',
        'fav_shape',
        'fav_color',
        'created',
        'modified',
    ];

    // Dates
    protected $useTimestamps    = true;
    protected $dateFormat       = 'datetime';
    protected $createdField     = 'created';
    protected $modifiedField    = 'modified';

    // Callbacks
    protected $allowCallbacks   = true;
    protected $beforeInsert     = ['beforeInsert'];

    public function __construct() 
    {
        parent::__construct(); 
    }

    protected function beforeInsert(array $data)
    {
        $data = $this->passwordHash($data);

        return $data;
    }

    protected function passwordHash(array $data)
    {
        if (isset($data['password'])) {

            $data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
        }

        return $data;
    }

       public function lastLogin($username)
    {
        $this->builder()
            ->where('username', $username)
            ->update('last_login', date('Y-m-d H:i:s'));
    }

    public function addUser($newUser)
    {
        $builder = $this->builder()
            ->set($newUser)
            ->insert();

        if ($builder->affected_rows() == 1) {

            return TRUE;
        
        } else {

            return FALSE;
        }
    }

    public function getUser($username)
    {
        $builder = $this->builder()
            ->where(['username' => $username])
            ->limit(1);

        if ($builder->countAllResults() === 1) {
            
            return $builder->get()->getRow();
        
        } else {

            return FALSE;
        }
    }
}

Here are excerpts from my controller Users.php:

class Users extends BaseController
{
    protected $userModel;

    public function __construct() 
    {
        $userModel = new UserModel();
    }

    public function login()
    {
        $validation = \Config\Services::validation();

        // Set session variable
        $session = session();

        if ($this->request->getMethod() === 'post' && ! empty($_POST)) {   
            $validation->getRuleGroup('login');
            $validation->setRuleGroup('login');
            $validation->withRequest($this->request)->run(); 

            $recaptchaResponse = trim($this->request->getVar('g-recaptcha-response'));

            $userIp = $this->request->getIPAddress();

            $secret = env('recaptcha2_secretkey');

            $credential = [
                'secret'    => $secret,
                'response'  => $recaptchaResponse,
                'remoteip'  => $userIp,
            ];

            $verify = curl_init();

            curl_setopt($verify, CURLOPT_URL, 'https://www.google.com/recaptcha/api/siteverify');
            curl_setopt($verify, CURLOPT_POST, TRUE);
            curl_setopt($verify, CURLOPT_POSTFIELDS, http_build_query($credential));
            curl_setopt($verify, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($verify, CURLOPT_RETURNTRANSFER, TRUE);

            $response = curl_exec($verify);

            $status = json_decode($response, TRUE);

            curl_close($verify);

            if (empty($validation->getErrors()) && $status['success']) {
                $login = $this->request->getPost();

                $user = $this->userModel->getUser($login['username']);

                // Storing session values
                $this->setUserSession($user);

                // Storing success message
                $session->setFlashdata('success', 'You have successfully logged in!');

                // Update last login datetime
                $this->userModel->lastLogin($login['username']);

                // Redirecting to dashboard after login
                if ($user['role'] == 1) {

                    return redirect()->to('admin/dashboard');
                
                } elseif ($user['role'] == 0) {

                    return redirect()->to('members/dashboard');
                }
            } else {

                $data = [
                    'title'     => 'Login',
                    'errors'    => $validation->getErrors(),
                ];

                echo view('templates/index_header', $data);
                echo view('users/login');
                echo view('templates/footer', $data);
            }   
        } else {
            $data = [
                'title' => 'Login',
            ];

                echo view('templates/index_header', $data);
                echo view('users/login');
                echo view('templates/footer', $data);  
        }
    }

    /**
     * Sets session with user id, username, isLoggedIn, and role for use in member/admin site
     * @param model user data
     * @return boole if session was set successfully
     */
    private function setUserSession($user)
    {
        $data = [
            'id' => $user->id,
            'username' => $user->username,
            'profile_image' => $user->profile_image,
            'isLoggedIn' => true,
            'role' => $user->role,
        ];

        if (session()->set($data)) {
            
            return true;
        
        } else {

            return false;
        }
    }

    public function register() 
    {
        $validation = \Config\Services::validation();

        if ($this->request->getMethod() == 'post' && ! empty($_POST)) {

            $validation->getRuleGroup('registration');
            $validation->setRuleGroup('registration');
            $validation->withRequest($this->request)->run(); 

            if (empty($validation->getErrors())) {

                $newUser = $this->request->getPost();

                if ($this->userModel->addUser($newUser)) {

                    $this->session->setFlashdata('success', 'Successful Registration');

                    $data['title'] = 'Login';

                    echo view('templates/index_header', $data);
                    echo view('users/login');
                    echo view('templates/footer', $data);

                } else {

                    $this->session->setFlashdata('error', 'Something went wrong with your registration! Please try again.');
                }

            } else {

                $data = [];

                $data = [
                    'title'     => 'Register',
                    'script'    => 'js/click_link',
                    'errors'    => $validation->getErrors(),
                ];
                
                echo view('templates/index_header', $data);
                echo view('users/register', $data);
                echo view('templates/footer', $data);
            }
        } else {
            $data = [
                'title'         => 'Register',
                'script'        => 'js/click_link',
            ];
        
            echo view('templates/index_header', $data);
            echo view('users/register', $data);
            echo view('templates/footer', $data);
        }
    }
}

These are my validation rules in Config\Validation:

/**
     *  Registration
     */
    public $registration = [
        'username'      => 'required|is_unique[users.username,username]|min_length[5]|max_length[25]|alpha_dash|badWordsFilter[username]',
        'password'      => 'required|min_length[8]|max_length[255]|regex_match[/^(?=.*[!@#$%^&*-])(?=.*[0-9])(?=.*[A-Z]).{8,255}$/]',
        'pass_confirm'  => 'required|matches[password]',
        'about_me'      => 'permit_empty|max_length[250]|alpha_numeric_punct|badWordsFilter[about_me]',
        'occupation'    => 'permit_empty|max_length[50]|alpha_space|badWordsFilter[occupation]',
        'hometown'      => 'permit_empty|max_length[50]|alpha_space|badWordsFilter[hometown]',
        'age'           => 'permit_empty|less_than[100]|greater_than[0]|numeric',
        'country'       => 'permit_empty',
    ];
/**
     *  Password Verification
     */
    public $login = [
        'password'      => 'required|validateUser[username,password]',
    ];

This is my custom rule to authenticate username and password credentials User_rules:

class User_rules 
{
    /**
     * Checks if input username exists in database and then checks whether the input password matches the hash for that username
     * @param string $str is the input password
     * @param string $fields are the associated form fields that are being used
     * @param array $data is an array containing the values for the fields indexed by field names
     * @return boolean true or false depending on if the user exists and the password matches the hashed password stored in the database
     */
    public function validateUser(string $str, string $fields, array $data)
    {
        $userModel = new UserModel();

        $user = $userModel->getUser($data['username']);

        if(!$user) {

            return FALSE;
        }

        return password_verify($data['password'], $user->password);
    }

Lastly, my view for login.php:

<div class='form-container'>

    <?= form_open('users/login',['autocomplete' => FALSE]); ?>

        <div class='form-header'>
            <h2>Login</h2>
        </div>
        
        <div class='form-body'>

            <div class='form-row'>
                <div class='input-container'>
                    <i class='fas fa-user'></i>

                    <?php $attributes = [
                        'type'      => 'text',
                        'name'      => 'username',
                        'class'     => 'input-field',
                        'id'        => 'username',
                        'placeholder'   => 'Username',
                        'required'      => TRUE,
                    ]; ?>
                    <?= form_input($attributes); ?>
                
                </div>
            </div>

            <div class='form-row'>
                <div class='input-container'>
                    <i class='fas fa-lock'></i>

                    <?php $attributes = [
                        'type'          => 'password',
                        'name'          => 'password',
                        'class'         => 'input-field',
                        'placeholder'   => 'Password',
                        'required'      => TRUE,
                    ]; ?>
                    <?= form_input($attributes); ?>
                  
                </div`>
            </div>
        </div>
                
        <div class='captcha-container'>
            <div class='g-recaptcha' data-sitekey='<?= env('recaptcha2_sitekey'); ?>'></div>
        </div>

        <div class='form-footer'>

            <?php $submit = [
                    'name'      => 'loginSubmit',
                    'value'     => 'Login',
                    'class'     => 'submit-btn',
            ];?>
            <?= form_submit($submit); ?>
    
        </div>

        <h4 style='text-align: center'>Not a member yet? Register 
            <a href= <?= site_url('users/register'); ?> title = 'Register'> HERE</a>
        </h4>

     <?= form_close(); ?>
</div>

CodePudding user response:

It was a stupid mistake. Someone on the codeigniter forum answered my question here: CodeIgniter Forum

basically in my constructor I needed $this->userModel = new UserModel(); instead of $userModel = new UserModel();.

  • Related