Zum Inhalt springen
Nibiru docsv0.9.2

Warum Nibiru und nicht Laravel?

Fünf Dinge, die Nibiru anders als Laravel und Symfony macht – jeweils unterpuntet durch echten Produktionscode.

Stable Reading time ~ 4 min Edit on GitHub

Die meisten Vergleiche von PHP-Frameworks sind eher subjektiv. Dieser ist nachvollziehbar.

Die fünf Unterschiedsmaker unten stammen aus einer realen Einnahmengenerierungswebsite: maschinen-stockert.de — einem multilingualen Industriemaschinen-Marktplatz. 36 Controller, 18 Module, 348 Vorlagen, 161 SQL-Migrationen, 37.369 Zeilen PHP über zwei Repositories. Jede Behauptung verweist auf einen Datei:Zeilenverweis innerhalb der Codebasen, sodass Sie es überprüfen können.

Wenn Sie mit Laravel oder Symfony gearbeitet haben, werden Sie die Muster erkennen, die Nibiru nicht hat. Das ist der Punkt.


In Laravel befindet sich die Seitenkopie in Blade-Dateien ({{ __('page.title') }}) oder in der Übersetzungsdatei JSON. Um sie zu ändern, bearbeitet ein Entwickler eine Datei und führt einen Deployment durch. In Nibiru befindet sich die Seitenkopie in der Datenbank, mit dem Schlüssel <controller>/<action> + Sprache, und Redakteure aktualisieren sie über die Admin-Oberfläche ohne Code zu berühren.

data.maschinen-stockert.de/application/controller/maschineController.php
public function pageAction()
{
$controllerPath = $this->getController() . '/' . $this->getRequest('_action', 'page');
foreach (Cms::init($this->getController())
->loadCmsTemplateTextsByControllerPath($controllerPath, $this->language)
as $t) {
View::assign([
$t['cms_template_texts_text_identifier']
=> $t['cms_template_texts_text_content']
]);
}
}

Gesamtwirkung: Marketing ändert einen Headline im Editor, aktualisiert die Seite und veröffentlicht. Kein PR, kein Deployment, kein Entwickler im Begriff. Das ist kein CMS-Plugin – das Modul Cms des Frameworks ist eingebaut.

→ Siehe Module für die Art und Weise, wie das CMS-Modul sich zusammensetzt.


2. Module bestehen aus Merkmalen (Traits), nicht aus einem Dienstcontainer

Abschnitt betitelt „2. Module bestehen aus Merkmalen (Traits), nicht aus einem Dienstcontainer“

Laravels Antwort auf eine Klasse mit 5.000 Zeilen sind Service-Provider und Dependency Injection. Nibirus Antwort ist Traits.

Das CMS-Modul auf maschinen-stockert.de besteht aus 13 Merkmalen, wobei jedes ein einzelner Verantwortungsbereich ist — CmsStore, TextsForm, PageBuilderForm, CmsPageStructureModifier, FormElements, … Die Hauptklasse Cms umfasst 30 Zeilen, die sie zusammenfügen. Das Hinzufügen einer neuen Funktion lautet “eine Merkmale erstellen, einbinden, fertig.”

application/module/cms/cms.php
class Cms implements Interfaces\Cms, SplSubject
{
use Traits\CmsStore;
use Traits\TextsForm;
use Traits\PageBuilderForm;
use Traits\CmsPageStructureModifier;
use Traits\FormElements;
/ …8 more traits
}

Gesamtwirkung: keine Konstruktor-Injektion, keine Dienstleistungsanbieterregistrierung, keine Aufrufe von bind() / singleton() in einer Konfigurationsdatei. Ein neuer Entwickler kann nach dem Trait-Namen suchen und alle Aufrufer sehen.


Eloquent und Doctrine verpflichten zu einer Gebühr: jede geladene Beziehung könnte eine N+1-Abfrage sein, und jede Aggregation, die in PHP übertragen wird, ist Speicher verschwendet. Nibiru setzt sich auf die Datenbank, wo sie stark ist.

-- application/module/machineryscout/traits/machinesElasticSearch.php
SELECT
m.ms_machines_id AS machine_id,
m.ms_machines_name AS machine_name,
JSON_ARRAYAGG(
DISTINCT JSON_OBJECT(
'attribute_name', ma.ms_machine_attributes_attribute_name,
'attribute_value', mav.ms_machine_attribute_values_value,
'attribute_type', ma.ms_machine_attributes_attribute_type
)
) AS attributes,
JSON_ARRAYAGG(DISTINCT mi.ms_machine_images_filename
ORDER BY mi.ms_machine_images_sort_order ASC) AS images
FROM ms_machines m
LEFT JOIN ms_machine_attribute_values mav ON mav.ms_machine_attribute_values_machine_id = m.ms_machines_id
LEFT JOIN ms_machine_attributes ma ON mav.ms_machine_attribute_values_attribute_id = ma.ms_machine_attributes_id
LEFT JOIN ms_machine_images mi ON mi.ms_machines_id = m.ms_machines_id
WHERE m.ms_active = 1
GROUP BY m.ms_machines_id;

Eine Anfrage. Aggregierte JSON-Attribute und Bilder, indiziert aus der Datenbank. Das Ergebnis fließt direkt in Elasticsearch ein, bereit, Bereichsanfragen auf Maße wie “2500 × 1200 mm” zu unterstützen.

Gesamtwirkung: Probleme des Typs N+1 existieren nicht. Überprüfbar, profilierbar und schnell.

→ Siehe Datenbank & Migrationen für den Pdo Adapter, der Abfragen wie diese ausführt.


In Laravel ist die Autorisierung ein Stack: auth Middleware → Policy-Klasse → Gate → Controller. In Nibiru sind es drei Zeilen im Konstruktor:

data.maschinen-stockert.de/application/controller/adminController.php
public function __construct() {
parent::__construct();
$this->user = new User();
$this->acl = new Acl();
$this->acl->init();
$this->user->validate();
}

$this->acl->init() lädt die Rollen-/Berechtigungsmenge für die aktuelle Sitzung. $this->user->validate() überprüft, ob die Sitzung gültig ist. Wenn einer dieser Schritte fehlschlägt, wird die Anfrage die Login-Seite zurückgeben. Berechtigungsprüfungen pro Aktion ($this->acl->can('edit', 'pages')) befinden sich in den Aktionen, die sie benötigen.

Der API-Controller nimmt den umgekehrten Ansatz: eine Whitelist öffentlicher Endpunkte im Konstruktor und Authentifizierung für alles andere.

// public endpoints can be listed up-front, auth wraps the rest.
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
/ public, skip auth
} else {
$this->user = new User();
$this->acl = new Acl();
$this->acl->init();
$this->user->validate();
}

Gesamtwirkung: Die Autorisierung ist ein durchsuchbares Ausdruck. Keine Probleme mit der Reihenfolge des Middlewares. Keine Geheimnisse wie „Warum ist dieser Endpunkt öffentlich?“.

→ Siehe Auth für Details zu Sitzungen und Zugriffssteuerung.


Die Ereignisse von Laravel werden über einen Dispatcher mit Schließlinsen, Listenern und Aufzeichnungen für wartige Jobs ausgegeben. Nibiru verwendet die PHP-Standardbibliothek-Schnittstellen SplSubject / SplObserver. Gleiches Muster, zwei Schnittstellen, keine Abstraktionsebene.

class Machineryscout implements IModule, \SplSubject
{
private \SplObjectStorage $observers;
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);
}
public function indexMachines(): void {
/ …do the work…
$this->notify(); / analytics, cache invalidator, audit log all see it.
}
}

Gesamtwirkung: Zustandsänderungen sind synchron, deterministisch und debugbar. Kein Queue Worker. Keine “hat der Listener gefeuert?” Geheimnis. Fügen Sie einen Observer im Controller hinzu, und er wird verbunden.

→ Siehe Module für das Muster.


Ein einzelner Entwickler oder eine kleine Team kann eine echte Produktions-App bauen und betreiben – wie zum Beispiel eine, die Maschinen in 12 Ländern verkauft – mit weniger Frameworks zu lernen, weniger Abstraktionen zu debuggen und weniger Zeremonien pro Funktion. Der Kompromiss ist, dass Sie lieber möchten, dass das Framework jede Datenbankabfrage verbirgt und jedes Ereignis orchestriert, dann sind Sie an einem anderen Ort glücklicher.

Wenn Sie lieber Ihren Code sehen möchten, nehmen Sie ihn.

Die Präsentation ansehen →