Guía de referencia para el desarrollo de nuevas funcionalidades en el framework SIU Toba.
- Arquitectura General
- Flujo de Ejecución de un Request
- Componentes Principales
- CI — Controlador de Interface
- CN — Controlador de Negocio
- Componentes de UI
- Persistencia y Base de Datos
- Servicios Globales — Clase
toba - Navegación y Vínculos
- Patrones Frecuentes
- Convenciones de Nombres
- Estructura de Directorios
- Referencia de Archivos Clave
SIU Toba es un framework PHP orientado a componentes. Cada "ítem" del menú de la aplicación corresponde a un par:
- CI (
toba_ci): Controlador de Interface. Maneja la presentación, la interacción con el usuario, y el ciclo de vida de la pantalla. - CN (
toba_cn): Controlador de Negocio. Encapsula la lógica de negocio y el acceso a datos.
Los componentes de UI (formularios, grillas, filtros, etc.) son dependencias del CI. El CI los configura y responde a sus eventos a través de callbacks con nombres convencionales.
Request HTTP
└── toba_nucleo (kernel, singleton)
└── toba_solicitud_web
├── CI (Controlador de Interface)
│ ├── dep('formulario') → toba_ei_formulario
│ ├── dep('cuadro') → toba_ei_cuadro
│ └── dep('pantalla') → toba_ei_pantalla
└── CN (Controlador de Negocio)
└── dep('datos') → toba_datos_tabla / toba_ap_tabla_db
Cada request web pasa por las siguientes etapas, en orden:
- Se recupera memoria sincronizada de sesión (
cargar_memoria()). - Se cargan las propiedades prefijadas con
s__para restaurar el estado. - Los componentes no deben lógica de negocio aquí (el constructor del CI es
final).
- Se restaura el estado de sesión desincronizado (
recuperar_estado_sesion()). - Se llama a
ini__operacion()sólo la primera vez que se accede a la operación. - Se llama a
ini()en cada request.
- Se atienden los eventos generados en el request anterior (clic de botón, selección de fila, etc.).
- Los eventos de las dependencias se propagan recursivamente.
- Luego se llama a
post_eventos(). - Finalmente se controla si hubo cambio de pantalla/solapa.
- Se invocan los callbacks
conf__<id_componente>()para configurar cada dependencia antes de renderizar. - Se genera el HTML/PDF/XML de respuesta.
- Se llama a
fin(). - Se persiste la memoria y el estado de sesión.
- Se destruyen los componentes hijos recursivamente.
| Clase | Prefijo | Rol |
|---|---|---|
toba_ci |
ci |
Controlador de Interface (presentación) |
toba_cn |
cn |
Controlador de Negocio (lógica) |
toba_ei_formulario |
form |
Formulario de edición |
toba_ei_cuadro |
cuadro |
Grilla/tabla de datos |
toba_ei_filtro |
— | Filtro de búsqueda |
toba_ei_pantalla |
— | Contenedor de solapas/tabs |
toba_ei_arbol |
— | Vista de árbol |
toba_datos_tabla |
— | Tabla de datos en memoria |
toba_ap_tabla_db |
— | Patrón de acceso a tabla DB |
El constructor de toba_ci está declarado como final:
// En toba_ci.php (NO modificar):
final function __construct($id)
{
$this->set_propiedades_sesion(array('_ini_operacion','_dependencias_ci', '_metodos_ajax'));
parent::__construct($id);
$this->_nombre_formulario = "formulario_toba";
}Esto significa que no podés sobreescribir el constructor en tu CI. En su lugar, el framework provee las siguientes alternativas para inicializar propiedades:
Ideal para inicializar propiedades que deben persistir mientras el usuario trabaja en esa operación. Si necesitás que una propiedad apunte a un objeto o tenga un valor inicial específico, hacelo aquí:
class ci_mis_datos extends toba_ci
{
protected $s__cliente; // persistirá en sesión automáticamente
function ini__operacion()
{
// Se ejecuta solo la primera vez que se accede a este item
$this->s__cliente = array('id' => null, 'nombre' => '');
}
}Ideal para propiedades que se recalculan o regeneran en cada pedido (por ejemplo, servicios, DAOs, o cualquier objeto que no se pueda serializar):
class ci_mis_datos extends toba_ci
{
// NO usar s__ aquí si el objeto no es serializable
protected $servicio_externo;
function ini()
{
// Se ejecuta en cada request; el objeto se crea de nuevo cada vez
$this->servicio_externo = new mi_servicio_rest();
}
}Si la propiedad sólo se necesita en ciertos contextos, podés inicializarla a demanda:
class ci_mis_datos extends toba_ci
{
protected $repo;
function get_repo()
{
if (! isset($this->repo)) {
$this->repo = new repositorio_clientes();
}
return $this->repo;
}
function conf__cuadro(toba_ei_cuadro $cuadro)
{
$cuadro->set_datos($this->get_repo()->listar_todos());
}
}Si tenés un objeto que necesitás persistir en sesión y además tiene referencias a componentes Toba, podés hacer que herede de toba_serializar_propiedades. Esa clase implementa __sleep() y __wakeup() para serializar correctamente.
class mi_contexto extends toba_serializar_propiedades
{
public $cliente_id;
public $filtro_activo;
// Los componentes toba se serializan con lógica especial
}
class ci_mis_datos extends toba_ci
{
protected $s__contexto; // Se guardará en sesión automáticamente
function ini__operacion()
{
$this->s__contexto = new mi_contexto();
$this->s__contexto->cliente_id = null;
}
}Importante: Los componentes Toba (
toba_ci,toba_cn, etc.) no son serializables directamente. Si intentás guardarlos en una propiedads__, obtendrás un error. Para referencias a componentes, usá el métodoini()para recrearlos en cada request, o usátoba_serializar_propiedades.
Toba ofrece dos mecanismos para persistir el estado entre requests:
Cualquier propiedad cuyo nombre empiece con s__ se persiste automáticamente en sesión. No se necesita ninguna configuración adicional.
class ci_ejemplo extends toba_ci
{
protected $s__filtro; // se guarda y restaura automáticamente
protected $s__pagina_actual; // ídem
protected $s__datos_form; // ídem
function evt__formulario__modificacion($datos)
{
$this->s__datos_form = $datos; // en el próximo request estará disponible
}
}Podés marcar propiedades con nombres arbitrarios para persistencia explícita:
class ci_ejemplo extends toba_ci
{
protected $pasadas_por_solapa;
function ini__operacion()
{
$this->pasadas_por_solapa = array('1' => 0, '2' => 0);
}
// Método OBSOLETO pero todavía funciona en 2.7.x:
function mantener_estado_sesion()
{
$propiedades = parent::mantener_estado_sesion();
$propiedades[] = 'pasadas_por_solapa';
return $propiedades;
}
// Alternativa moderna (desde toba_componente):
// llamar $this->set_propiedades_sesion(array('pasadas_por_solapa')) en ini()
}Regla práctica: Usá
s__como prefijo siempre que sea posible. Es la forma idiomática y más clara de indicar que una propiedad es parte del estado de sesión.
Constructor (final, no sobreescribir)
↓
ini__operacion() ← Solo la 1ra vez; ideal para inicializar estado persistente
↓
ini() ← Cada request; ideal para crear objetos no-serializables
↓
[disparo de eventos del request anterior]
evt__<comp>__<evento>() ← Handler de evento
↓
post_eventos() ← Hook posterior a todos los eventos
↓
[cambio de pantalla si aplica]
evt__<pantalla>__salida()
evt__<pantalla>__entrada()
↓
conf__<comp>() ← Configura cada componente antes de renderizar
↓
fin() ← Justo antes de destruir; estado ya en sesión
↓
destruir() ← Framework persiste sesión y destruye objetos
El método conf__<id_componente>() se ejecuta en la etapa de servicios, justo antes de que el componente genere su HTML. Es el lugar para:
- Proveer datos al componente.
- Modificar su apariencia o comportamiento.
- Configurar eventos dinámicamente.
class ci_listado extends toba_ci
{
protected $s__filtro;
// Retornando un array, se setean los datos del componente directamente
function conf__cuadro()
{
return $this->obtener_datos();
}
// Recibiendo el componente como parámetro (type-hint habilitado)
function conf__cuadro(toba_ei_cuadro $cuadro)
{
$cuadro->set_datos($this->obtener_datos());
$cuadro->evento('alta')->desactivar();
}
// Retornando un array desde conf__ de un formulario
function conf__form_filtro()
{
if (isset($this->s__filtro)) {
return $this->s__filtro;
}
}
// conf sin id de componente: se llama antes de procesar cualquier dependencia
function conf()
{
// Útil para lógica global de la pantalla actual
if ($this->get_id_pantalla() == 'detalle') {
$this->pantalla()->eliminar_evento('cancelar');
}
}
}Nota sobre el type-hint: En los métodos conf__, podés recibir el componente como parámetro con type-hint. Esto también funciona para eventos:
function conf__formulario(toba_ei_formulario $form) { ... }
function evt__cuadro__seleccion($fila) { ... }Los eventos siguen el patrón evt__<id_componente>__<id_evento>:
class ci_abm extends toba_ci
{
protected $s__datos;
protected $s__clave;
// Evento de modificación de formulario
function evt__formulario__modificacion($datos)
{
$this->s__datos = $datos;
}
// Evento de selección de fila en cuadro (recibe la fila seleccionada)
function evt__cuadro__seleccion($fila)
{
$this->s__clave = $fila['id'];
$this->set_pantalla('detalle');
}
// Evento de baja en cuadro
function evt__cuadro__baja($fila)
{
toba::notificacion()->agregar("Eliminado: {$fila['nombre']}", 'info');
}
// Evento de filtrar en filtro
function evt__filtro__filtrar($datos)
{
$this->s__filtro = $datos;
}
// Evento de entrada a una solapa (id de pantalla = 'edicion')
function evt__edicion__entrada()
{
// El usuario acaba de entrar a la solapa 'edicion'
}
// Evento de salida de una solapa
function evt__edicion__salida()
{
// Se puede lanzar una excepción toba_error_usuario para cancelar la navegación
}
// Evento propio del CI (sin componente)
function evt__guardar()
{
$this->cn()->procesar($this->s__datos);
toba::notificacion()->agregar('Guardado correctamente', 'exito');
$this->set_pantalla('listado');
}
}function evt__procesar()
{
// Acceder al CN asociado
$this->cn()->procesar($this->s__datos);
// Navegar a otra pantalla/solapa
$this->set_pantalla('confirmacion');
// Mostrar notificaciones al usuario
toba::notificacion()->agregar('Operación exitosa', 'exito'); // 'exito', 'info', 'error'
// Lanzar error de usuario (muestra mensaje sin romper la app)
throw new toba_error_usuario("El campo X es obligatorio");
}// Obtener una dependencia por su ID
$this->dep('formulario'); // equivalente a $this->dependencia('formulario')
$this->dep('cuadro');
$this->dep('filtro');
// Verificar si existe una dependencia
$this->existe_dependencia('detalle');
// Acceder a la pantalla actual
$this->pantalla();
// Acceder a una pantalla específica
$this->dep('ci_hijo')->pantalla('nombre_pantalla');
// Operaciones comunes sobre dependencias
$this->dep('formulario')->set_datos($datos);
$this->dep('cuadro')->deseleccionar();
$this->dep('pantalla')->eliminar_evento('cancelar');
// Acceder a un EF específico dentro de un formulario
$this->dep('formulario')->ef('nombre_campo')->desactivar();
$this->dep('formulario')->ef('nombre_campo')->set_estado($valor);Para operaciones asíncronas, definí métodos con el prefijo ajax__. Son invocados desde JavaScript:
class ci_busqueda extends toba_ci
{
// PHP: método invocado por AJAX
function ajax__buscar_cliente($parametros, toba_ajax_respuesta $respuesta)
{
$datos = $this->cn()->buscar($parametros['texto']);
$respuesta->set($datos);
}
// PHP: retornar HTML
function ajax__cargar_detalle($id, toba_ajax_respuesta $respuesta)
{
$html = '<div>Detalle del elemento ' . htmlspecialchars($id) . '</div>';
$respuesta->set($html);
}
// JS: cómo invocarlo desde extender_objeto_js()
function extender_objeto_js()
{
$id_js = toba::escaper()->escapeJs($this->objeto_js);
echo "
{$id_js}.evt__btn_buscar = function() {
var params = { texto: this.dep('form').ef('busqueda').get_estado() };
// Llamada AJAX: método, parámetros, contexto, callback
this.ajax('buscar_cliente', params, this, this.mostrar_resultados);
return false; // evita el submit normal
};
{$id_js}.mostrar_resultados = function(datos) {
// datos es lo que se pasó a $respuesta->set(...)
console.log(datos);
};
";
}
}Tipos de llamadas AJAX disponibles:
this.ajax(metodo, params, contexto, callback)— retorna datos serializadosthis.ajax_html(metodo, params, nodo_html)— inyecta HTML en un nodothis.ajax_cadenas(metodo, params, contexto, callback)— múltiples cadenas nombradas
El CN encapsula la lógica de negocio. También tiene constructor final.
class cn_clientes extends toba_cn
{
protected $s__datos;
function ini()
{
// Inicialización en cada request
}
// Método de negocio invocado desde el CI
function procesar($datos)
{
// Transacción abierta automáticamente por el framework
$this->dep('tabla_clientes')->set_datos($datos);
}
// Validación de datos
function evt__validar_datos()
{
if (empty($this->dep('tabla_clientes')->get_datos())) {
throw new toba_error_usuario('No hay datos para procesar');
}
}
// Procesamiento específico (se ejecuta dentro de una transacción)
function evt__procesar_especifico()
{
// La sincronización con la DB ocurre automáticamente en destruir()
}
}Ciclo de procesamiento del CN:
- CI llama
$this->cn()->procesar()o$this->cn()->cancelar() procesar()abre transacción → llamaevt__validar_datos()→evt__procesar_especifico()→ cierra transacción →limpiar_memoria()cancelar()limpia la memoria y el estado
El formulario presenta campos editables (EF - Elementos de Formulario).
// En el CI:
function conf__formulario(toba_ei_formulario $form)
{
// Proveer datos (array asociativo campo => valor)
$form->set_datos(array(
'nombre' => 'Juan',
'apellido' => 'Pérez',
'activo' => 1,
));
// Configuraciones opcionales
$form->set_ancho_etiqueta('200px');
$form->cambiar_layout(); // cambia disposición de columnas
}
// Alternativa: retornar el array directamente
function conf__formulario()
{
return array('nombre' => 'Juan', 'apellido' => 'Pérez');
}
// Evento de modificación (se dispara cuando el usuario cambia algo y confirma)
function evt__formulario__modificacion($datos)
{
// $datos es array asociativo con todos los campos del formulario
$this->s__datos_form = $datos;
}Operaciones sobre EFs individuales:
function conf__formulario(toba_ei_formulario $form)
{
$form->ef('codigo')->desactivar(); // campo solo lectura
$form->ef('descripcion')->ocultar(); // campo invisible
$form->ef('importe')->set_estado(1500); // setear valor puntual
$form->ef('tipo')->agregar_opcion('X', 'Otro tipo');
}La grilla muestra datos tabulares con soporte de paginado, ordenamiento y eventos por fila.
function conf__cuadro(toba_ei_cuadro $cuadro)
{
// Datos: array de arrays asociativos
$datos = array(
array('id' => 1, 'nombre' => 'Rectorado', 'activo' => 1),
array('id' => 2, 'nombre' => 'Secretaría', 'activo' => 0),
);
$cuadro->set_datos($datos);
// Configuraciones opcionales
$cuadro->evento('baja')->set_alineacion_pre_columnas(true);
$cuadro->evento('seleccion')->desactivar();
}
// Alternativa: retornar el array directamente
function conf__cuadro()
{
return $this->cn()->listar();
}
// Evento de selección de fila
function evt__cuadro__seleccion($fila)
{
// $fila es el array asociativo de la fila seleccionada
$this->s__id_seleccionado = $fila['id'];
}
// Evento de baja
function evt__cuadro__baja($fila)
{
$this->cn()->eliminar($fila['id']);
toba::notificacion()->agregar('Registro eliminado', 'info');
}
// Evento de ordenamiento (retornar true para que Toba NO haga el sort automático)
function evt__cuadro__ordenar($param)
{
// $param['columna'], $param['sentido'] ('asc' o 'desc')
$this->s__orden = $param;
return true; // el CI manejará el orden
}El filtro es un formulario especial para búsqueda.
class ci_listado extends toba_ci
{
protected $s__filtro;
// Datos actuales del filtro para pre-cargar
function conf__filtro()
{
if (isset($this->s__filtro)) {
return $this->s__filtro;
}
}
// El usuario aplicó el filtro
function evt__filtro__filtrar($datos)
{
$this->s__filtro = $datos;
}
// El usuario limpió el filtro
function evt__filtro__cancelar()
{
unset($this->s__filtro);
}
// El cuadro usa los datos filtrados
function conf__cuadro(toba_ei_cuadro $cuadro)
{
$datos = $this->cn()->listar($this->s__filtro);
$cuadro->set_datos($datos);
}
}La pantalla es el contenedor de solapas (tabs).
class ci_abm extends toba_ci
{
function conf()
{
// Ocultar/mostrar solapas dinámicamente
$this->pantalla()->tab('detalle')->ocultar();
$this->pantalla()->tab('listado')->mostrar();
// Desactivar una solapa (se ve pero no se puede clickear)
$this->pantalla()->tab('avanzado')->desactivar();
// Cambiar etiqueta e imagen de una solapa
$this->pantalla()->tab('listado')->set_etiqueta('Resultados');
$this->pantalla()->tab('listado')->set_imagen('buscar.png');
// Eliminar un evento de la pantalla actual
$this->pantalla()->eliminar_evento('cancelar');
}
// Navegar programáticamente a una solapa
function evt__cuadro__seleccion($fila)
{
$this->s__seleccionado = $fila;
$this->set_pantalla('detalle'); // nombre de la solapa destino
}
// Obtener la solapa actualmente visible
function conf__cuadro()
{
$pantalla_actual = $this->get_id_pantalla();
// ...
}
// Wizard: navegar hacia adelante/atrás
function conf()
{
if ($this->wizard_avanza()) {
// navegando hacia adelante
}
}
}Eventos de entrada/salida de solapas:
// Cuando el usuario entra a la solapa 'detalle'
function evt__detalle__entrada()
{
// Inicializar formulario de detalle
}
// Cuando el usuario intenta salir de 'detalle'
function evt__detalle__salida()
{
// Validar antes de salir; lanzar excepción para cancelar
if ($this->hay_cambios_sin_guardar()) {
throw new toba_error_usuario('Hay cambios sin guardar');
}
}function conf__arbol()
{
return array(
array('id' => 1, 'padre' => null, 'nombre' => 'Raíz'),
array('id' => 2, 'padre' => 1, 'nombre' => 'Hijo 1'),
array('id' => 3, 'padre' => 1, 'nombre' => 'Hijo 2'),
);
}
function evt__arbol__seleccion($nodo)
{
$this->s__nodo_id = $nodo['id'];
}Tabla de datos en memoria que rastrea cambios (INSERT/UPDATE/DELETE):
// Dentro del CN:
$tabla = $this->dep('tabla_clientes');
$tabla->cargar(); // carga desde DB
$filas = $tabla->get_datos(); // array de filas
$tabla->set_datos($nuevas_filas); // reemplaza todo
$tabla->agregar_fila(array('nombre'=>'X')); // INSERT pendiente
$tabla->modificar_fila(0, array('nombre'=>'Y')); // UPDATE pendiente
$tabla->borrar_fila(0); // DELETE pendiente
// Al destruir el CN, los cambios se sincronizan con la DB// Obtener conexión a la fuente de datos por defecto
$db = toba::db();
// A una fuente específica
$db = toba::db('nombre_fuente');
// Ejecutar consulta
$resultado = $db->consultar("SELECT * FROM clientes WHERE activo = 1");
// Con parámetros (usar siempre para evitar SQL injection)
$resultado = $db->consultar(
"SELECT * FROM clientes WHERE id = $1",
array($id)
);
// Ejecutar acción (INSERT/UPDATE/DELETE)
$db->ejecutar("UPDATE clientes SET activo = $1 WHERE id = $2", array(0, $id));
// Transacciones manuales
$db->abrir_transaccion();
try {
$db->ejecutar("...");
$db->cerrar_transaccion();
} catch (Exception $e) {
$db->abortar_transaccion();
throw $e;
}La clase toba es una fachada estática con acceso a todos los servicios del framework:
// Notificaciones al usuario
toba::notificacion()->agregar('Operación exitosa', 'exito');
toba::notificacion()->agregar('Atención: revisar datos', 'info');
toba::notificacion()->agregar('Error al procesar', 'error');
// Logger
toba::logger()->debug('Mensaje de debug', 'mi_categoria');
toba::logger()->info('Información', 'mi_categoria');
toba::logger()->error('Error grave', 'mi_categoria');
// Usuario actual
$usuario = toba::usuario();
$login = $usuario->get_login();
$nombre = $usuario->get_nombre();
// Verificar permisos
if (toba::derechos()->tiene_permiso('ABM_CLIENTES')) { ... }
// Proyecto
$id_proyecto = toba::proyecto()->get_id();
$param = toba::proyecto()->get_parametro('mi_parametro');
// Instalación
if (toba::instalacion()->es_produccion()) { ... }
// Memoria de sesión (baja nivel)
toba::memoria()->set_parametro('mi_clave', $valor);
$valor = toba::memoria()->get_parametro('mi_clave');
// Escape seguro para output (anti-XSS)
echo toba::escaper()->escapeHtml($texto);
echo toba::escaper()->escapeJs($texto);
// Generador de vínculos
$url = toba::vinculador()->get_url_operacion('mi_proyecto', 'id_item');// Generar una URL a otro ítem
$url = toba::vinculador()->get_url_operacion('proyecto_id', 'item_id');
// Con parámetros pasados por canal
toba::vinculador()->set_parametro_canal('cliente_id', $id);
$url = toba::vinculador()->get_url_operacion('proyecto_id', 'item_id');
// Leer parámetros recibidos por canal en el CI destino
function ini__operacion()
{
if (isset($this->_canal_recibidos)) {
$this->s__cliente_id = $this->_canal_recibidos['cliente_id'];
}
}
// Activar navegación por AJAX (toda la app navega sin recargar página)
toba_ci::set_navegacion_ajax(true);
// (se llama típicamente desde el CI principal o contexto de ejecución)class ci_abm_ejemplo extends toba_ci
{
protected $s__clave;
protected $s__modo; // 'alta' | 'modificacion'
// LISTADO
function conf__cuadro()
{
return $this->cn()->listar();
}
function evt__cuadro__alta()
{
$this->s__modo = 'alta';
$this->s__clave = null;
$this->set_pantalla('formulario');
}
function evt__cuadro__modificacion($fila)
{
$this->s__modo = 'modificacion';
$this->s__clave = $fila['id'];
$this->set_pantalla('formulario');
}
function evt__cuadro__baja($fila)
{
$this->cn()->eliminar($fila['id']);
}
// FORMULARIO
function conf__formulario(toba_ei_formulario $form)
{
if ($this->s__modo == 'modificacion') {
$form->set_datos($this->cn()->get($this->s__clave));
}
}
function evt__formulario__modificacion($datos)
{
$this->cn()->guardar($datos, $this->s__clave, $this->s__modo);
$this->set_pantalla('listado');
}
function evt__cancelar()
{
$this->set_pantalla('listado');
}
}class ci_busqueda extends toba_ci
{
protected $s__filtro;
function conf__filtro()
{
return $this->s__filtro;
}
function evt__filtro__filtrar($datos)
{
$this->s__filtro = $datos;
}
function evt__filtro__cancelar()
{
unset($this->s__filtro);
}
function conf__cuadro()
{
return $this->cn()->buscar($this->s__filtro);
}
}class ci_wizard extends toba_ci
{
protected $s__paso1_datos;
protected $s__paso2_datos;
function conf()
{
$pantalla = $this->get_id_pantalla();
// Lógica de skip de pasos
if ($pantalla == '3' && condicion_skip()) {
$paso = $this->wizard_avanza() ? 4 : 2;
$this->set_pantalla($paso);
}
// Cambiar etiqueta del botón "Siguiente" en el último paso
if ($pantalla == '4') {
$this->pantalla()->evento('cambiar_tab__siguiente')->set_etiqueta('Finalizar');
}
}
function evt__form_paso1__modificacion($datos)
{
$this->s__paso1_datos = $datos;
}
function evt__form_paso2__modificacion($datos)
{
$this->s__paso2_datos = $datos;
$this->cn()->procesar($this->s__paso1_datos, $this->s__paso2_datos);
}
}| Concepto | Convención | Ejemplo |
|---|---|---|
| Clase CI | ci_<nombre> |
ci_listado_clientes |
| Clase CN | cn_<nombre> |
cn_clientes |
| Propiedad de sesión | s__<nombre> |
$s__datos_form |
| Callback de config | conf__<id_dep> |
conf__formulario |
| Handler de evento | evt__<dep>__<evento> |
evt__cuadro__seleccion |
| Método AJAX | ajax__<nombre> |
ajax__calcular_total |
| Callback ini | ini__operacion / ini |
|
| Callback fin | fin |
|
| Callback post-eventos | post_eventos |
|
| Archivos PHP | snake_case | ci_listado_clientes.php |
| Métodos | snake_case | get_datos_cliente() |
/mi_proyecto/
├── www/
│ └── aplicacion.php ← Punto de entrada HTTP
├── php/
│ ├── componentes/ ← Clases CI, CN y personalizaciones de EI
│ │ ├── mi_ci/
│ │ │ ├── ci_ejemplo.php
│ │ │ └── cn_ejemplo.php
│ │ └── ...
│ ├── lib/ ← Librerías propias del proyecto
│ └── mi_proyecto_autoload.php
├── metadatos/ ← Definiciones de componentes (editor Toba)
│ ├── items/
│ ├── componentes/
│ └── ...
├── proyecto.ini ← Configuración del proyecto
└── instalacion/
| Archivo | Descripción |
|---|---|
php/nucleo/toba_nucleo.php |
Kernel principal (singleton) |
php/nucleo/toba.php |
Fachada estática de servicios |
php/nucleo/toba_solicitud_web.php |
Procesamiento del request HTTP |
php/toba_autoload.php |
Mapa de clases → archivos |
| Archivo | Descripción |
|---|---|
php/nucleo/componentes/toba_componente.php |
Clase base de todos los componentes |
php/nucleo/componentes/interface/toba_ei.php |
Base de elementos de interface |
php/nucleo/componentes/interface/toba_ci.php |
Controlador de Interface (leer completo) |
php/nucleo/componentes/negocio/toba_cn.php |
Controlador de Negocio |
php/nucleo/componentes/interface/toba_ei_pantalla.php |
Pantalla/solapas |
php/nucleo/componentes/interface/toba_ei_formulario.php |
Formulario |
php/nucleo/componentes/interface/toba_ei_cuadro.php |
Grilla |
php/nucleo/componentes/interface/toba_ei_filtro.php |
Filtro |
| Archivo | Descripción |
|---|---|
php/nucleo/componentes/persistencia/toba_datos_tabla.php |
Tabla de datos en memoria |
php/nucleo/componentes/persistencia/toba_ap_tabla_db.php |
Patrón acceso a DB |
php/lib/db/toba_db.php |
Abstracción de base de datos |
php/nucleo/lib/toba_serializar_propiedades.php |
Serialización de objetos en sesión |
| Archivo | Descripción |
|---|---|
proyectos/toba_referencia/php/componentes/ci/ci_wizard.php |
Wizard multi-paso |
proyectos/toba_referencia/php/componentes/ci/ci_solapas.php |
Manejo de solapas con estado |
proyectos/toba_referencia/php/componentes/ci/ci_tabs.php |
Ocultar/mostrar tabs dinámicamente |
proyectos/toba_referencia/php/componentes/ei_filtro - ei_cuadro/extension_ci.php |
Filtro + cuadro completo |
proyectos/toba_referencia/php/componentes/ajax/ci_ajax.php |
Todos los patrones AJAX |
proyectos/toba_referencia/php/componentes/eventos/extension_ci.php |
Manejo avanzado de eventos |
// Muestra un mensaje al usuario sin abortar la operación de forma fatal
throw new toba_error_usuario("El campo X es obligatorio");
// Error técnico (aborta y muestra error)
throw new toba_error("Error interno grave");// En el CI origen: pasar datos al CI destino via canal
toba::memoria()->set_parametro(
apex_hilo_qs_canal_obj . 'id_ci_destino',
array('cliente_id' => 5)
);
// En el CI destino: leer en ini__operacion
function ini__operacion()
{
if (isset($this->_canal_recibidos)) {
$this->s__cliente_id = $this->_canal_recibidos['cliente_id'];
}
}class mi_formateo extends toba_formateo
{
function formato_moneda($valor)
{
return '$ ' . number_format($valor, 2, ',', '.');
}
}
// En conf__cuadro:
function conf__cuadro(toba_ei_cuadro $cuadro)
{
$cuadro->set_formateo_columna('importe', 'moneda', 'mi_formateo');
$cuadro->set_datos($this->cn()->listar());
}function extender_objeto_js()
{
// $this->objeto_js contiene el ID JS del componente actual
$id_js = toba::escaper()->escapeJs($this->objeto_js);
echo "
{$id_js}.evt__mi_boton = function() {
// lógica JS del botón
return false; // cancelar submit normal
};
";
}