---
title: "¿Por qué Nibiru, no Laravel?"
description: "Cinco cosas que Nibiru hace diferente de Laravel y Symfony — cada una respaldada por código real de producción."
---

La mayoría de las comparaciones de frameworks PHP son opiniones personales. Esta es una que se puede buscar con `grep`.

Los cinco diferenciadores a continuación se extraen de un sitio web real que genera ingresos: **[maschinen-stockert.de](/en/showcase/projects/)** — una plataforma multilingüe de mercado de maquinaria industrial. **36 controladores, 18 módulos, 348 plantillas, 161 migraciones SQL, 37,369 líneas de PHP** en dos repositorios. Cada afirmación se vincula a una referencia de archivo:línea dentro de los códigos fuente, por lo que puedes verificarlo.

Si has trabajado con Laravel o Symfony, reconocerás los patrones que Nibiru *no* tiene. Ese es el punto.

---

## 1. Capa de CMS integrada en el framework

En Laravel, el texto de las páginas reside en archivos Blade (`{{ __('page.title') }}`) o en un archivo JSON de traducción. Para cambiarlo, un desarrollador edita un archivo y lo despliega. En Nibiru, el texto de las páginas reside en la base de datos, claveado por `<controller>/<action>` + idioma, y los editores lo actualizan desde la interfaz de administración **sin tocar código**.
```php
// 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']
        ]);
    }
}
```
**Efecto neto**: las modificaciones de marketing cambian un titular en el editor, refrescan la página y se envían. Sin PR, sin despliegue, sin que el desarrollador esté involucrado. Eso no es un plugin del CMS — el módulo `Cms` del framework está integrado.

→ Consulte [Módulos](/es/core/modules/) para ver cómo se compone el módulo del CMS.

---

## 2. Los módulos se componen mediante rasgos, no mediante un contenedor de servicios

La respuesta de Laravel a una clase de 5,000 líneas es los proveedores de servicios e inyección de dependencias. La respuesta de Nibiru es **tratos**.

El módulo CMS en `maschinen-stockert.de` está compuesto por **13 rasgos**, cada uno con una sola responsabilidad — `CmsStore`, `TextsForm`, `PageBuilderForm`, `CmsPageStructureModifier`, `FormElements`, … La clase principal `Cms` es de 30 líneas que los integra. Agregar una nueva característica es *"crear un rasgo, incluirlo, listo."*
```php
// 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
}
```
**Efecto neto**: cero inyección de constructores, cero registro de proveedores de servicios, cero llamadas a `bind()` / `singleton()` en un archivo de configuración. Un nuevo desarrollador puede buscar el nombre del trait y ver todos los llamadores.

---

## 3. Consulta directa con agregación de JSON, no un ORM

Eloquent y Doctrine vienen con un costo: cada relación cargada es potencialmente una consulta N+1, y cada agregación insertada en PHP es memoria desperdiciada. Nibiru se apoya en la base de datos donde es fuerte.
```sql
-- 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;
```
Una consulta. Atributos y imágenes agregados en JSON, indexados desde la base de datos. El resultado se alimenta directamente a Elasticsearch, listo para respaldar consultas de rango en dimensiones como *"2500 × 1200 mm"*.

**Efecto neto**: Los problemas N+1 no existen. Auditoriable, perfilable, rápido.

→ Consulta [Base de Datos y Migraciones](/es/core/database/) para el adaptador `Pdo` que ejecuta consultas como esta.

---

## 4. Control de acceso basado en roles sin cadenas de middleware

En Laravel, la autorización es una pila: `auth` middleware → clase de política → puerta → controlador. En Nibiru, son tres líneas en el constructor:
```php
// 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()` carga el conjunto de roles / permisos para la sesión actual. `$this->user->validate()` verifica que la sesión esté bien. Si cualquiera falla, la solicitud devuelve la página de inicio de sesión. Las verificaciones por acción (`$this->acl->can('edit', 'pages')`) se encuentran en las acciones que las necesitan.

El controlador de la API toma un enfoque inverso: una lista blanca de puntos finales públicos en el constructor, autenticación requerida para todo lo demás.
```php
// 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();
}
```
**Efecto neto**: la autorización es una expresión que se puede buscar con grep. Sin errores de ordenamiento en el middleware. Sin misterios como "¿por qué este punto final es público?".

→ Consulte [Autenticación](/es/core/auth/) para detalles sobre sesiones y ACL.

---

## 5. Patrón observador, no despachador de eventos

Los eventos de Laravel se propagan mediante un despachador con cierres, oyentes y registros de trabajos en cola. Nibiru utiliza las interfaces estándar de PHP `SplSubject` / `SplObserver`. Mismo patrón, dos interfaces, sin capa de abstracción.
```php
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.
    }
}
```
**Efecto neto**: los cambios de estado son sincrónicos, deterministas y depurables. Sin trabajador de cola. Sin misterio de "¿se disparó el oyente?". Agrega un observador en el controlador y está conectado.

→ Consulte [Módulos](/es/core/modules/#el-patrón-observador) para el patrón.

---

## ¿Qué obtienes con esto, en dos oraciones?

Un desarrollador solitario o un pequeño equipo puede construir y *operar* una aplicación de producción real —como una que vende maquinaria industrial en 12 países— con **menos marcos para aprender**, **pocas abstracciones para depurar** y **menos ceremonia por característica**. El contratiempo es que si prefieres que el marco oculte cada consulta de base de datos y orqueste cada evento, estarás más feliz en otro lugar.

Si prefieres ver tu código, tómalo.

→ [Lee la presentación →](/es/presentacion/proyectos/)
