Checkboxes and Radio Buttons
Checkboxes and radio buttons use a similar helper pattern as other form elements, with special handling for checked states.
Always do this:
- In your view: Use
value="1" for checkboxes
- In your controller: Convert POST data to boolean for views, integer for database
- In your database: Store as
0 or 1
Checkboxes
Use form_checkbox():
form_checkbox($name, $value = '1', $checked = false, $attributes = []);
Parameters
$name (string, required) - The name attribute for the checkbox
$value (string|bool|int, optional) - The value attribute. Defaults to '1'
$checked (mixed, optional) - Whether the checkbox should be checked. Defaults to false
$attributes (array, optional) - Additional HTML attributes. Defaults to []
Recommended Pattern (Best Practice)
For accessibility and improved usability, always wrap the checkbox input directly within the HTML <label> element. This allows users to click the text to toggle the checkbox, increasing the target area.
Note on Validity: Placing the input inside the label (implicit association) is perfectly valid HTML5 and the industry-standard best practice for these elements.
<?php
echo '<label>'; // Start the label wrapper
echo form_checkbox('subscribe', 1, $subscribe_checked);
echo ' Newsletter Subscription'; // Place the text after the input
echo '</label>'; // Close the label wrapper
?>
The Checkbox Reality
Unchecked checkboxes don't submit any data. This is HTML behavior, not Trongate.
When a form submits:
- Checked checkbox → POST contains
subscribe='1'
- Unchecked checkbox → POST contains no
subscribe field
// After form submission:
$raw_value = post('subscribe', true);
// If checked: returns '1'
// If unchecked: returns '' (empty string)
Here's something crucial: unchecked checkboxes don't submit anything.
If a checkbox is checked, the form submits its value. If unchecked, the field doesn't appear in the POST data at all.
// Checkbox was checked
post('subscribe', true); // Returns '1' (or whatever value you set)
// Checkbox was unchecked
post('subscribe', true); // Returns '' (empty string)
This is why you often see this pattern:
// In controller (submit method)
$data['subscribe'] = (int) (bool) post('subscribe', true);
// Converts to 1 if checked, 0 if unchecked
The Complete Pattern
Step 1: View File (Use Wrapping Pattern)
<?php
echo '<label>';
echo form_checkbox('newsletter', 1, $newsletter_checked);
echo ' Newsletter Subscription';
echo '</label>';
?>
Important: The third parameter ($newsletter_checked) must be true or false.
Step 2: Controller (Two different conversions)
Your controller needs to handle data differently depending on whether it's going to the view or to the database.
class Members extends Trongate {
// Display form (data goes TO view)
public function create(): void {
$update_id = segment(3, 'int');
if ($update_id > 0 && REQUEST_TYPE === 'GET') {
// Editing existing record
$user = $this->db->get_where($update_id, 'users');
$data['newsletter_checked'] = (bool) $user->newsletter;
} else {
// New form OR redisplay after validation error
$data['newsletter_checked'] = (bool) post('newsletter', true);
}
$this->view('user_form', $data);
}
// Process submission (data goes TO database)
public function submit(): void {
$this->validation->set_rules('email', 'email address', 'required|valid_email');
$result = $this->validation->run();
if ($result === true) {
$data['email'] = post('email', true);
// Convert for database: 1 (checked) or 0 (unchecked)
$data['newsletter'] = (int) (bool) post('newsletter', true);
$update_id = segment(3, 'int');
if ($update_id > 0) {
$this->db->update($update_id, $data, 'users');
set_flashdata('User updated');
} else {
$this->db->insert($data, 'users');
set_flashdata('User created');
}
redirect('users/manage');
} else {
$this->create(); // Redisplay form with errors
// Note: create() method fetches posted data for $newsletter_checked
}
}
}
Step 3: Database Schema
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255),
newsletter TINYINT(1) DEFAULT 0 -- 0 = no, 1 = yes
);
Two Different Conversions
When working with checkboxes the type of conversion that happens differs, depending on whether the goal is to render a view file or enter a checkbox value into a database.
| Destination |
Needs |
Conversion |
Result |
| View (form_checkbox) |
Boolean |
(bool) post('field', true) |
true or false |
| Database |
Integer 0/1 |
(int) (bool) post('field', true) |
1 or 0 |
Common Pitfalls
Never use string values for checkboxes.
// ❌ CONFUSING
echo form_checkbox('newsletter', 'yes');
// Then you need messy checks:
$value = post('newsletter', true); // 'yes' or ''
$is_checked = ($value === 'yes'); // Have to check string
$for_db = (int) ($value === 'yes'); // Even more complex
Do this: Always use 1 as the value
// ✅ CLEAR
echo form_checkbox('newsletter', 1, $newsletter_checked);
// Simple conversions:
$for_view = (bool) post('newsletter', true); // true/false
$for_db = (int) (bool) post('newsletter', true); // 1/0
Radio Buttons
Radio buttons are simpler - they always submit a value. You should also wrap the radio input within the <label> element for accessibility.
form_radio($name, $value = '', $checked = false, $attributes = []);
Parameters
$name (string, required) - The name attribute for the radio button
$value (string|bool|int, optional) - The value attribute. Defaults to ''
$checked (mixed, optional) - Whether the radio button should be checked. Defaults to false
$attributes (array, optional) - Additional HTML attributes. Defaults to []
Radio Group Example (Recommended Pattern)
<?php
$status_options = [
'active' => 'Active',
'inactive' => 'Inactive',
'pending' => 'Pending'
];
foreach ($status_options as $value => $label) {
$is_checked = ($current_status === $value);
echo '<label>';
echo form_radio('status', $value, $is_checked);
echo ' ' . $label;
echo '</label>';
}
?>
// Controller - both view and database use the same value
public function create(): void {
$update_id = segment(3, 'int');
if ($update_id > 0 && REQUEST_TYPE === 'GET') {
$record = $this->db->get_where($update_id, 'items');
$data['current_status'] = $record->status;
} else {
$data['current_status'] = post('status', true);
}
$this->view('form', $data);
}
public function submit(): void {
// Same value works for both view and database
$data['status'] = post('status', true);
$this->db->insert($data, 'items');
}
Multiple Checkboxes
For multiple selections, store as JSON array:
<?php
$categories = ['web', 'mobile', 'design', 'marketing'];
$selected_cats = $selected_categories ?? [];
foreach ($categories as $category) {
$is_checked = in_array($category, $selected_cats, true);
echo '<label>';
echo form_checkbox('categories[]', $category, $is_checked);
echo ' ' . ucfirst($category);
echo '</label>';
}
?>
// Controller
public function submit(): void {
$selected = post('categories', true) ?: []; // Array or empty array
$data['categories'] = json_encode($selected);
$this->db->insert($data, 'projects');
}
Quick Reference
| Element |
View Code |
To View (boolean) |
To Database |
| Checkbox |
<label>...form_checkbox('field', 1, $checked)...</label> |
(bool) post('field', true) |
(int) (bool) post('field', true) |
| Radio |
<label>...form_radio('field', 'value', $checked)...</label> |
post('field', true) |
post('field', true) |
| Text Input |
form_input('field', $value) |
post('field', true) |
post('field', true) |
Remember: Checkboxes are the only form element that needs different handling for views vs. databases. Everything else uses the same value for both.