Forms
A form is the way to get data from a visitor: a new post, a comment, a password. It needs two routes: one shows the page with the form, the other accepts the submitted data. Functions for form tags, error output and previous values are collected in the form helper.
Routes and loading the helper
// app/Config/Routes.php
$routes->get('posts/new', 'Posts::new');
$routes->post('posts', 'Posts::create');The posts/new route must stand above posts/(:num). Add the form helper to BaseController: protected $helpers = ['form'];.
The form view
Create the file app/Views/posts/new.php:
<!-- app/Views/posts/new.php -->
<?= form_open('posts') ?>
<label for="title">Title</label>
<input type="text" id="title" name="title">
<label for="body">Text</label>
<textarea id="body" name="body"></textarea>
<button type="submit">Save</button>
<?= form_close() ?>form_open('posts') prints the opening form tag with the POST method and the full /posts address, form_close() prints the closing one. Fields can be written in plain HTML.
The controller
// app/Controllers/Posts.php
public function new(): string
{
return view('posts/new');
}Open /posts/new and the form appears. Its data is accepted by the create() method from the previous lesson: it checks the fields and either saves the post or sends the visitor back.
CSRF protection
CSRF is an attack in which another site submits a form on behalf of a logged-in visitor. A secret token in a hidden field protects against it. The check is switched on in app/Config/Filters.php:
// app/Config/Filters.php
public array $globals = [
'before' => [
'csrf',
],
];After that form_open() adds a hidden field with the token to the form by itself, and a POST request without it is rejected. In a form written with a plain form tag the field is inserted with <?= csrf_field() ?>.
Showing errors
After a failed check the controller sends the visitor back with redirect()->back()->withInput(). validation_show_error() prints the message for a field:
<label for="title">Title</label>
<input type="text" id="title" name="title">
<p class="error"><?= validation_show_error('title') ?></p>Submit an empty form and the error text appears under the field. <?= validation_list_errors() ?> prints all messages as one list.
Previous values
So that the visitor does not have to type the text again, fields are filled with the old() function:
<input type="text" id="title" name="title" value="<?= old('title') ?>">
<textarea id="body" name="body"><?= old('body') ?></textarea>old('title') returns the value entered last time, already escaped. When the form is opened for the first time it returns nothing.
Functions of the form helper
| Function | What it does |
|---|---|
form_open('posts') | The opening form tag and the CSRF token |
form_close() | The closing tag |
csrf_field() | A hidden field with the token |
old('title') | The previous value of a field |
validation_show_error('title') | The error of one field |
validation_list_errors() | The list of all errors |
Common mistake
If the visitor is sent back with redirect()->back() without withInput(), neither old() nor validation_show_error() prints anything: the values and errors were not kept for the next request. The form simply opens empty, and the visitor has no idea what happened.