Documentation · Chapter 04

Workshop: finish
the manager.

Combine the concepts into a usable application, then verify every scenario as a user and a developer.

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

REQUIREMENTS

The /books page must become a real starting point.

  1. Display all session books.
  2. Provide an Add a book link.
  3. Provide Edit and Delete for every book.
  4. Preserve invalid values and display errors.
  5. Redirect after every successful change.

index() action

phpaml — zsh
public function index(Request $request): Response
{
    return $this->view('books/index.php', [
        'books' => $this->session->get('books', []),
    ]);
}

List view

src/views/books/index.php

phpaml — zsh
<h1>My books</h1>
<p><a href="/books/create">Add a book</a></p>

<?php if ($books === []): ?>
    <p>No books yet.</p>
<?php else: ?>
    <ul>
    <?php foreach ($books as $book): ?>
        <li>
            <strong><?= $view->escape($book['title']) ?></strong>
            — <?= $view->escape($book['author']) ?>
            <a href="/books/<?= (int) $book['id'] ?>/edit">Edit</a>
            <form method="post" action="/books/<?= (int) $book['id'] ?>/delete">
                <?= $view->csrfField() ?>
                <button type="submit">Delete</button>
            </form>
        </li>
    <?php endforeach; ?>
    </ul>
<?php endif; ?>

Delete action

phpaml — zsh
public function destroy(Request $request): Response
{
    $id = (int) $request->attribute('id');
    $books = $this->session->get('books', []);
    $books = array_values(array_filter(
        $books,
        static fn (array $book): bool => $book['id'] !== $id,
    ));
    $this->session->set('books', $books);

    return Response::redirect('/books', 303);
}

array_filter keeps books with a different identifier. array_values resets array positions into a simple order. The book's business identifier itself does not change.

Verification plan

Create a valid book303 → GET /books
Submit an empty title422 + error
Remove the CSRF token419
Edit a missing identifier404
Delete a bookdisappears from list
phpaml — zsh
aml routes
aml test

What you actually learned

You did more than create a form. You separated reading and modification, read a request, validated values, protected an action, kept state across requests, chose useful HTTP statuses, and prevented POST repetition.

SUMMARY

What to remember

  • The browser helps, but the server always validates.
  • csrfField() protects every modifying form.
  • The view escapes every dynamic value.
  • A 303 redirect cleanly completes a POST.
  • Session suits learning, not permanent persistence.