Michael Korte – Senior Full-Stack Developer Freelancer aus Dortmund

Ziel dieses Teils

Ab diesem Punkt ist endgültig klar: Controller erzeugen keinen Output mehr.

Stattdessen beschreiben sie den vollständigen Zustand einer Seite – bevor irgendein HTML entsteht.

Merksatz

PageContext ist kein Datenbeutel.
Er ist ein Vertrag.

8.1 Das Kernproblem klassischer MVCs

In vielen MVC-Systemen existiert kein klarer Ort, an dem man den Zustand einer Seite vollständig erfassen kann.

  • Meta-Daten werden im Template überschrieben
  • Controller setzen Header „nebenbei“
  • Assets werden an mehreren Stellen registriert
  • Fehlerseiten folgen anderen Regeln

Typische Folge

Wenn etwas „komisch“ ist, sucht man: im Controller, im Template, im Event, im Plugin.

8.2 Was der PageContext löst

Der PageContext bündelt alles, was eine Seite ist – bevor sie gerendert wird.

  • Status-Code
  • Meta-Daten
  • strukturierte Inhaltsdaten
  • Block-Definitionen

Wenn sich eine Seite anders verhält als erwartet, ist der PageContext der erste Ort, an dem gesucht wird.

8.3 Minimaler PageContext

<?php
namespace Core;

final class PageContext
{
    private array $data = [];
    private array $meta = [];

    public function withData(array $data): self
    {
        $this->data = array_merge($this->data, $data);
        return $this;
    }

    public function withMeta(array $meta): self
    {
        $this->meta = array_merge($this->meta, $meta);
        return $this;
    }

    public function getData(): array
    {
        return $this->data;
    }

    public function getMeta(): array
    {
        return $this->meta;
    }
}

Wichtig: Der PageContext enthält keine Logik. Er beschreibt nur Zustand.

8.4 Controller als Zustands-Erzeuger

<?php
namespace App\Controller;

use Core\PageContext;

final class HomeController
{
    public function index(): PageContext
    {
        return (new PageContext())
            ->withMeta([
                'title' => 'Clean Output MVC',
                'description' => 'Eine deterministische Render-Architektur'
            ])
            ->withData([
                'headline' => 'Hallo PageContext',
                'content'  => 'Noch kein HTML – nur Zustand.'
            ]);
    }
}

Der Controller entscheidet, was diese Seite aussagt – nicht wie sie aussieht.

8.5 Kritische Frage: „Was, wenn Meta überschrieben wird?“

Gute Frage

„Wenn Meta-Daten später anders sind als erwartet – wo suche ich dann?“

Die Antwort ist bewusst eindeutig:

  • Controller setzen Meta
  • PageContext trägt Meta
  • Renderer gibt Meta aus

Wenn Meta anders ist, gibt es nur zwei Möglichkeiten:

  • Der Controller hat es geändert
  • Ein definierter Hook hat es geändert

Es gibt keine stillen Orte, an denen Meta „zufällig“ überschrieben wird.

8.6 Warum das deterministisch ist

Deterministisch heißt hier:

  • gleicher Input → gleicher PageContext
  • gleicher PageContext → gleicher Output
  • keine impliziten Abkürzungen

In vielen Frameworks kann man so arbeiten.
In Clean-Output-MVC muss man es.

Projekt & Quellcode

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