G2/3w/2.9.2/DocTecnica/Personalizaciones/Operacion

<volver>

Creación de una operación en G3W2

En este tutorial se va a crear una nueva operación como una personalización. La operación tendrá dos componentes, un formulario y un cuadro. Este último se cargará a partir del contenido de una tabla en la base de datos, que a la vez es modificada por el formulario.

Requisitos y asunciones

  • $PROJECT_ROOT contiene el path a la carpeta inicial del proyecto. Si desea configurarla para poder copiar/pegar ejecutar el siguiente comando:
    export PROJECT_ROOT=/path/a/g3w2
    
  • Se asume que se cuenta con una instalación funcional de G3W2 >= 2.9.2

Creación de la tabla en la BD

Se crea una tabla simple en el esquema de Guaraní.

CREATE TABLE tut_form (
        id serial,
        nombre   varchar(40),
        apellido varchar(40)
);

Insertamos unos datos de prueba

INSERT INTO tut_form (nombre, apellido) VALUES ('Juan', 'Perez');
INSERT INTO tut_form (nombre, apellido) VALUES ('Roberto', 'Gomez');

Configuración de la personalización

Todo el tutorial estará en una personalización separada llamada tut_01. Para preparar el sistema para ser personalizado hay q seguir los siguientes pasos:

  • Crear la carpeta src/pers/tut_01
    cd $PROJECT_ROOT
    mkdir src/pers/tut_01
    
  • Activar la personalización en el archivo instalacion/config.php
    • Chequear que el flag usar_personalizaciones tenga el valor true
    • En el punto de acceso que apunta a la base donde creamos la tabla setear la clave personalizacion a tut_01

Creación de la estructura de la operación

Dentro de la personalización creamos la siguiente estructura

├── controlador.php
├── cuadro_nombres
│   └── default.twig
├── pagelet_form_nombres.php
├── form_nombres
│   ├── builder_form_nombres.php
│   └── default.twig
├── pagelet_cuadro_nombres.php
├── template.twig
└── vista.php

Los comandos para crear esa estructura fueron los siguientes

cd $PROJECT_ROOT/src/pers/tut_01
mkdir -p operaciones/abm_nombres
cd operaciones/abm_nombres
mkdir form_nombres
mkdir cuadro_nombres
touch controlador.php
touch vista.php
touch template.twig
touch form_nombres/default.twig
touch form_nombres/builder_form_nombres.php
touch pagelet_form_nombres.php
touch cuadro_nombres/default.twig
touch pagelet_cuadro_nombres.php

Agregar la operación al menú

Para agregar una operación al menú de alumnos hay que personalizar el archivo siu/conf/acceso/acc_ALU.php

cd $PROJECT_ROOT
mkdir -p src/pers/tut_01/conf/acceso
cp src/siu/conf/acceso/acc_ALU.php src/pers/tut_01/conf/acceso

Una vez copiado el archivo hay que agregar la siguiente entrada

<?php
return array(
    'operaciones' => array(
        'abm_nombres' => array(
            'activa' => true,
            'menu' => array(
                'visible' => true
            )
        )
    )
);

Si accedemos al sistema con un alumno en este momento veremos que se agregó un item al menú llamado header.menu.listado_alumnos, este nombre es la clave que se busca en el archivo de mensajes, como todavía no existe muestra la clave. Para crear la clave hay que personalizar el archivo siu/mensajes/mensajes.es.php

cd $PROJECT_ROOT
mkdir src/pers/tut_01/mensajes
touch src/pers/tut_01/mensajes/mensajes.es.php

Las claves de este archivo pisan y agregan a las que están en el archivo del SIU. Así que sólo deberemos agregar lo que necesitemos

# archivo src/pers/tut_01/mensajes/mensajes.es.php

<?php
return array(
        'header.menu.abm_nombres' => 'ABM Nombres',
        'tit_abm_nombres' => 'ABM Nombres',
        'abm_nombres_tit_form' => 'Formulario de nombres',
        'abm_nombres_btn_agregar' => 'Agregar',
);

Hola Mundo!

En este momento si intentamos acceder a la operación obtendremos un error. Ahora vamos a implementar lo mínimo para lograr ver la operación vacía.

El controlador debe al menos definir dos métodos, uno que provee acceso al modelo (inexistente actualmente) y una acción index.

<?php
// Archivo src/tut_01/operaciones/abm_nombres/controlador.php
namespace tut_01\operaciones\abm_nombres;
use siu\extension_kernel\controlador_g3w2;

class controlador extends controlador_g3w2
{       
    function modelo()
    {
        return null;
    }

    function accion__index()
    {
    }
}
?>

La vista debe definir el método ini, donde se especifica el título de la operación y más adelante se configuraran los pagelets.

<?php
// Archivo src/tut_01/operaciones/abm_nombres/vista.php

namespace tut_01\operaciones\abm_nombres;
use siu\extension_kernel\vista_g3w2;

class vista extends vista_g3w2
{
    function ini()
    {
    }
}
?>

Y por último hay que definir el template de la vista

{# Archivo src/tut_01/operaciones/abm_nombres/template.twig #}
{% extends "kernel/dos_columnas.twig" %}
{% block titulo_operacion %}<h2>{{"tit_abm_nombres"|trans|capitalize}}</h2>{% endblock %}
{% block columna_1 %}

{% endblock %}
{% block columna_2 %}

{% endblock %}

Ahora ya podremos acceder a la operación!

Arquitectura de la operación

Antes de seguir adelante hay que tener en claro lo que queremos construir. La idea es tener un formulario en página que permita dar de alta personas con su nombre y apellido. También deseamos ver un cuadro que liste el contenido de la tabla creada anteriormente. En futuros tutoriales se verá como modificar y borrar elementos de la tabla.

El modelo

El acceso al modelo desde la aplicación generalmente se construye en dos capas. Una capa de transacción que contiene acciones de alto nivel sobre el modelo y una de más bajo nivel, llamada catálogo, que contiene las sentencias SQL a ejecutarse. Este ejemplo podría beneficiarse de una estructura más simple, pero al armarlo de esta manera se tienen dos beneficios:

  • Crear algo que puede escalar en complejidad de acceso al modelo
  • Ayuda a entender el código de las operaciones más complejas del G3W2

Los catálogos del sistema se ubican en la carpeta src/pers/tut_01/modelo/datos/db. Con el siguiente comando se crea la estructura de carpetas y el archivo vacío en la personalización

cd $PROJECT_ROOT
mkdir -p src/pers/tut_01/modelo/datos/db
touch src/pers/tut_01/modelo/datos/db/nombres.php

A continuación vemos el código del catálogo

<?php
namespace tut_01\modelo\datos\db;
use \kernel\kernel;

class nombres
{
        /**
         * parametros: nombre, apellido
         * cache: no
         */
        function insertar_nombre($parametros)
        {
                $sql = "
                        INSERT INTO tut_form (nombre, apellido) 
                        VALUES (
                                {$parametros['nombre']}, 
                                {$parametros['apellido']}
                        );
                ";
                kernel::db()->ejecutar($sql);
        }

        /**
         * cache: no
         */
        function lista()
        {
                $sql = "SELECT id, nombre, apellido FROM tut_form";
                return kernel::db()->consultar($sql);
        }
}

Las clases de catálogo son simplemente contenedores de métodos con las consultas SQL. Lo único especial de estos archivos son las annotations que tiene cada método. Toda función de catálogo debe tener un bloque de comentarios arriba. En este caso se utilizan dos claves:

  • parametros: especifica la lista de parámetros que recibirá este método
  • cache: especifica el tipo de cache que tendrá la consulta de este métodos. Por ahora está desactivado

Cada vez que se crea un nuevo archivo de catálogo hay que ejecutar el siguiente comando

cd $PROJECT_ROOT
bin/guarani generar_catalogo ua_01

Este comando compila los comentarios de los catálogos a archivos php para su posterior consumo.

La transacción

Las transacciones del sistema se ubican en la carpeta src/pers/tut_01/modelo/transacciones. Con el siguiente comando se crea la estructura de carpetas y el archivo vacío en la personalización

cd $PROJECT_ROOT
mkdir -p src/pers/tut_01/modelo/transacciones
touch src/pers/tut_01/modelo/transacciones/carga_nombres.php

Por ahora la transacción tendrá dos métodos, uno de consulta y otro de inserción. La convención en las transacciones es que los métodos de consulta comienzan con info__ y las que efectuan cambios en la base tienen el prefijo evt__. A continuación se muestra el contenido

<?php
namespace tut_01\modelo\transacciones;
use siu\modelo\datos\catalogo;

class carga_nombres
{
        //---------------------------------------------
        //      INFO
        //---------------------------------------------
        
        function info__nombres()
        {
                return catalogo::consultar('nombres', 'lista', array());
        }

        //---------------------------------------------
        //      EVENTOS
        //---------------------------------------------

        function evt__agregar_nombre($parametros)
        {
                catalogo::consultar('nombres', 'insertar_nombre', $parametros);
        }
}

El controlador

El controlador tiene dos propósitos

  • Define acciones que se pueden realizar sobre el sistema (métodos que tienen el prefijo accion__)
  • Es intermediario entre vista y modelo

Ahora que ya definimos nuestra capa de modelo hay que actualizar el método modelo del controlador. A continuación vemos en nuevo código del controlador

<?php
namespace tut_01\operaciones\abm_nombres;
use siu\extension_kernel\controlador_g3w2;
use tut_01\modelo\transacciones\carga_nombres;
use kernel\kernel;

class controlador extends controlador_g3w2
{       
    function modelo()
    {
        return new carga_nombres();
    }

    function accion__index()
    {
    }
}

Una nota sobre los pagelets

A continuación vamos a emprender la construcción visual de la operación. El bloque de construcción básico de G3W3 es el pagelet.

Un pagelet es un conjunto de archivos que define una parte de la pantalla. Mínimamente tienen la siguiente estructura

├── cuadro_nombres
│   └── default.twig
└── pagelet_cuadro_nombres.php
  • Un archivo php que prepara los datos para ser consumido por el template
  • Un archivo twig que define el template html del pagelet
  • Adentro de la carpeta del pagelet pueden existir archivos js que definen el comportamiento en el lado del cliente
  • Adentro de la carpeta del pagelet pueden existir archivos css para hacer mejoras específicas al template

El cuadro

El cuadro será una tabla con todos los elementos. Antes de preparar la vista hay que configurar la operación para que incluya este pagelet.

Configuración para la inclusión del pagelet

Modificación del template.twig

El archivo template.twig contiene el layout visual de toda la operación. Lo que debemos hacer es agregar el pagelet a este layout. Así quedaría el archivo

{% extends "kernel/dos_columnas.twig" %}
{% block titulo_operacion %}<h2>{{"tit_abm_nombres"|trans|capitalize}}</h2>{% endblock %}
{% block columna_1 %}
{% endblock %}
{% block columna_2 %}
    {{ cuadro_nombres.render }} {# SE AGREGA EL PAGELET A LA ESTRUCTURA VISUAL EN LA 2DA COLUMNA #}
{% endblock %}

Modificación del vista.php

La vista es la que prepara los datos para ser consumidos por el template anterior. Nuestra clase vista.php quedaría de la siguiente manera

<?php
namespace tut_01\operaciones\abm_nombres;
use siu\extension_kernel\vista_g3w2;
use kernel\kernel;

class vista extends vista_g3w2
{
    function ini()
    {
        $clase = 'operaciones\abm_nombres\pagelet_cuadro_nombres';
        $pl = kernel::localizador()->instanciar($clase, 'cuadro_nombres');
        $this->add_pagelet($pl); // SE AGREGA EL PAGELET A LA VISTA
    }
}

Creación del pagelet

Lo primero que debemos hacer es generar los datos que tiene que utilizar el template para mostrar la tabla. Esta generación se hace en el archivo pagelet_cuadro_nombres.php. Los archivos php de los pagelets tienen un método prepare donde tienen que preparar los valores. A continuación vemos el archivo resultante

<?php
namespace tut_01\operaciones\abm_nombres;
use kernel\interfaz\pagelet;

class pagelet_cuadro_nombres extends pagelet
{
    function get_nombre()
    {
        return 'cuadro_nombres';
    }

    function prepare()
    {
        // la variable this.data se utiliza luego desde el template
        $this->data = array();
        // se consume desde la transacción creada anteriormente
        $this->data['nombres'] = $this->controlador->modelo()->info__nombres();
    }
}

El segundo paso es hacer el template html. Lo único que hay que hacer es definir la tabla e iterar los datos preparados para completarla. A continuación vemos el archivo cuadro_nombres/default.twig

{% extends "kernel/pagelet.twig" %}
{% block contenido %}
{# Se utiliza bootstrap. Ver http://getbootstrap.com/2.3.2/ para más detalles #}
<table class='table table-condensed table-hover table-striped'>
	<thead>
		<tr>
			<th>#</th>
			<th>{{'nombre'|trans}}</th>
			<th>{{'apellido'|trans}}</th>
		</tr>
	</thead>
	<tbody>
		{# prestar atención al consumo de la variable creada en pagelet_cuadro_nombres #}
		{% for persona in this.data.nombres %}
			<tr>
				<td>{{ persona.ID }}</td>
				<td>{{ persona.NOMBRE }}</td>
				<td>{{ persona.APELLIDO }}</td>
			</tr>
		{% endfor %}
	</tbody>
</table>
{% endblock %}

Si ingresamos nuevamente a la operación desde el browser ya se puede ver el cuadro!

El formulario

Construiremos un formulario que agrega elementos a la tabla. El primer paso es el de configuración del pagelet (igual que para el cuadro, se agrega a la vista y al template).

Una vez configurado el pagelet tenemos que construir el formulario.

Construcción del formulario

Para construir un formulario vamos a utilizar un builder definido en el archivo form_nombres/builder_form_nombres.php. Hay que definir cuatro métodos en esta clase para llevar a cabo su propósito, estos métodos son:

  • get_id_html: define cuál será el id html del formulario. Útil para interacciones con JS
  • get_action: define la acción contra la cuál se posteará el formulario al hacer el submit
  • generar_definicion: donde se crean los campos del formulario
  • get_configuracion_layout_grilla: Por defecto los forms se basan en un layout de grilla que está dividido en grupos. Estos contienen filas y las filas son los elementos del formulario creados en generar_definicion

A continuación vemos el código del builder

<?php
namespace tut_01\operaciones\abm_nombres\form_nombres;
use kernel\interfaz\componentes\forms\form_elemento_config;
use kernel\kernel;
use kernel\util\validador;
use siu\extension_kernel\formularios\builder_formulario;
use siu\extension_kernel\formularios\fabrica_formularios;
use siu\extension_kernel\formularios\guarani_form;

class builder_form_nombres extends builder_formulario
{
        function get_id_html() 
        {
                return 'formulario_nombres';
        }
        
        function get_action() 
        {
                // se posteará a la acción index del controlador
                return kernel::vinculador()->crear('abm_nombres', 'index');
        }
        
        protected function generar_definicion(guarani_form $form, fabrica_formularios $fabrica) 
        {
                $form->add_accion($fabrica->accion_boton_submit('agregar', kernel::traductor()->trans('abm_nombres_btn_agregar')));
                
                $form->add_elemento($fabrica->elemento('nombre', array(
                        // notar el validador TIPO_ALPHA. Esto luego lo usaremos para ver el manejo de errores
                        form_elemento_config::filtro            => validador::TIPO_ALPHA,
                        form_elemento_config::obligatorio       => true,
                        form_elemento_config::elemento          => array('tipo' => 'text'),
                        form_elemento_config::largo             => 40
                )));
                $form->add_elemento($fabrica->elemento('apellido', array(
                        form_elemento_config::filtro            => validador::TIPO_TEXTO,
                        form_elemento_config::obligatorio       => true,
                        form_elemento_config::elemento          => array('tipo' => 'text'),
                        form_elemento_config::largo             => 40
                )));
                // Existen elementos más complejos que estos. Se verán en subsiguientes tutoriales
        }

        /**
         * Por defecto los filtros de guarani se basan en el layout grilla. Se debe configurar en este metodo para poder
         * instanciar el mismo
         */
        function get_configuracion_layout_grilla()
        {
                return array(
                        array(
                                'grupo' => 'nombres',
                                'titulo' => trans('abm_nombres_tit_form'),

                                'filas' => array(
                                        array('nombre' => array('span' => 9)),
                                        array('apellido' => array('span' => 9)),
                                )
                        ),
                );
        }
}

Inclusión del formulario en el pagelet

Básicamente lo que tenemos que hacer es

  • Construir el form con el builder
  • Agregarlo al pagelet para que lo pueda consumir el archivo twig

A continuación vemos el código de pagelet_form_nombres.php

<?php
namespace tut_01\operaciones\abm_nombres;
use kernel\kernel;
use kernel\interfaz\pagelet;

class pagelet_form_nombres extends pagelet
{
        protected $form_builder;
        
        function get_nombre()
        {
               return 'form_nombres';
        }


        /**
         * @return builder_form_nombres
         */
        function get_builder_form()
        {
                // se hace esta función para no construir cada vez el builder
                if (! isset($this->form_builder)) {
                        $this->form_builder = kernel::localizador()->instanciar('operaciones\abm_nombres\form_nombres\builder_form_nombres');
                }
                
                return $this->form_builder;
        }
        
        function prepare()
        {
                // Obtenemos el formulario del builder
                $form = $this->get_builder_form()->get_formulario();
                // Lo inicializamos
                $form->inicializar();
                // Se agrega al pagelet
                $this->add_form($form);
        }
}

Modificación del vista.php

<?php
namespace tut_01\operaciones\abm_nombres;
use siu\extension_kernel\vista_g3w2;
use kernel\kernel;

class vista extends vista_g3w2
{
    function ini()
    {
        $clase = 'operaciones\abm_nombres\pagelet_cuadro_nombres';
        $pl = kernel::localizador()->instanciar($clase, 'cuadro_nombres');
        $this->add_pagelet($pl);
        
        $clase = 'operaciones\abm_nombres\pagelet_form_nombres';
        $pl = kernel::localizador()->instanciar($clase, 'form_nombres');
        $this->add_pagelet($pl);
        
        kernel::pagina()->set_etiqueta('titulo', kernel::traductor()->trans("tit_abm_nombres"));
    }
}
?>

Modificación del template.twig

El archivo template.twig contiene el layout visual de toda la operación. Lo que debemos hacer es agregar el pagelet a este layout. Así quedaría el archivo

{% extends "kernel/dos_columnas.twig" %}
{% block titulo_operacion %}<h2>{{"tit_abm_nombres"|trans|capitalize}}</h2>{% endblock %}
{% block columna_1 %}
    {{ form_nombres.render }}
{% endblock %}
{% block columna_2 %}
    {{ cuadro_nombres.render }}
{% endblock %}

Renderizando el form

Los formularios tienen una vista incorporada para no tener que escribir todo el html a mano. En el twig utilizaremos esa vista para armar el layout visual. A continuación vemos el código de form_nombres/default.twig

{% extends "kernel/pagelet.twig" %}

{% block contenido %}
	{# Obtenemos una vista del builder #}
	{% set vista_form = this.get_builder_form().get_vista() %}
	{{ vista_form.render_encabezado() | raw }}
	{{ vista_form.render_cuerpo() | raw }}
	{{ vista_form.render_acciones() | raw }}
	{{ vista_form.render_cierre() | raw }}
{% endblock %}

Si accedemos a la operación en este momento veremos nuestro formulario a la izquierda de la pantalla.

Guardando los datos

El guardado de datos se hace en el controlador. Para esto hay que manejar el post del formulario en la acción index. Los forms tienen un método procesar que automáticamente levanta los datos desde el post (o el método que se haya definido) y los valida contra los filtros que se definieron en la creación del formulario en el builder. Este método devuelve true o false, indicando el estado de la validación. Si el formulario es válido obtenemos los datos con el método get_datos y lo guardamos con el evento que habíamos definido en la transacción. A continuación vemos el código revisitado del controlador

<?php
namespace tut_01\operaciones\abm_nombres;
use siu\extension_kernel\controlador_g3w2;
use tut_01\modelo\transacciones\carga_nombres;
use kernel\kernel;

class controlador extends controlador_g3w2
{       
    function modelo()
    {
                return new carga_nombres();
    }

    function accion__index()
    {
                if (kernel::request()->isPost()) {
                        $pagelet = $this->vista()->pagelet('form_nombres');
                        $form = $pagelet->get_builder_form()->get_formulario();

                        if ($form->procesar()) {
                                $this->modelo()->evt__agregar_nombre($form->get_datos());
                        }
                }
    }
}

Ahora nuestro form ya agrega datos y lo podemos ver refrescado en el cuadro! Se puede intentar ingresar un valor numérico en el campo nombre para ver como falla.

Obtención del código

Se puede descargar el código desde el pié de esta página. Todo lo que hay que hacer es poner la carpeta dentro de pers y activar la personalización.

<volver>

Attachments