---
title: "Controladores"
description: "Escribiendo controladores de Nibiru — el ciclo de vida de la acción, View::assign y patrones del código de producción."
---

Un controlador de Nibiru es una clase que extiende `Nibiru\Adapter\Controller`, reside en `application/controller/<nombre>Controller.php`, y se carga automáticamente por el [Dispatcher](/core/dispatcher/) cuando llega una URL como `/<nombre>/...`.

## Anatomía de un controlador
```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 []; }
}
```
## El ciclo de vida de la acción

Cuando el despachador invoca un controlador, llama a los métodos en este orden fijo:

1. **`navigationAction()`** — poblar menús globales, migas de pan, navegación consciente del rol.
2. **`<verb>Action()`** — solo si `?_action=<verb>` está establecido o la URL tiene un segundo segmento que nombra una acción.
3. **`pageAction()`** — último llamado antes del renderizado.

Tanto `navigationAction()` como `pageAction()` son **siempre llamados**, incluso para acciones desconocidas. Esto es conveniente (nunca necesitas verificarlo), pero puede sorprenderte si asumes que las acciones son exclusivas.

## Hablando con la vista

`View::assign(['key' => $value, ...])` es cómo llega la información a las plantillas. Es estático y puede llamarse tantas veces como desees — las llamadas posteriores sobrescriben a las anteriores.
```php
View::assign(['title' => 'Products']);
View::assign(['products' => $list]);

// In templates/products.tpl:
//   {$title}     →  "Products"
//   {$products}  →  the array
```
Ayudantes de conveniencia del controlador base:
```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']
```
Estos existen porque `Controller` es amigable con `final`: puedes simularlos en pruebas sustituyendo una clase hija.

## Reenvío

Para redirigir dentro de una acción:
```php
View::forwardTo('/login');             // 302 to the URL, exits
View::forwardToJsonHeader();           // sets Content-Type: application/json
```
`forwardToJsonHeader()` es el patrón canónico para puntos finales JSON — establece la cabecera, asigna `data`, y devuelve. La capa de vista se encarga del resto.

## Múltiples acciones por controlador

Nibiru está feliz de alojar cualquier número de acciones por controlador. El TPMS `erpController` de producción tiene `pageAction`, `navigationAction`, además de `syncAction`, `statusAction`, `dryRunAction`, `cancelAction`, etc. — cada una invocada a través de `?_action=sync` o `/erp/sync`.
```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]);
}
```
## Trabajando con módulos

Los controladores son delgados. La mayoría de la lógica debe residir en los módulos y sus complementos:
```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');
        }
    }
}
```
Este patrón de *delegación* es consistente en todas las aplicaciones de demostración: los controladores orquestan y los módulos realizan el trabajo.

## Contenido multilingüe / gestionado por CMS

Un patrón de `prod.maschinen-stockert.de` — carga todo el texto en página desde una tabla CMS claveada por la ruta del controlador:
```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']
        ]);
    }
}
```
Resultado: los no desarrolladores pueden cambiar el texto sin tocar el código. El módulo de CMS posee la tabla y la interfaz de edición; el controlador solo carga las cadenas.

## Generando controladores

La CLI genera un controlador y su plantilla en un solo paso:
```bash
./nibiru -c products
```
→ `application/controller/productsController.php`
→ `application/view/templates/products.tpl`

Ambos están poblados con el esqueleto canónico. Estás listo para escribir.
