Documentation · Chapter 04

Create, read,
update, delete.

CRUD is simply shorthand for the four most frequent operations on a data collection.

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

Complete action map

GET /booksindex — Read
GET /books/createcreate — form
POST /booksstore — Create
GET /books/{id}/editedit — form
POST /books/{id}/editupdate — Update
POST /books/{id}/deletedestroy — Delete

HTML forms directly understand GET and POST. We therefore use explicit paths for update and delete without adding a hidden method-override mechanism.

The routes

src/routes/WebApp.php

phpaml — zsh
$this->get('/books', [BookController::class, 'index']);
$this->get('/books/create', [BookController::class, 'create']);
$this->post('/books', [BookController::class, 'store']);
$this->get('/books/{id}/edit', [BookController::class, 'edit']);
$this->post('/books/{id}/edit', [BookController::class, 'update']);
$this->post('/books/{id}/delete', [BookController::class, 'destroy']);

Read a dynamic identifier

phpaml — zsh
public function edit(Request $request): Response
{
    $id = (int) $request->attribute('id');
    $books = $this->session->get('books', []);
    $book = $this->findBook($books, $id);

    if ($book === null) {
        return Response::html('<h1>404</h1><p>Book not found.</p>', 404);
    }

    return $this->view('books/edit.php', [
        'book' => $book,
        'errors' => [],
    ]);
}

The router extracts {id} from the address and places it in Request attributes. The int cast prevents treating this identifier as arbitrary text.

Search without duplicating the loop

phpaml — zsh
/** @param list<array{id:int,title:string,author:string}> $books */
private function findBook(array $books, int $id): ?array
{
    foreach ($books as $book) {
        if ($book['id'] === $id) {
            return $book;
        }
    }

    return null;
}

This private method does not answer the browser. It simply helps controller actions find a book. In the next chapter, a database and repository will replace this session search.

Intentional session limitation

Books here belong to one browser and may disappear when the session expires. This is enough to learn HTTP; it is not production storage.