Complete Example and Summary
The following is a complete Members controller that integrates every piece of the member authentication system covered in this chapter.
<?php
class Members extends Trongate {
/**
* Display the welcome page.
* Members are redirected here after logging in.
*
* @return void
*/
public function welcome(): void {
$data['view_module'] = 'members';
$data['view_file'] = 'welcome';
$this->templates->public($data);
}
/**
* Log the member out.
*
* @return void
*/
public function logout(): void {
$this->trongate_tokens->destroy();
redirect('member-login');
}
/**
* Display the registration form.
*
* Shows the create account form.
*
* @return void
*/
public function create_account(): void {
$data['view_module'] = 'members';
$data['view_file'] = 'create_account';
$this->templates->public($data);
}
/**
* Process registration form submission.
*
* Validates input, creates trongate_users and members records,
* and redirects to the login page.
*
* @return void
*/
public function submit_create_account(): void {
$this->validation->set_rules('username', 'username', 'required|min_length[3]|max_length[55]|callback_unique_username');
$this->validation->set_rules('email_address', 'email address', 'required|valid_email|max_length[255]|callback_unique_email');
$this->validation->set_rules('password', 'password', 'required|min_length[5]|max_length[35]|callback_password_check');
$this->validation->set_rules('password_repeat', 'password repeat', 'required|matches[password]');
$result = $this->validation->run();
if ($result === true) {
// Create the trongate_users record
$sql = 'INSERT INTO trongate_users (user_level_id, code) VALUES (:user_level_id, :code)';
$params = [
'user_level_id' => 2,
'code' => make_rand_str(32)
];
$this->db->query_bind($sql, $params);
$trongate_user_id = $this->db->insert_id();
// Create the members record
$data['username'] = post('username', true);
$data['email_address'] = post('email_address', true);
$data['password'] = $this->login->hash_password(post('password'));
$data['first_name'] = post('first_name', true);
$data['last_name'] = post('last_name', true);
$data['trongate_user_id'] = $trongate_user_id;
$data['date_created'] = time();
$data['num_logins'] = 0;
$data['last_login'] = 0;
$data['code'] = make_rand_str(16);
$data['ip_address'] = '';
$this->db->insert($data, 'members');
set_flashdata('Your account has been created. Please log in.');
redirect('member-login');
}
// Validation failed - redisplay the form with errors
$data['view_module'] = 'members';
$data['view_file'] = 'create_account';
$this->templates->public($data);
}
/**
* Display the member's account dashboard.
*
* @return void
*/
public function your_account(): void {
$token = $this->trongate_tokens->attempt_get_valid_token(2);
if ($token === false) {
redirect('member-login');
}
$params = ['trongate_user_id' => $token->user_id];
$sql = 'SELECT * FROM members WHERE trongate_user_id = :trongate_user_id';
$member = $this->db->query_bind($sql, $params, 'object');
if (empty($member)) {
redirect('member-login');
}
$data = (array) $member[0];
$data['view_module'] = 'members';
$data['view_file'] = 'your_account';
$this->templates->public($data);
}
/**
* Update the member's password.
*
* @return void
*/
public function update_password(): void {
$token = $this->trongate_tokens->attempt_get_valid_token(2);
if ($token === false) {
redirect('member-login');
}
$this->validation->set_rules('password', 'password', 'required|min_length[5]|max_length[35]|callback_password_check');
$this->validation->set_rules('password_repeat', 'password repeat', 'required|matches[password]');
$result = $this->validation->run();
if ($result === true) {
$hashed_password = $this->login->hash_password(post('password'));
$params = ['trongate_user_id' => $token->user_id];
$sql = 'SELECT id FROM members WHERE trongate_user_id = :trongate_user_id';
$member = $this->db->query_bind($sql, $params, 'object');
$update_id = $member[0]->id;
$this->db->update($update_id, ['password' => $hashed_password], 'members');
set_flashdata('Your password has been updated.');
redirect('members/your_account');
}
$data['view_module'] = 'members';
$data['view_file'] = 'update_password';
$this->templates->public($data);
}
// ──────────────────────────────────────────────
// Validation Callbacks
// ──────────────────────────────────────────────
/**
* Validate unique username.
*/
public function unique_username(string $username): bool|string {
$user_obj = $this->db->get_one_where('username', $username, 'members');
if ($user_obj !== false) {
return 'The username that you submitted is not available.';
}
return true;
}
/**
* Validate unique email address.
*/
public function unique_email(string $email_address): bool|string {
$user_obj = $this->db->get_one_where('email_address', $email_address, 'members');
if ($user_obj !== false) {
return 'This email address cannot be used. Please try a different one.';
}
return true;
}
/**
* Validate password strength.
*/
public function password_check(string $password): bool|string {
if (!preg_match('/[A-Za-z]/', $password)) {
return 'The password must contain at least one letter.';
}
if (!preg_match('/[0-9]/', $password)) {
return 'The password must contain at least one number.';
}
return true;
}
}What Each Method Does
| Method | URL | Purpose |
|---|---|---|
welcome() |
/members/welcome |
Landing page after successful login. Rendered with the public template. |
logout() |
/members/logout |
Destroys session tokens and redirects to the login page. |
create_account() |
/members/create_account |
Displays the registration form. |
submit_create_account() |
/members/submit_create_account |
Processes registration: validates, creates user and member records. |
your_account() |
/members/your_account |
Protected member dashboard. Checks for valid token before display. |
update_password() |
/members/update_password |
Protected password change form. |
unique_username() |
- | Validation callback. Returns error if username is taken. |
unique_email() |
- | Validation callback. Returns error if email is taken. |
password_check() |
- | Validation callback. Enforces letter and number requirements. |
Summary
You now have a complete member authentication system for Trongate v2. The login module handles the heavy lifting - credential validation, token management, rate limiting, and forgot-password flows - while your Members controller provides the user-facing experience.
Key principles to remember:
- The login module is config-driven - configure, do not code.
- Each user level gets its own database table linked via
trongate_users. - Secret login words require matching entries in
custom_routing.php. - Members route through
login/login/{secret_word}- the login module handles authentication. - Administrators get a ready-made login system from the framework's
trongate_administratorsmodule. - Use
attempt_get_valid_token()oris_logged_in()to protect member-only pages. - Always use
$this->login->hash_password()when storing passwords. - Forgot-password requires the
trongate_emailmodule with SMTP configuration.
The complete source code for this chapter is available on GitHub:
https://github.com/grady-trongate/Trongate-v2-Login-System
The companion repository includes config/login.php, config/custom_routing.php, the full members module, and the login.sql database schema - everything you need to deploy a member authentication system on any Trongate v2 project.
We're continually improving the Trongate documentation. If anything is incorrect, unclear, incomplete, or could be better, we'd genuinely appreciate your input.
Share your thoughts in the Documentation Feedback.