Documentation · Chapter 04

Build an
understandable form.

Before processing data, let us build an accessible page where every label, name, and type has a precise function.

We continue the reading-list project. Data will be kept in the session before introducing a database.

Add the route displaying the form

src/routes/WebApp.php

phpaml — zsh
use App\Controllers\BookController;

// Dans routes():
$this->get('/books/create', [BookController::class, 'create']);

This route does not save anything yet. It only asks BookController to display the form.

Add the create() action

src/controllers/BookController.php

phpaml — zsh
public function create(Request $request): Response
{
    return $this->view('books/create.php', [
        'errors' => [],
        'old' => ['title' => '', 'author' => ''],
    ]);
}

We already pass errors and old, even when empty. The view will therefore keep the same shape when validation fails later.

Create the view file

src/views/books/create.php

phpaml — zsh
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Add a book</title></head>
<body>
<main>
    <h1>Add a book</h1>
    <form method="post" action="/books">
        <?= $view->csrfField() ?>

        <label for="title">Title</label>
        <input id="title" name="title" type="text" required
               value="<?= $view->escape($old['title'] ?? '') ?>">

        <label for="author">Author</label>
        <input id="author" name="author" type="text" required
               value="<?= $view->escape($old['author'] ?? '') ?>">

        <button type="submit">Save</button>
    </form>
</main>
</body>
</html>

Read the form element

method=postsends a POST request
action=/bookschooses the destination address
name=titlecreates the title request key
requiredhelps the browser, without replacing the server
$view->csrfField()adds the protection token
Check your understanding: Why is required not enough?

Because a client can bypass browser rules and send a request directly. The server must always validate for itself.