Views and forms

The renderer displays a PHP view, e() escapes dynamic output, and csrfField() inserts a hidden field with the CSRF token.

Passing data to a view

The controller loads an article through the application ArticleRepository and passes it to ViewRendererInterface::render() as article. The articles.detail name selects the PHP view in the next file; render() returns an HTML response. Use content() when only the rendered string is needed.

app/Controllers/ArticleController.php
<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Services\ArticleRepository;

use Lemonade\Framework\View\ViewRendererInterface;
use Psr\Http\Message\ResponseInterface;

final readonly class ArticleController
{
    public function __construct(
        private ViewRendererInterface $views,
        private ArticleRepository $articles,
    ) {}

    public function detail(string $id): ResponseInterface
    {
        return $this->views->render('articles.detail', [
            'article' => $this->articles->get($id),
        ]);
    }
}

The template reads the article array supplied by the controller and renders title and body. e() escapes both dynamic values; ensuring a valid article exists is the application repository and controller’s responsibility.

app/Views/articles/detail.php
<?php

declare(strict_types=1);

/** @var array{id:string,title:string,body:string} $article */ ?>

<article>
    <h1><?= e($article['title']) ?></h1>
    <p><?= e($article['body']) ?></p>
</article>

Forms and CSRF

Routing.php registers a GET action for the form and a POST action for submission. CsrfMiddleware checks the POST token before ContactController::store() runs; the view in the next file supplies that token. CSRF requires a configured session.

Route and view
<?php

use App\Controllers\ContactController;
use Lemonade\Framework\Routing\ControllerAction;
use Lemonade\Framework\Security\Csrf\CsrfMiddleware;

$router->getNamed('contact.form', '/contact', ControllerAction::for(ContactController::class, 'form'));
$router->postNamed('contact.store', '/contact', ControllerAction::for(ContactController::class, 'store'))
    ->middleware(CsrfMiddleware::class);

The view posts to the named contact.store route. csrfField() inserts the hidden token; e() escapes both the action URL and the email value supplied by form().

app/Views/contact/form.php
<form method="post" action="<?= e($this->url('contact.store')) ?>">
    <?= $this->csrfField() ?>
    <label for="email">E-mail</label>
    <input id="email" name="email" value="<?= e($email) ?>">
    <button type="submit">Send</button>
</form>

Validating submitted data

ContactController renders the form with an empty email value. On POST it checks that getParsedBody() returned an array, then passes it to FormValidation. Errors become JSON with HTTP 422; on success it returns validated() data with HTTP 200. It neither sends nor stores the message.

Controller action
<?php

declare(strict_types=1);

namespace App\Controllers;

use Lemonade\Framework\Http\Response\Responses;
use Lemonade\Framework\Validation\FormValidation;
use Lemonade\Framework\View\ViewRendererInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

final readonly class ContactController
{
    public function __construct(
        private ViewRendererInterface $views,
        private FormValidation $validator,
        private Responses $responses,
    ) {}

    public function form(): ResponseInterface
    {
        return $this->views->render('contact.form', ['email' => '']);
    }

    public function store(ServerRequestInterface $request): ResponseInterface
    {
        $body = $request->getParsedBody();
        $data = is_array($body) ? $body : [];
        $result = $this->validator->field('email', 'E-mail')
            ->required()->email()->maxLength(150)->validate($data);
        if (!$result->isValid()) {
            return $this->responses->json(['errors' => $result->errors()], 422);
        }

        return $this->responses->json(['data' => $result->validated()], 200);
    }
}

Validation and CSRF documentation →

Related documentationRead the Views and layouts chapter in the documentation for more detail.

← All examples