Inglés

Descubriendo JUX 3.0 (Parte VIII)

28 / 03 / 2026

Curso Acelerado - Tu primer Módulo Maestro-Detalle en 5 Minutos

Llegados a este punto de la serie, ya sabemos cómo el motor interno de JUX tritura dependencias, cómo intercepta inyecciones y cómo escupe código puro. Hoy vamos a mancharnos las manos. Vamos a construir un módulo completo para gestionar "Facturas" y sus "Líneas de Factura" (la clásica relación Maestro-Detalle). Sin CLI, sin artisan make:module, y sin escribir una sola línea de HTML.

En el mundo moderno, para empezar un proyecto te pasas la primera hora peleando con andamiajes automatizados. En JUX, el andamiaje es groseramente empírico: Creas una carpeta.

Si quieres crear un módulo llamado "Facturación", creas una carpeta en _modules_/facturacion. Fin del Setup.


1. La Anatomía del Módulo JUX

Un módulo básico asusta por su extrema simplicidad. Suelen componerse de un puñado de archivos que se enlazan orgánicamente:

  • init.php: (Opcional) El lugar donde declaras tus permisos, inyectas variables temporales y preparas el entorno local del módulo antes de que se renderice nada.
  • run.php: El corazón visual. Es el archivo que se devuelve por defecto cuando el usuario llama a la URL normal (`/facturacion`). Ojo, no es una plantilla Twig: es PHP nativo.
  • ajax.php: El buzón ciego. Cualquier cosa enviada asíncronamente vía wquery desde tu Frontend aterrizará directamente aquí. Devuelves un JSON y te olvidas de las "Rutas POST" externas.
  • TABLE_FACTURAS.php: Los archivos mágicos de la clase Table. Aquí se forja la estructura de nuestra base de datos y la interfaz de datos CRUD.

2. Armando el Maestro (Las Facturas)

No vamos a abrir PhpMyAdmin. Vamos a decirle al framework que lo haga él solo. Creamos un archivo llamado TABLE_FACTURAS.php en nuestra carpeta. Aquí definimos nuestro God Object de andamiaje.

<?php

$tabla = new TableMySql('FACTURAS');        

$tabla->addCols([
    $tabla->field('ID',           'int')->len(5)->editable(false)->hide(true),
    $tabla->field('NUMERO',   'varchar')->len(20)->required(true)->searchable(true),
    $tabla->field('FECHA',       'date')->required(true)->filtrable(true),
    $tabla->field('CLIENTE',  'varchar')->len(100)->required(true)->searchable(true),
    $tabla->field('TOTAL',    'decimal')->len('10,2')->calculated(true)->editable(false)
]);

$tabla->showtitle = true;
$tabla->orderby = 'ID DESC';

// Tabla detalle
$tabla->detail_tables = array('FACT_FACTURAS_LINEAS');    

// Permisos conectados al núcleo de seguridad nativo
$tabla->perms['view']   = $juxACL->hasPermission('ver_facturas');
$tabla->perms['edit']   = $juxACL->hasPermission('editar_facturas');

// Instanciamos la clase events (nuestro clon de Delphi)
class facturas_Events extends defaultTableEvents implements iEvents{ 
    
    function OnCalculate($owner,&$row){
        // Calculamos el total de la factura sumando sus líneas hijas "al vuelo"
        $row['TOTAL'] = $owner->getFieldValue("SELECT SUM(PRECIO * CANTIDAD) FROM FACTURAS_LINEAS WHERE ID_FACTURA = ".$row['ID']);
        $row['TOTAL'] = '' . number_format((float)$row['TOTAL'], 2) . ' €';
    }
}

$tabla->events = new facturas_Events();
    

La maravilla de este archivo no es que genere los `<input>` y la tabla DataGrid automáticamente. La maravilla es que, si la tabla no existe en MySQL o SQLite al entrar al módulo, JUX coge este esquema, la construye por su cuenta en el motor de DB, y después la renderiza. En frío.

3. Armando el Detalle (Líneas de Factura)

Una factura sin cosas cobradas no sirve para nada. Ahora creamos TABLE_FACTURAS_LINEAS.php. La clave radicará en conectarlo mediante un menú desplegable asociado al padre.

<?php

$tabla = new TableMySql('FACTURAS_LINEAS');        

$tabla->addCols([
    $tabla->field('ID',               'int')->len(5)->editable(false)->hide(true),
    $tabla->field('ID_FACTURA',        'id')->len(7)->required(true)->hide(true),   // Clave foranea
    $tabla->field('CONCEPTO',     'varchar')->len(200)->required(true),
    $tabla->field('CANTIDAD',         'int')->len(5)->required(true),
    $tabla->field('PRECIO',       'decimal')->len('10,2')->required(true)
]);

// $parent es el id de la factura padre, que JUX le pasa automáticamente a esta tabla hija para que podamos forzar la relación
$tabla->setParent('ID_FACTURA',$parent);


// ... Definición de permisos como arriba ...
$tabla->perms['view']   = $juxACL->hasPermission('ver_facturas');
$tabla->perms['edit']   = $juxACL->hasPermission('editar_facturas');    
    

4. Dando vida al Frankenstein (run.php)

Ya tenemos nuestros esquemas (los modelos MVC tradicionales estarían llorando viendo este nivel de acoplamiento táctico). Ahora solo nos queda llamar al andamiaje para que dispare HTML renderizado por pantalla.

En el archivo run.php (la interfaz de nuestra App), literalmente hacemos lo siguiente:

<?php    
    //Visualiza tus recibos y líneas de forma maestra.
    echo '<h1>facturación</h1>';
    Table::show_table('FACTURAS');
    Table::show_table('FACTURAS_LINEAS');
    

Como mínimo necesitaremos unos pocos archivos mas:


index.php
<?php
    // El entrypoint único para el módulo
    if (OUTPUT=='ajax'){

        include(SCRIPT_DIR_MODULE.'/ajax.php');
            
    }else{

        include(SCRIPT_DIR_MODULE.'/run.php');

    }        

init.php
<?php   
    // Indicamos qué clase se encarga del CRUD
    $db_engine = 'scaffold';

ajax.php
<?php  
    // Enviamos las llamadas ajax a la clase Table
    include(SCRIPT_DIR_CLASSES.'/scaffold/ajax.php');  

Total, 6 archivos con cuatro líneas de código y ... he aquí el resultado. Y ni siquiera hemos necesitado crear las tablas en sitio alguno.

Conclusión de la Masterclass

Hacer lo mismo en Next.js requería un Schema de Prisma, tres migraciones CLI, definir un router tRPC, tipar las inferencias en TypeScript, enlazar Zustand, programar un Grid de Ag-Grid e instalar Tailwind. ¿Tiempo consumido? Un día o dos para dejarlo prístino.

En JUX Framework, has creado dos archivos PHP dictando columnas, y un run.php que tira del hilo. El sistema auto-crea la tabla MySQL en base a tu array, autogenera combos selectores cruzados (el `toarray`), inyecta validadores obligatorios (JS nativo) y blinda permisos según el AreaACL. Tiempo de reloj: Cinco minutos cronometrados.

Esto no es programar un CRUD, es encender una fotocopiadora industrial. Y en el mundo del Freelance y las pymes, la herramienta que saca fotocopias legibles en cinco minutos siempre destronará a la herramienta académica que tarda tres días.