Trongate Way Docs

Registration and Account Management

Now that members can log in and log out, we need to give them the ability to create accounts and manage their profiles. This page covers registration, form validation, account updates, and password changes.

Creating a Member Registration Flow

A registration system creates a new user in the trongate_users table, inserts a corresponding record in the members table with a bcrypt-hashed password, and optionally sends a confirmation email.

Add a create_account() method to your Members controller:

PHP
/**
 * Create a new member account.
 *
 * Validates the submitted fields, creates the trongate_users
 * record and the members record, and redirects to the login page.
 *
 * @return void
 */
public function 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);
}

Validation Callbacks

The registration form uses custom validation callbacks to check unique fields and password strength. These are methods on your Members controller:

PHP
/**
 * Validate that the username is available.
 *
 * @param string $username The username to check.
 * @return bool|string True if available, error message if taken.
 */
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 that the email address is available.
 *
 * @param string $email_address The email address to check.
 * @return bool|string True if available, error message if taken.
 */
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.
 *
 * Requires at least one letter and one number.
 *
 * @param string $password The password to validate.
 * @return bool|string True if strong enough, error message if not.
 */
public function password_check(string $password): bool|string {

    // Ensure the validation module can use callback methods on
    // a different controller than the one receiving the request.
    $this->module('login');

    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;
}

The password_check() callback is invoked by Trongate's validation module automatically. Notice the pattern: the method name in the validation rule (callback_password_check) corresponds to a method on the current controller.

Hash Password Safely

Always use the login module's hash_password() method rather than calling password_hash() directly. This ensures the configured bcrypt cost factor is applied consistently across your application:

PHP
$hashed = $this->login->hash_password(post('password'));

The hash_password() method reads the password_hash_cost from your config/login.php and applies it with PASSWORD_BCRYPT.

Account Management

Once logged in, members typically need to update their profile and change their password. Here are the patterns for each:

Updating Account Details

PHP
/**
 * Display the account update form.
 *
 * Protected  -  only accessible to authenticated members.
 *
 * @return void
 */
public function your_account(): void {
    $member_level_id = 2;
    $token = $this->trongate_tokens->attempt_get_valid_token($member_level_id);

    if ($token === false) {
        redirect('member-login');
    }

    // Fetch the member's data
    $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');
    }

    $member = $member[0];
    $data = (array) $member;
    $data['view_module'] = 'members';
    $data['view_file'] = 'your_account';
    $this->templates->public($data);
}

Changing the Password

PHP
/**
 * Update the member's password.
 *
 * Validates the new password, hashes it, and updates the database.
 *
 * @return void
 */
public function update_password(): void {
    $member_level_id = 2;
    $token = $this->trongate_tokens->attempt_get_valid_token($member_level_id);

    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');
    }

    // Validation failed
    $data['view_module'] = 'members';
    $data['view_file'] = 'update_password';
    $this->templates->public($data);
}

Forgot Password Flow

The forgot-password flow is handled entirely by the login module. Enable it by setting enable_forgot_password to true in your configuration. When a member visits login/forgot_password/member-login, the login module:

  1. Prompts the member for their email address.
  2. Generates a time-sensitive reset token and stores it in the database.
  3. Sends an email with a reset link.
  4. Validates the reset link when the member clicks it.
  5. Allows the member to set a new password.
  6. Destroys all existing authentication tokens.

This flow requires the trongate_email module with SMTP credentials. Create config/trongate_email.php:

PHP
<?php
$config['trongate_email'] = [
    'smtp_host' => 'mail.yourdomain.com',
    'smtp_port' => 465,
    'smtp_user' => '[email protected]',
    'smtp_pass' => 'your_password',
    'smtp_secure' => 'ssl',
    'smtp_from_name' => 'Your Website'
];

The forgot-password feature is optional per level. In our configuration, administrators do not have it enabled, but members do - a common production pattern.

Key Points

  • Every user needs a trongate_users record before their per-level record.
  • Always use $this->login->hash_password() - never hash passwords manually.
  • Use custom validation callbacks (callback_method_name) for unique field checks.
  • Protect account management pages by checking for a valid token.
  • The forgot-password flow is built into the login module - you just need to enable it and configure SMTP.

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.

Leave Feedback About This Page