Zum Inhalt springen
Nibiru docsv0.9.2

Architektur (MMVC)

Wie sich Module, Controller, Views, Modelle, der Registry und der Dispatcher gegenseitig beeinflussen.

Stable Reading time ~ 3 min Edit on GitHub

Nibiru ist MMVC: Modell — Ansicht — Steuerung — und ein zweiter M für Modul. Die ersten drei sind vertraut; das zweite M ist, was Nibiru seinen Geschmack gibt.

Querschnittsillustration der Nibiru-Laufzeit als Lotus, wobei jede Blattfläche als Systemkomponente beschriftet ist: Router, Controller, View, Model, Modul, Registry.
Die Nibiru-Laufzeit als Lotus.
┌──────────────┐
│ Browser │
└──────┬───────┘
│ HTTP
┌──────────────┐
│ index.php │
└──────┬───────┘
│ require
┌──────────────┐
│ framework.php│ ◀── boots Config, Router, Engine, Smarty,
└──────┬───────┘ all 28 form types, DB drivers, Auth.
┌──────────────┐
│ Dispatcher │
└──────┬───────┘
┌───────────────┼─────────────────┐
▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌───────────┐
│ Router │ │ Modules │ │ Auto │
└────────┘ └──────────┘ │ loader │
└───────────┘
┌─────────────────────────────┐
│ applicationController.php │
│ navigationAction() │
│ <_action>Action() │
│ pageAction() │
└──────────────┬──────────────┘
│ View::assign(...)
┌──────────────┐
│ Smarty │ templates/<ctrl>.tpl + shared/*
└──────────────┘

Ein Controller ist eine Klasse, die von Nibiru\Adapter\Controller erbt. Der Dispatcher führt eine festgelegte Sequenz bei jedem Anfrageaufruf aus:

  1. navigationAction() — Menüs/Brotkrumen-Daten auffüllen.
  2. <_action>Action() — nur, wenn ?_action=foo gesetzt ist.
  3. pageAction() — endgültige Datenzuweisung zur Renderzeit.

Nachdem alle drei zurückgekehrt sind, überreicht Display::display() die zugewiesenen Variablen an Smarty.

Smarty .tpl-Dateien. Der Dispatcher löst <controller>.tpl automatisch auf; verschachtelte Aktionen befinden sich unter templates/<controller>/<action>.tpl. Jede über View::assign(['x' => ...]) übergebene Variable ist als {$x} verfügbar.

Modelle werden automatisch generiert aus Ihrem Datenbankschema durch Model::__construct(false) – eine PHP-Klasse pro Tabelle. Sie erweitern Nibiru\Adapter\<Driver>\Db und bieten CRUD-Hilfsprogramme. Sie können einen generierten Modell bearbeiten und den Regenerator mit [GENERATOR] database = false deaktivieren.

Ein Modul kapselt seine eigenen Merkmale, Plugins, Schnittstellen und Einstellungen. Der Registry entdeckt automatisch jede settings/*.ini des Moduls und macht die geparste Konfiguration über Registry::getInstance()->loadModuleConfigByName('users') zugänglich. Module können SplSubject implementieren, um das Beobachtermuster zu verwenden, was anderen Teilen des Systems erlaubt, Beobachter anzuhängen und auf Zustandsänderungen zu reagieren.

5. Singleton-Klassen, die das Universum zusammenhalten

Abschnitt betitelt „5. Singleton-Klassen, die das Universum zusammenhalten“
SingletonAufgabe
Config::getInstance()Liest settings.<env>.ini und fügt Modulkonfigurationen hinzu.
Router::getInstance()Parst die URL in Controller/Aktion/Parameter; erkennt SEO-URL-Formen.
Registry::getInstance()Modulentdeckung + Konfigurations-Caching.
Dispatcher::getInstance()Der Dirigent. dispatch::run() ist das Herzschlag Ihrer Anwendung.
View::getInstance()Verpackt Smarty. View::assign() ist die globale Vorlagenvariablen-Eingabebox.

Einfache MVC funktioniert, bis Ihre Controller beginnen, Logik zu teilen – Authentifizierungsprüfungen, Formularfabriken, Third-Party-API-Clients. Die üblichen Antworten sind Dienste + DI-Container, aber das ist viel Zeremonie für ein Framework zur schnellen Prototypenentwicklung.

Nibiru’s answer: Module. Ein Modul besitzt eine Domäne (users, cms, analytics, tpms-quotes), indem es seine Dienste über Plugins verfügbar macht, die Controller direkt instanziieren können:

// In a controller
$user = new \Nibiru\Module\Users\Plugin\User();
if (!$user->isAuthorized()) {
View::forwardTo('/login');
}

Das Modul besitzt seine Konfiguration, seine Datenbanktabellen, seine Vorlagen und seine Formulare – und kann als Einheit entfernt werden.

Einige Module implementieren SplSubject, sodass andere Code Teile auf Ereignisse reagieren können, ohne Kopplung. Aus der Präsentation macht das analytics-Modul auf prod.maschinen-stockert.de genau dies: jeder Controller kann einen Tracker (Matomo Plugin) mit attach() anhängen und das Analytics-Modul benachrichtigt (notify()) bei jedem Seitenaufruf, ohne dass der Controller weißt, welche Trackers existieren.

class Analytics implements \SplSubject {
private \SplObjectStorage $observers;
public function __construct() { $this->observers = new \SplObjectStorage(); }
public function attach(\SplObserver $o): void { $this->observers->attach($o); }
public function detach(\SplObserver $o): void { $this->observers->detach($o); }
public function notify(): void {
foreach ($this->observers as $o) { $o->update($this); }
}
}
  • Kein DI-Container. Singletons und Plugins erfüllen die Aufgabe.
  • Kein ORM. Modelle werden aus dem Schema generiert; Abfragen verwenden den Db Adapter oder raw SQL über den aktiven Treiber.
  • Keine Template-Ererbung durch Twig/Blade-Tricks. Smarty {include} ist die Kompositions-Einheit.
  • Kein Event-Bus. SplSubject/SplObserver sind erstklassig.
  • Keine ersten-Klasse Hintergrundjobs. Die CLI ist Ihr Scheduler — führen Sie ihn über cron oder systemd Timers aus.

Weniger zu lernen, mehr zu liefern.