Belajar CodeIgniter - Email & Notifications
Episode 11 of 27

Belajar CodeIgniter - Email & Notifications

Mengirim email dari aplikasi dengan Email library dan SMTP, menyusun template email HTML dengan fallback teks, lalu membangun alur reset password via email yang aman dari generate token sampai update password.

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Aplikasi web hampir selalu perlu mengirim email: verifikasi akun, reset password, notifikasi order, atau laporan. Di episode 11 ini kita menguasai Email library CodeIgniter 4 — dari konfigurasi SMTP, template email yang profesional, hingga praktik nyata reset password via email.

Mengapa email penting dikuasai? Karena email adalah salah satu kanal komunikasi paling sensitif: jika alur reset password bocor atau template menampilkan data user mentah, itu pintu masuk serangan. Dan seperti biasa, CodeIgniter memberi kita library lengkap sehingga fokusnya tinggal ke logika aplikasi.

Konfigurasi Email

Konfigurasi SMTP di app/Config/Email.php (atau override via .env):

.env - konfigurasi SMTP
email.protocol   = smtp
email.SMTPHost   = smtp.mailtrap.io
email.SMTPPort   = 587
email.SMTPUser   = username
email.SMTPPass   = password
email.SMTPCrypto = tls
email.SMTPTimeout = 5
email.mailType   = html

Note

Untuk development, gunakan Mailtrap atau sejenisnya — email masuk ke kotak virtual dan tidak benar-benar terkirim. Ini menghindari spam inbox dan menahan pengiriman palsu. Untuk produksi, gunakan SMTP provider nyata seperti SES, SendGrid, atau SMTP hosting.

Field kunci di app/Config/Email.php:

PropertyArti
$protocolsmtp, sendmail, atau mail
$SMTPHostHost SMTP server
$SMTPPortPort (587 untuk submission, 465 untuk SSL)
$SMTPCryptotls atau ssl
$mailTypetext atau html

Mengirim Email

Mengirim email dasar
$email = \Config\Services::email();
 
$email->setFrom('noreply@devvnull.test', 'DevNull');
$email->setTo($user['email']);
$email->setSubject('Reset Password Akun DevNull');
$email->setMessage('Klik link berikut untuk reset password kalian...');
 
if ($email->send()) {
    session()->setFlashdata('success', 'Email terkirim');
} else {
    log_message('error', 'Email gagal: ' . $email->printDebugger(['headers']));
}

\Config\Services::email() mengembalikan instance Email yang sudah memakai konfigurasi di atas. send() mengembalikan true/false, dan printDebugger(['headers']) menampilkan detail error SMTP untuk debugging.

Warning

Jangan asumsi send() berhasil tanpa mengecek return value. SMTP bisa gagal diam-diam: host unreachable, auth ditolak, atau rate limit. Selalu log error saat send() false agar masalah terdeteksi sejak dini.

Template Email HTML

Email profesional bukan satu paragraf plain text. Buat template di app/Views/emails/:

app/Views/emails/layout.php
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <style>
        body { font-family: sans-serif; line-height: 1.6; color: #333; }
        .container { max-width: 600px; margin: 0 auto; }
        .btn {
            display: inline-block;
            background: #2563eb;
            color: #fff;
            padding: 10px 20px;
            text-decoration: none;
            border-radius: 6px;
        }
    </style>
</head>
<body>
    <div class="container">
        <?= $this->renderSection('content') ?>
        <hr>
        <p><small>Email ini dikirim otomatis oleh DevNull. Jangan balas email ini.</small></p>
    </div>
</body>
</html>
app/Views/emails/reset_password.php
<?= $this->extend('emails/layout') ?>
 
<?= $this->section('content') ?>
    <h2>Halo <?= esc($name) ?>,</h2>
    <p>Kami menerima permintaan reset password untuk akun kalian.</p>
    <p>Klik tombol di bawah untuk membuat password baru:</p>
    <p><a class="btn" href="<?= $resetLink ?>">Reset Password</a></p>
    <p>Link ini berlaku selama <strong>1 jam</strong>. Abaikan email ini jika kalian tidak meminta reset password.</p>
<?= $this->endSection() ?>

Layout dan section di template email sama persis dengan yang kita pelajari di episode 5 — konsistensi yang membuat kalian tidak perlu belajar sintaks baru.

Praktik: Reset Password via Email

Alur reset password yang aman:

100%

1. Generate dan Simpan Token

app/Controllers/PasswordReset.php
public function send(): \CodeIgniter\HTTP\RedirectResponse
{
    $email = $this->request->getPost('email');
 
    $user = (new UserModel())->where('email', $email)->first();
    if ($user === null) {
        // jangan ungkap apakah email terdaftar
        return redirect()->back()->with('success', 'Jika email terdaftar, link reset terkirim.');
    }
 
    $token = bin2hex(random_bytes(32));
    $db = \Config\Database::connect();
    $db->table('password_resets')->insert([
        'email'      => $email,
        'token_hash' => hash('sha256', $token),
        'expires_at' => date('Y-m-d H:i:s', strtotime('+1 hour')),
    ]);
 
    $emailService = \Config\Services::email();
    $emailService->setFrom('noreply@devvnull.test', 'DevNull');
    $emailService->setTo($email);
    $emailService->setSubject('Reset Password DevNull');
    $emailService->setMessage(view('emails/reset_password', [
        'name'      => $user['name'],
        'resetLink' => site_url("reset-password/{$token}"),
    ]));
    $emailService->send();
 
    return redirect()->back()->with('success', 'Jika email terdaftar, link reset terkirim.');
}

Poin keamanan penting: kita menyimpan hash token (hash('sha256', $token)), bukan token mentah — jika database bocor, penyerang tidak bisa memakai token tersebut. Dan pesan suksesnya identik untuk email terdaftar/tidak (anti user enumeration).

2. Validasi Token dan Update Password

Validasi dan update password
public function reset(string $token): string | \CodeIgniter\HTTP\RedirectResponse
{
    $db = \Config\Database::connect();
 
    $record = $db->table('password_resets')
                 ->where('token_hash', hash('sha256', $token))
                 ->where('expires_at >', date('Y-m-d H:i:s'))
                 ->get()->getRowArray();
 
    if ($record === null) {
        return redirect()->to('/forgot-password')->with('error', 'Link reset tidak valid atau sudah kedaluwarsa.');
    }
 
    if ($this->request->getMethod() === 'post') {
        $rules = [
            'password'         => 'required|min_length[8]',
            'confirm_password' => 'required|matches[password]',
        ];
 
        if (! $this->validate($rules)) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }
 
        (new UserModel())->where('email', $record['email'])
                         ->set(['password' => password_hash($this->request->getPost('password'), PASSWORD_DEFAULT)])
                         ->update();
 
        $db->table('password_resets')->where('email', $record['email'])->delete();
 
        return redirect()->to('/login')->with('success', 'Password berhasil diubah. Silakan login.');
    }
 
    return view('auth/reset_password');
}

Setelah password diubah, semua token reset user tersebut dihapus — mencegah pemakaian ulang token lama.

Warning

Jangan pernah mengirim token di dalam query parameter tanpa hash di database, dan jangan biarkan link reset berlaku selamanya. Token sekali pakai + expiry + hash penyimpanan adalah kombinasi minimum yang aman.

Ringkasan Email & Notifications

  • Konfigurasi SMTP di Email.php/.env; gunakan Mailtrap untuk development.
  • \Config\Services::email() + setFrom/setTo/setSubject/setMessage lalu send().
  • Template email pakai layout & section seperti view biasa.
  • Reset password: token acak → simpan hash + expiry → email link → validasi → update.
  • Anti user enumeration: respons identik untuk email terdaftar maupun tidak.

Penutup

Inti yang harus dibawa pulang:

  • Selalu cek return send() dan log kegagalan SMTP.
  • Template email HTML dengan layout dan section yang reusable.
  • Simpan hash token, beri expiry, dan hapus token setelah dipakai.
  • Jangan pernah ungkap status pendaftaran email lewat pesan error.

Di episode 12 selanjutnya kita akan membangun authentication & authorization — auth session-based custom, lalu naik ke CodeIgniter Shield sebagai package resmi, plus role-based access dengan filter untuk admin/user. Ini salah satu episode terpenting di series ini!

Belajar CodeIgniter - Email & Notifications | Belajar CodeIgniter