Michael Korte – Senior Full-Stack Developer Freelancer aus Dortmund

Ziel dieses Teils

Zum ersten Mal erzeugt das System jetzt echtes HTML. Wichtig ist dabei nicht das Template selbst, sondern wo es benutzt wird.

Grundregel

Controller liefern Zustand – Templates liefern Darstellung.
Sie kennen sich nicht gegenseitig.

7.1 Zielstruktur

/project-root
├─ public/
│  └─ index.php
├─ src/
│  ├─ Core/
│  │  ├─ App.php
│  │  ├─ Router.php
│  │  ├─ Renderer.php
│  │  ├─ View.php
│  │  └─ PageContext.php
│  └─ App/
│     └─ Controller/
│        └─ HomeController.php
├─ templates/
│  ├─ base.html.twig
│  └─ home.html.twig
└─ vendor/

Templates liegen bewusst außerhalb von App und Core. Der Core kennt nur einen Renderer – nicht die Seitenlogik.

7.2 View: reine Template-Engine

<?php
namespace Core;

use Twig\Environment;
use Twig\Loader\FilesystemLoader;

final class View
{
    private Environment $twig;

    public function __construct()
    {
        $loader = new FilesystemLoader(
            dirname(__DIR__, 2) . '/templates'
        );

        $this->twig = new Environment($loader, [
            'cache' => false,
            'autoescape' => 'html',
        ]);
    }

    public function render(string $template, array $data): string
    {
        return $this->twig->render($template, $data);
    }
}

Wichtig

Die View ist kein Service für Controller.
Sie wird ausschließlich vom Renderer benutzt.

7.3 Base-Layout

<!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <title>{{ title|default('Clean Output MVC') }}</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<header>
  <h1>Clean-Output-MVC</h1>
</header>

<main>
  {% block content %}{% endblock %}
</main>

<footer>
  <small>Tutorial v0.1</small>
</footer>

</body>
</html>

7.4 Seiten-Template

{% extends "base.html.twig" %}

{% block content %}
  <h2>{{ headline }}</h2>
  <p>{{ message }}</p>
{% endblock %}

7.5 Controller: kein Rendering

<?php
namespace App\Controller;

use Core\PageContext;

final class HomeController
{
    public function index(): PageContext
    {
        $page = new PageContext();

        $page
            ->withTemplate('home.html.twig')
            ->withData([
                'headline' => 'Hello Twig',
                'message'  => 'Die erste echte Ausgabe entsteht im Renderer.'
            ]);

        return $page;
    }
}

Entscheidender Punkt

Der Controller kennt kein Twig.
Er beschreibt nur den Seitenzustand.

Zwischenfazit

  • HTML entsteht ausschließlich im Renderer
  • Templates sind reine Darstellung
  • Controller bleiben testbar und deterministisch

Jetzt greift das Konzept

Wenn sich HTML ändert, suchst du im Template.
Wenn sich Inhalt ändert, suchst du im PageContext.
Wenn sich Entscheidungen ändern, suchst du im Controller.

Projekt & Quellcode

Der Bootstrap und der Einstiegspunkt sind im Repository vollständig nachvollziehbar umgesetzt: