Ziel dieses Teils
In diesem Teil entsteht erstmals etwas, das sich wie eine „Seite“ anfühlt.
Technisch tun wir aber nur eines: Wir treffen eine eindeutige Entscheidung anhand der URL.
- Routing explizit registrieren
- erste Route definieren
- Controller als Ziel
- Error-Controller als regulären Standardweg
5.1 Routing ist Entscheidung – nicht Darstellung
Der Router beantwortet exakt eine Frage:
Was wird entschieden?
Welcher Controller ist zuständig?
Nicht: welches Template.
Nicht: welches HTML.
Nicht: wie die Seite aussieht.
Alles andere ist explizit nicht Aufgabe des Routers.
5.2 Router registrieren (bewusst im Bootstrap)
Der Router wird nicht „irgendwo“ erstellt, sondern explizit während der Bootstrap-Phase.
<?php
// Core/Bootstrap.php
use Core\Router;
use App\Controller\HomeController;
use App\Controller\ErrorController;
private static function register(App $app): void
{
$router = new Router();
$router->get('/', [HomeController::class, 'index']);
// Kein Match ist kein Sonderfall
$router->fallback([ErrorController::class, 'error404']);
$app->setRouter($router);
}
Determinismus an dieser Stelle
Jede Anfrage endet immer in genau einem Controller. Es gibt keinen „leeren“ Pfad.
Hinweis zu Teil 5: Controller-Referenzen im Bootstrap
Aufmerksamen Lesern fällt an dieser Stelle etwas auf: Der Bootstrap kennt konkrete Controller-Klassen.
Das wirkt wie ein Bruch – und das ist es auch. Allerdings bewusst und ausschließlich für v0.1.
Warum das irritieren darf
- Der Bootstrap sollte langfristig keine Domain kennen
- Controller gehören fachlich zu Capabilities / Components
- Routing sollte deklarativ, nicht hart verdrahtet sein
In einer idealen Zielarchitektur würde der Bootstrap keinen einzelnen Controller referenzieren. Er würde lediglich Mechanismen bereitstellen, über die Capabilities ihre Routen selbst registrieren.
In v0.1 existieren diese Mechanismen jedoch noch nicht:
- keine Component-Discovery
- keine Route-Provider
- keine deklarative Capability-Registrierung
Statt impliziter Magie zeigt das Tutorial daher einen ehrlichen, sichtbaren Zwischenschritt: Der Bootstrap bindet die Controller explizit.
Wichtig für das Verständnis
Dieses Vorgehen ist kein Pattern und kein Zielzustand, sondern ein temporärer Kompromiss für v0.1.
In späteren Versionen (v0.2+) wird der Bootstrap keine Controller mehr kennen, und Routing erfolgt ausschließlich über registrierte Capabilities.
Für v0.1 gilt bewusst: Lieber ein erklärbarer Bruch als eine unsichtbare Abkürzung.
5.3 Der erste Controller
Controller treffen Entscheidungen. Sie erzeugen noch kein HTML und rufen keine Templates auf.
<?php
namespace App\Controller;
use Core\PageContext;
final class HomeController
{
public function index(): PageContext
{
$page = new PageContext();
$page->withData([
'message' => 'Hallo Welt'
]);
return $page;
}
}
Bewusster Bruch mit klassischem MVC
Der Controller gibt keinen String, kein View-Objekt und kein Response-Objekt zurück, sondern reinen Seitenzustand.
5.4 ErrorController ist kein Sonderfall
Fehlerseiten folgen exakt denselben Regeln wie normale Seiten.
<?php
namespace App\Controller;
use Core\PageContext;
final class ErrorController
{
public function error404(): PageContext
{
$page = new PageContext();
$page
->withStatus(404)
->withData([
'title' => 'Seite nicht gefunden'
]);
return $page;
}
}
Der Router entscheidet, dass kein Match existiert.
Der Controller entscheidet, welcher Zustand daraus entsteht.
5.5 Warum hier bewusst nichts gerendert wird
An diesem Punkt existiert:
- kein Template
- kein HTML
- keine Assets
Das ist kein Mangel, sondern der Kern der Architektur.
Trennung greifbar
Entscheidung ≠ Darstellung
Routing ≠ Rendering
Controller ≠ Output
Zwischenstand
Der aktuelle Ablauf ist jetzt vollständig – aber noch ohne Ausgabe:
- Request
- Router
- Controller
- PageContext
HTML existiert noch nicht. Und genau deshalb ist es später eindeutig erklärbar.
Übergang zu Teil 6
In Teil 6 kommt der entscheidende Schritt:
Rendering – und warum nur der Renderer HTML erzeugt und alle anderen Schichten es nicht dürfen.
Projekt & Quellcode
Der Bootstrap und der Einstiegspunkt sind im Repository vollständig nachvollziehbar umgesetzt:
- 👤 GitHub-Profil: github.com/MichaelKorte73
- 📦 Projekt-Repository: github.com/MichaelKorte73/CleanOutputMVC