Controlador de Interface
Un controlador de interface (ci) es el responsable de manejar las pantallas y sus distintos elementos contenidos, siendo el nexo existente entre la capa de persistencia/negocio y la capa interface (eis).
Mantiene el concepto de navegación en una operación, constituyendo distintas pantallas.
Contiene y controla otros componentes como cuadros, filtros, formularios o incluso otros ci. Permite escuchar eventos propios o de componentes contenidos y [referencia/Configuracion configurarlos], definiendo así el comportamiento de las operaciones.
Mantiene el estado de la operación a través de los distintos pedidos de página, utilizando el mecanismo de persistencia en sesión.
Determina el marco transaccional de la operación, procesando los cambios al final de una transacción lógica que atraviesa varios pedidos de página.
El CI además de contener y controlar cosas tiene una API propia, con ventanas para inicializar una operación, un pedido de página, configurarse, atrapar sus eventos, borrar los datos de la sesión, etc.
Definición
En su definición en el editor se puede:
Definir distintas pantallas y especificar extensiones de las mismas.
Incluir otros componentes como dependencias y asociarlos a las pantallas.
Definir eventos y asociarlos a las pantallas.
Pantallas
Para la construcción de interfaces más flexibles, el CI tiene capacidad de manejar diferentes pantallas en una operación. Así, por ejemplo, reservar una entrada puede pensarse como dos pantallas: una especificando el pago y la otra seleccionando la ubicación. La navegación entre estas pantallas puede ser:
A través de solapas (tabs).
En un estilo wizard (anterior, siguiente)
Definida exclusivamente en la extensión del componente (cuando tal cosa sucede ir a la pantalla tal)
Personalizando la navegación
Desde los listeners de eventos y la configuración general del CI se puede determinar cual pantalla mostrar con el método toba_ci::set_pantalla($id). Por ejemplo
<?php
function conf()
{
if (isset($this->s__forma_pago)) {
$this->set_pantalla('pant_ubicacion');
} else {
$this->set_pantalla('pant_pago');
}
}
?>
Una vez que el CI decide que pantalla utilizar, delega la generación del servicio a un componente de clase toba_ei_pantalla, fuertemente acoplado al CI. Esta pantalla puede tener una extensión para redefinir su layout, e incluso graficar contenido estático o radicalmente distinto a de los componentes disponibles. La forma de acceder al API de la pantalla actual en el ci es a través del método toba_ci::pantalla():
<?php
function conf()
{
$this->pantalla()->set_descripcion('Recuerde Guardar los datos antes de salir');
}
?>
Al igual que los componentes las pantallas pueden tener configuraciones
específicas, definiendo en el CI el método conf__idpantalla
, por
ejemplo:
<?php
function conf__pant_pago()
{
$this->pantalla()->tab('pant_ubicacion')->desactivar();
$this->dep('form_pago_cheque')->colapsar();
}
?>
Controlando la entrada y la salida
Cuando se entra y sale de una pantalla se dispara un evento que es
posible capturar para, por ejemplo, hacer controles. Los eventos se
llaman entrada
y salida
:
<?php
function evt__pant_pago__salida()
{
if ($this->s__pago < $this->importe_entrada) {
throw new toba_error("Debe abonar al menos $ {$this->importe_a_pagar}");
}
}
function evt__pant_ubicacion__entrada()
{
if (count($this->s__reservas) >= $this->maximas_reservas) {
throw new toba_error("Lo sentimos, se ha llegado al límite de reservas");
}
}
?>
Una excepción lanzada en estos listeners evita el cambio de pantalla y el mensaje de la excepción es agregado a la cola de mensajes de la operación para ser mostrada al usuario.
Cambio de Layout
Una de las ventajas de la delegación de salida a una pantalla es que se
puede extender la pantalla para poder
cambiar su disposición gráfica o layout. Por defecto el layout de una
pantalla es una depedencia arriba de la otro, esto se puede cambiar
extendiendo la pantalla (debe heredar de toba_ei_pantalla
) y
redefiniendo el método generar_layout
incluyendo el html deseado. El
método contiene originalmente este código:
<?php
protected function generar_layout()
{
$existe_previo = 0;
foreach($this->dependencias as $dep) {
if ($existe_previo) {
echo "<hr>\n";
}
$dep->generar_html();
$existe_previo = 1;
}
}
?>
<div style='clear:both'> </div>
Por ejemplo si se quiere lograr un layout de dos columnas se puede redefinir el método de la siguiente manera:
<?php
protected function generar_layout()
{
echo "<table>";
$i = 0;
foreach($this->dependencias as $dep) {
$ultimo = ($i == count($this->dependencias));
if ($i % 2 == 0) {
echo "<tr>";
}
echo "<td>";
$dep->generar_html();
echo "</td>";
$i++;
if ($i % 2 == 0 || $ultimo) {
echo "</tr>";
}
}
echo "</table>";
}
?>
Dependencias
El CI es un contenedor de dependencias, componentes que delegan al CI su configuración y eventos.
Para acceder a una depedencia particular se utiliza el método toba_ci::dep($id)). Así, siguiendo el ejemplo, para acceder al formulario desde cualquier método del CI se ejecuta:
<?php
$this->dep('form')->metodo();
?>
Configuración
Para la configuración de una dependencia se
definen métodos conf__dependencia($dep)
recibiendo como parámetro la
dependencia que se quiere configurar, por ejemplo para configurar un
formulario, se escribiría un método así:
<?php
function conf__form(toba_ei_formulario $form)
{
//-- *$form == $this->dep('form')
$form->ef('nombre')->set_etiqueta('Nombre');
$form->set_datos(array('nombre' => 'Julián',..));
}
?>
Listeners de Eventos
Para definir comportamientos a sus dependencias, se escuchan los
Eventos producidos por estas definiendo el método
evt__dependencia__evento
, los parámetros recibidos depende de lo que
notifique particularmente el evento:
<?php
function evt__form__modificacion($datos)
{
$this->s__datos_form = $datos;
}
?>
Persistencia en Sesión
En la subclase específica de un componente se pueden especificar aquellas propiedades que se desean mantener para el próximo pedido de página. Como se ve en el marco transaccional de una operación, se necesita una forma de recordar cosas en un intercambio cliente (browser) *servidor (php). En una programación ad-hoc se utilizan las llamadas variables de sesión de PHP, pero para organizar y optimizar el consumo de estas variables, los componentes ofrecen tomar la responsabilidad de administrarlas, almacenándolas cuando se termine la construcción de una página y restaurándolas en el siguiente pedido.
La forma de indicar al framework que una propiedad sea mantenida en
sesión es prefijar su nombre con s__
(de sesión), por ejemplo:
<?php
class ci_forma_pago extends toba_ci
{
protected $s__importe = 0; //-- *se persiste
protected $tope_maximo; //-- *no se persiste
protected $s__medio_pago; //-- *se persiste
}
?>
Se recomienda persistir estructuras de datos (tipos básicos, arreglos, etc.) y no objetos, aunque es posible persistir grafos completos de objetos con algunas consideraciones:
- Si estos objetos poseen otros por composición, es posible que las clases de estos estén definidos en un archivo no incluido originalmente, para esto se debe utilizar la función __autoload de PHP que permite al sistema encontrar el archivo de esta clase antes de intentar reconstuir el objeto.
- Si en esta composición existen referencias circulares es necesario
definir
__sleep
en el objeto hijo, para anular el controlador y__wakeup
en el padre para restablecerlo.
Ejemplos
Existen ejemplos en el proyecto Referencia, carpeta Componentes/Controladores de Interface.