Form Validation
Never trust incoming data. Every value arriving via $_POST, $_GET, or a JSON
request body could be missing, malformed, or deliberately hostile, and checking it by hand with a pile
of if statements gets messy and error-prone fast. CodeIgniter 4 ships a dedicated
Validation library specifically to solve this: you describe the rules a piece of data
must satisfy as a simple array, hand that array to the library, and it tells you exactly what passed
and what didn't — along with ready-to-display error messages for each failure.
1. Running Validation in a Controller
There are two equivalent ways to reach the Validation library. The first is explicit, going through the
same Config\Services factory introduced on the previous page:
$validation = \Config\Services::validation();
In practice, though, almost every CodeIgniter controller uses the far more convenient shortcut: every
controller extending BaseController has a $this->validate() method built
in, which runs the Validation service against the current request's input and returns a simple boolean:
<?php
namespace App\Controllers;
use App\Models\ArticleModel;
class Articles extends BaseController
{
public function store()
{
$rules = [
'title' => 'required|min_length[3]|max_length[255]',
'email' => 'required|valid_email',
];
if (!$this->validate($rules)) {
return view('articles/create', [
'errors' => $this->validator->getErrors(),
]);
}
$model = new ArticleModel();
$model->insert([
'title' => $this->request->getPost('title'),
]);
return redirect()->to('/articles');
}
}
$this->validate($rules) automatically pulls the data straight out of the current request
(POST fields by default), checks every rule, and returns true or false. When it
fails, the framework's built-in $this->validator property holds onto the results so you can
immediately ask it for the details.
2. Common Built-in Rules
Rules are written as a single pipe-separated string per field, and CodeIgniter ships dozens of them covering the vast majority of everyday validation needs. Here are the ones you'll reach for constantly:
| Rule | What it checks |
|---|---|
required | The field must be present and not empty |
min_length[3] | String must be at least the given number of characters |
max_length[255] | String must not exceed the given number of characters |
valid_email | Value must be a syntactically valid email address |
numeric | Value must consist only of numeric characters |
is_unique[table.column] | No existing row in that table/column already has this value — ideal for unique usernames or emails |
matches[field] | Value must match another field exactly — commonly used for password confirmation |
alpha_numeric | Value must contain only letters and numbers |
Rules for one field are combined by chaining them with the pipe character, and rules needing a parameter take it in square brackets:
'username' => 'required|min_length[3]|max_length[30]|is_unique[users.username]'
3. Retrieving and Displaying Errors
When validation fails, getErrors() returns an associative array keyed by field name, with
each value being the specific message for that field's first broken rule. Passing that array into a
view lets you show each error right next to the field it belongs to:
// app/Views/articles/create.php
<?php if (!empty($errors)): ?>
<div class="alert alert-danger">
<ul>
<?php foreach ($errors as $field => $message): ?>
<li><?= esc($message) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
Wrapping each message in esc() before printing it matters here just as much as anywhere
else user-influenced text ends up in a view — the Views lesson covers exactly why. If you'd rather
check a single field's error individually (handy for placing a message right under one input box),
the validator instance also offers a per-field lookup:
<?= $validation->getError('email') ?>
4. Reusable Named Rule Groups
Typing the same rule array out in every controller that touches "articles" invites drift — one
controller's rules quietly fall out of sync with another's. CodeIgniter's answer is to define rule
groups once, centrally, in app/Config/Validation.php, then reference them everywhere by name.
// app/Config/Validation.php
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Validation extends BaseConfig
{
public $article = [
'title' => 'required|min_length[3]|max_length[255]',
'body' => 'required|min_length[10]',
];
public $article_errors = [
'title' => [
'required' => 'Please give your article a title.',
],
];
}
Any controller can then validate against that named group instead of repeating the rule array:
if (!$this->validate('article')) {
// same $this->validator->getErrors() as before
}