How can I add custom user attributes such as first name, last name, etc. to Codeigniter 4 Shield and process them correctly?
I have already looked in the docs, where unfortunately there is only a non-descriptive description. (https://codeigniter4.github.io/shield/customization/) and if this is only designed for more login attributes. But the documentation does not describe where and how I properly process the data in the newly created user model. Be it also query then via auth() etc..
There are many conversation on this topic and several ways you can implement this. Some have expanded the users table, some the auth_identities table. I chose to create a seperate user_details table. Either method is fine and purely based on your development style and application needs.
Here is my table, it has a lot more than this, but you get the idea: I set a Key to the auth_identities table in case the user is removed.
CREATE TABLE `user_details` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT(11) UNSIGNED NOT NULL,
`firstname` VARCHAR(255) NOT NULL COLLATE 'utf8_general_ci',
`middlename` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_general_ci',
`lastname` VARCHAR(255) NOT NULL COLLATE 'utf8_general_ci',
`phone` VARCHAR(255) NOT NULL COLLATE 'utf8_general_ci',
`address1` VARCHAR(255) NOT NULL COLLATE 'utf8_general_ci',
`address2` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_general_ci',
`city` VARCHAR(255) NOT NULL COLLATE 'utf8_general_ci',
`state` VARCHAR(255) NOT NULL COLLATE 'utf8_general_ci',
`zip` INT(11) NULL DEFAULT '0',
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE,
INDEX `user_id` (`user_id`) USING BTREE,
CONSTRAINT `auth_identities_user_id_fk2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1;
In my Controller, I have the following as a view for the user via Ajax:
public function view($id = null) {
if (!$this->user->hasPermission('users.read')) {
throw new \CodeIgniter\Exceptions\PageNotFoundException();
}
$user = $this->usersModel->where('id', $id)->first();
$permission = $user->getPermissions();
$identity = $user->getEmailIdentity();
$data = [
'title' => 'View Users',
'user' => $user,
'details' => $this->detailsModel->where('user_id', $id)->first(),
'group' => roleName($id),
'pTable' => buildPermissionsTable($permission),
'identity' => $identity->secret
];
return view('users/view', $data);
}
The "roleName" and "buildPermissionsTable" are in a users helper file as I only give one role to the users where Shield allows multiple. I assign each user permissions based on Controller.Method IE: users.create or users.update or users.delete. This helps me to prevent group overlap or creating several different groups when I only need to assign a specific level of access.
Updating the user is the same as above, sort the data from the posted user form in the controller and insert into the appropriate table. You can find more on that here:
https://codeigniter4.github.io/shield/quickstart/#editing-a-user
You put everything in the users and auth_identities tables first, then the rest in the user_details table, basic example below:
The below taken from the documentation above, Make sure you are using the right Model before your class name:
use CodeIgniter\Shield\Models\UserModel;
Example to modify the data in users and auth_identities tables slightly modified from the above documentation link (obviously it's not complete):
public function edit($id = null) {
if (!$this->user->hasPermission('users.update')) {
throw new \CodeIgniter\Exceptions\PageNotFoundException();
}
$id = $this->request->getPost('id');
$originalDate = Time::now('America/Chicago', 'en_US');
$newDateString = $originalDate->format('Y-m-d H:i:s');
// Add all of your post value here...
// $authFields are for Shield
// $detailFields are for user_details table
$authFields['username'] = $this->request->getPost('username');
$authFields['status'] = $this->request->getPost('status');
$authFields['status_message'] = $this->request->getPost('status_message');
$authFields['updated_at'] = $newDateString;
$detailFields['firstname'] = $this->request->getPost('firstname');
$detailFields['lastname'] = $this->request->getPost('lastname');
$detailFields['phone'] = $this->request->getPost('phone');
// Snip... You get the idea from here...
$this->validation->setRules([
// check validation rules for everything...
]);
if ($this->validation->run($fields) == FALSE) {
//Show Error in Input Form
} else {
// Get the User Provider (UserModel by default)
$users = model('UserModel'); //or auth()->getProvider();
$user = $users->findById($id);
$user->fill($authFields);
if ($users->save($user)) {
// update data in the user_details table
} else {
// show error message
}
}
}
Hope this helps!