Welcome Guest, Not a member yet? Register   Sign In
CodeIgniter 4 Model Callbacks not hashing password before insert
#1

Hi,
I am totally new to CI4 but do have some experience with PHP. I having trouble getting the Model class callback functions to work when inserting a new user row.
Here is the Controller:
Code:
<?php
namespace App\Controllers;

use App\Models\UserModel;

class Users extends BaseController
{
public $users_model;

public function __construct() {
$this->users_model = new UserModel();
}

public function register() {
$data = [
'page_title' => 'Register An Account',
'validation' => NULL //must have this set to null until/if any validation rules are broken
];

if ($this->request->getMethod() == 'post') {

//validation rules
$rules = [
'username' => [
'rules' => 'required|min_length[4]|max_length[20]|validateUsername[username]',
'errors' => [
'required' => 'A username is required',
'min_length' => 'Username must be at least {param} characters long',
'max_length' => 'Username cannot be more than {param} characters long',
'validateUsername' => 'Username can only contain letters and numbers',
],
] ,
'email' =>  [
'rules' => 'required|valid_email|is_unique[users.email]',
'errors' => [
'required' => 'An Email is required',
'valid_email' => 'Enter a valid email',
'is_unique' => 'That email has already been registerd',
],
],
'password' => [
'rules' => 'required|min_length[6]|max_length[16]|validatePassword[password]',
'errors' => [
'required' => 'A password is required',
'min_length' => 'Password must contain at least {param} characters',
'max_length' => 'Password cannot be more than {param} characters in length',
'validatePassword' => 'Password must have at least 1 numeric value',
],
],
'confirm_password' => [
'rules' => 'required|matches[password]',
'errors' => [
'required' => 'Must confirm password',
'matches' => 'Passwords do not match'
],
]

];

if ($this->validate($rules)) {
//all fields passed validation so need to save to the db
$user_data = [
'username' => $this->request->getVar('username', FILTER_SANITIZE_STRING),
'email' => $this->request->getVar('email', FILTER_SANITIZE_EMAIL),
'password' => $this->request->getVar('password')
];
if ($this->users_model->createUser($user_data)) {
echo 'user stored in the db.';
} else {
echo 'user not stored in the db.';
}

} else {
//there are some validation errors
$data['validation'] = $this->validator;
}



}//post request check ends here

return view('users/register', $data);
}// register method ends here.

public function login() {
$data = [
'page_title' => 'Login'
];
return view('users/login', $data);
}

public function logout() {
//not implemented yet
}
}
And here is the Model class:
Code:
<?php
namespace App\Models;

use CodeIgniter\Model;

class UserModel extends Model
{
protected $table = 'users';
protected $allowedFields = ['username', 'email', 'password'];
protected $beforeInsert = ['beforeInsert'];
protected $beforeUpdate = ['beforeUpdate'];
protected $allowCallbacks = TRUE;
protected $builder;

public function createUser(array $data) {
$this->builder = $this->db->table($this->table);
$this->db->transStart();
$this->builder->insert($data);
$this->db->transComplete();
if($this->db->affectedRows() == 1) {
return TRUE;
} else {
return FALSE;
}
}


protected function beforeInsert(array $data) {
if (isset($data['data']['password']))
$data['data']['password'] = password_hash($data['data']['password'], PASSWORD_DEFAULT);
        return $data;
}

protected function beforeUpdate(array $data) {
if (isset($data['data']['password']))
$data['data']['password'] = password_hash($data['data']['password'], PASSWORD_DEFAULT);
        return $data;
}
}
What is weird to me is that the record does get stored in the database but the password is plain text and not hashed. Also, in the controller class, the if statement:
Code:
if ($this->users_model->createUser($user_data)) {
echo 'user stored in the db.';
} else {
echo 'user not stored in the db.';
}
Always echo's the 'user not stored in the db' message and the user is stored in the db.
In the model:
Code:
if($this->db->affectedRows() == 1) {
return TRUE;
} else {
return FALSE;
}
I have echoed out the result of
Code:
$this->db->affectedRows()
and it is 1 on a successful insert (although the password is not hashed) so I thought this would cause the method to return true, therefore over in the controller, I would see the truthy part of the if condition, 'user stored in the db'.
Can anyone tell me where I am going wrong in all of this?
I have successfully used the
Code:
password_hash()
function in the controller itself on the
Code:
$this->request->getVar('password')
data so I know it works. But I wanted to leverage the in-built Model callbacks and take care of the hashing for inserts and updates there.
Thanks for any help.
Reply
#2

At first, the $allowCallbacks model property is enabled by default. 

For events to work, you need to use defined methods like insert / update / save / etc.

$model->insert($yourData);

But you create your own createUser method and work directly with the builder. Of course, event triggers will not work in this case.
They need to be called manually.
Reply
#3

(01-02-2021, 09:41 AM)iRedds Wrote: At first, the $allowCallbacks model property is enabled by default. 

For events to work, you need to use defined methods like insert / update / save / etc.

$model->insert($yourData);

But you create your own createUser method and work directly with the builder. Of course, event triggers will not work in this case.
They need to be called manually.

Hi and thanks for the response. So, when you say the event triggers need to be called manually, does that mean I need to call them in the createUser() method like $this->db->beforeInsert($data)?
Thanks
Reply
#4

(01-02-2021, 11:15 AM)josh2112o Wrote: Hi and thanks for the response. So, when you say the event triggers need to be called manually, does that mean I need to call them in the createUser() method like $this->db->beforeInsert($data)?
Thanks

PHP Code:
// 1 way 

$this->db->transStart();
$this->insert($data); // call default method with events
//or $this->save($data); If $data have key equal $this->primaryKey will be called update method else insert method
$this->db->transComplete();

// 2 way 
$this->db->transStart();
$eventData $this->trigger('beforeInsert', ['data' => $data]); // will call all methods specified in $this->beforeInsert
// or call your specific method directly $eventData = $this->beforeInsert(['data' => $data]);
$this->db->insert($eventData['data']); 
$this->db->transComplete(); 

IMHO if you make one request in the database, then it makes no sense to use transactions
Reply
#5

Also keep in mind that password_hash is expecting a string parameter, may cause problems passing arrays.
What did you Try? What did you Get? What did you Expect?

Joined CodeIgniter Community 2009.  ( Skype: insitfx )
Reply
#6

(01-02-2021, 09:24 PM)iRedds Wrote:
(01-02-2021, 11:15 AM)josh2112o Wrote: Hi and thanks for the response. So, when you say the event triggers need to be called manually, does that mean I need to call them in the createUser() method like $this->db->beforeInsert($data)?
Thanks

PHP Code:
// 1 way 

$this->db->transStart();
$this->insert($data); // call default method with events
//or $this->save($data); If $data have key equal $this->primaryKey will be called update method else insert method
$this->db->transComplete();

// 2 way 
$this->db->transStart();
$eventData $this->trigger('beforeInsert', ['data' => $data]); // will call all methods specified in $this->beforeInsert
// or call your specific method directly $eventData = $this->beforeInsert(['data' => $data]);
$this->db->insert($eventData['data']); 
$this->db->transComplete(); 

IMHO if you make one request in the database, then it makes no sense to use transactions

Hi thanks again for the help.
I implemented the first suggestion by calling $this->insert($data) in the createUser($data) method I had created in the UserModel class and it works as expected. The password was hashed automatically by the $beforeInsert method. Thanks again for the help.
I also had posted this same issue over on StackOverflow.
The post is here and if you are are active on that site and want to answer there, I can mark it as accepted over there as well.
Reply
#7

(01-02-2021, 09:45 PM)InsiteFX Wrote: Also keep in mind that password_hash is expecting a string parameter, may cause problems passing arrays.
Thank you for the information and best practices suggestion. I guess I hadn't thought it would be any trouble if passing in the associative array key to password_hash() that it might cause issues.
Thanks again.
Reply
#8

Also be careful on the user inputting null values there have been problems with it in password_hash,
best bet is to read up on it on PHP.net
What did you Try? What did you Get? What did you Expect?

Joined CodeIgniter Community 2009.  ( Skype: insitfx )
Reply




Theme © iAndrew 2016 - Forum software by © MyBB