---
title: "Controller"
description: "Schreiben von Nibiru-Controllern – der Aktion Lebenszyklus, View::assign und Muster aus Produktionscode."
---

Ein Nibiru-Controller ist eine Klasse, die von `Nibiru\Adapter\Controller` erbt, sich in `application/controller/<name>Controller.php` befindet und automatisch vom [Dispatcher](/core/dispatcher/) geladen wird, wenn eine URL wie `/<name>/...` ankommt.

## Anatomie eines Controllers
```php
<?php
namespace Nibiru;
use Nibiru\Adapter\Controller;

class productsController extends Controller
{
    public function pageAction() {
        View::assign([
            'title'    => 'Products',
            'products' => $this->loadProducts(),
        ]);
    }

    public function navigationAction() {
        JsonNavigation::getInstance()->loadJsonNavigationArray();
    }

    public function detailAction() {
        $id = (int) ($_REQUEST['id'] ?? 0);
        View::assign(['product' => $this->loadProduct($id)]);
    }

    private function loadProducts(): array { /* ... */ return []; }
    private function loadProduct(int $id): array { /* ... */ return []; }
}
```
## Der Lebenszyklus der Aktion

Wenn der Dispatcher einen Controller aufruft, ruft er die Methoden in dieser festgelegten Reihenfolge auf:

1. **`navigationAction()`** — füllen Sie globale Menüs, Breadcrumbs und rollebasierte Navigation.
2. **`<verb>Action()`** — nur wenn `?_action=<verb>` gesetzt ist oder die URL einen zweiten Abschnitt hat, der eine Aktion benennt.
3. **`pageAction()`** — letzter Aufruf vor dem Rendern.

Sowohl `navigationAction()` als auch `pageAction()` werden **immer aufgerufen**, selbst für unbekannte Aktionen. Dies ist praktisch (Sie müssen nie überprüfen), kann aber überraschen, wenn Sie annehmen, dass Aktionen exklusiv sind.

## Kommunikation mit der Ansicht

`View::assign(['key' => $value, ...])` ist die Art und Weise, wie Daten an Templates gelangen. Es ist statisch und kann so oft aufgerufen werden, wie Sie möchten – später Aufrufe überschreiben frühere Aufrufe.
```php
View::assign(['title' => 'Products']);
View::assign(['products' => $list]);

// In templates/products.tpl:
//   {$title}     →  "Products"
//   {$products}  →  the array
```
Hilfsfunktionen aus dem Basiscontroller:
```php
$this->getRequest('id', false);   // $_REQUEST['id'] ?? false
$this->getPost('email', '');      // $_POST['email']  ?? ''
$this->getGet('page', 1);         // $_GET['page']    ?? 1
$this->getServer('REQUEST_URI');  // $_SERVER['REQUEST_URI']
$this->getFiles('upload');        // $_FILES['upload']
$this->getSession('auth');        // $_SESSION['auth']
```
Diese bestehen, weil `Controller` `final`-freundlich ist: Sie können sie in Tests durch eine untergeordnete Klasse ersetzen.

## Weiterleitung

Um innerhalb einer Aktion umzuleiten:
```php
View::forwardTo('/login');             // 302 to the URL, exits
View::forwardToJsonHeader();           // sets Content-Type: application/json
```
`forwardToJsonHeader()` ist das kanonische Muster für JSON-Endpunkte – setzen Sie den Header, weisen Sie `data` zu und geben Sie zurück. Der Anzeigebereich kümmert sich danach um die Reste.

## Mehrere Aktionen pro Controller

Nibiru freut sich, eine beliebige Anzahl von Aktionen pro Controller zu hosten. Der TPMS-Controller `erpController` aus der Produktion hat `pageAction`, `navigationAction`, sowie `syncAction`, `statusAction`, `dryRunAction`, `cancelAction` usw. — jeweils über `?_action=sync` oder `/erp/sync` aufgerufen.
```php
// /erp/sync  →  $_REQUEST['_action'] = 'sync'
public function syncAction(): void
{
    View::forwardToJsonHeader();
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        View::assign(['data' => ['success' => false, 'error' => 'POST method required']]);
        return;
    }
    $result = AlphaplanSyncService::getInstance()->syncAbDocuments();
    View::assign(['data' => $result]);
}
```
## Arbeiten mit Modulen

Controller sind dünn. Die meisten Logiken sollten in Modulen und deren Plugins leben:
```php
namespace Nibiru;
use Nibiru\Adapter\Controller;
use Nibiru\Module\Users\Plugin\User;

class loginController extends Controller
{
    private User $user;

    public function __construct() {
        parent::__construct();
        $this->user = new User();
    }

    public function authAction() {
        if (!$this->user->isAuthorized()) {
            View::assign(['loginForm' => $this->user->loginForm()]);
        } else {
            View::forwardTo('/index');
        }
    }
}
```
Dieser *Delegierungs*-Muster ist konsistent in den Showcase-Anwendungen: Controller orchestrieren, Module führen die Arbeit aus.

## Mehrsprachige / CMS-gesteuerter Inhalt

Ein Muster von `prod.maschinen-stockert.de` — Laden Sie alle auf der Seite befindlichen Texte aus einer CMS-Tabelle, die nach dem Controller-Pfad indiziert ist:
```php
public function pageAction() {
    $controllerPath = $this->getController()
        . '/' . $this->getRequest('_action', 'page');

    $texts = Cms::init($this->getController())
        ->loadCmsTemplateTextsByControllerPath($controllerPath, $this->language);

    foreach ($texts as $t) {
        View::assign([
            $t['cms_template_texts_text_identifier'] =>
            $t['cms_template_texts_text_content']
        ]);
    }
}
```
Ergebnis: Nicht-Entwickler können den Text ändern, ohne den Code zu berühren. Das CMS-Modul besitzt die Tabelle und die Editor-Benutzeroberfläche; der Controller lädt einfach nur Zeichenfolgen.

## Controller generieren

Die CLI erstellt einen Controller und seine Vorlage in einem Schritt:
```bash
./nibiru -c products
```
→ `application/controller/productsController.php`
→ `application/view/templates/products.tpl`

Beide sind mit dem kanonischen Gerüst bevölligt. Sie sind bereit zu schreiben.
