Skip to content

Controllers ​

Controllers live in src/Controller/ and extend TinyTank\Controller:

php
<?php

declare(strict_types=1);

namespace App\Controller;

use TinyTank\Controller;
use Symfony\Component\HttpFoundation\Response;

final class BlogController extends Controller
{
    public function show(string $slug): string|Response
    {
        $post = $this->app->posts()->slug($slug)->one();
        if ($post === null) {
            return $this->notFound($this->t('Post not found.'));
        }

        return $this->render('blog/post.twig', ['post' => $post]);
    }
}

Each request creates a new controller, with the kernel as its only constructor argument ($this->app). Extending TinyTank\Controller is optional, but it gives you the helpers below.

What an action returns ​

  • A string: sent as an HTML page (200, text/html).
  • Any Symfony Response: JSON, redirects, files, streamed responses, custom status codes.
php
use Symfony\Component\HttpFoundation\RedirectResponse;

return new RedirectResponse($this->path('blog'), 301);

Successful GET pages automatically get an ETag and Cache-Control: public, no-cache, so repeat visits are answered with a 304.

Helpers ​

MethodWhat it does
render($template, $vars)Renders a Twig template to a string
stream($template, $vars)Renders a template as a Datastar response
json($data, $status = 200)A JsonResponse
notFound($message)The 404 error page
path($name, $params, $language)URL path of a named route, in the current language
t($message, $params)Translates a UI text (Languages)
get($id)A service from the container
request()The current Symfony Request

The kernel ​

$this->app is the TinyTank\Kernel. Its most useful parts:

Property / method
$app->seoPage metadata: title, description, Open Graph, JSON-LD (SEO)
$app->siteSite name, URL, languages and the current language
$app->blogBlog posts: find(), all(), page(), search(), tags()
$app->containerServices registered in config/bootstrap.php
$app->twigThe Twig environment
$app->debugWhether debug mode is on
$app->error($status, $message)An error page with any status

The full list is in the PHP API reference.

Closures ​

For tiny pages, a closure in config/routes.php is enough:

php
$app->get('/healthz', static fn () => new Response('ok', 200, ['Content-Type' => 'text/plain']), 'health');

Closures receive route parameters as named arguments like controller methods do; use $app from the surrounding function to render templates.

TinyTank documentation