sábado, 11 de diciembre de 2010

Kumbia + jQuery


En esta entrada voy a seguir trabajando con nuestra tabla 'state'. Recordemos que un estado se encuentra dentro de un país, lo que en base de datos implementamos mediante una clave foranea country_id a la tabla 'country' en la tabla 'state'. A medida que agregamos estados, la lista completa de estados podría llegar a ser muy grande. Una manera alternativa de mostrar los datos, es permitiendo al usuario seleccionar un país (de una lista desplegable) y mostrar en la tabla sólo los estados que pertenecen al país seleccionado.

En computación, como en la vida misma, casi siempre un problema puede ser cierto de diversas formas. Esta no es la excepción. Yo voy a resolver este problema utilizando Ajax. Concretamente, utilizaré jQuery, una de las bibliotecas JavaScript más utilizadas en el mundo, la cual provee un conjunto de funciones que facilitan enormemente el trabajo con Ajax y el DOM.

La idea es programar una función (en JavaScript) que maneje el evento onChange de nuestro combo de países. Cada vez que seleccionemos un país de la lista, realizar una búsqueda en base de datos de los estados que pertenecen al país, y mostrar esa data en una región de página web (concretamente un DIV) definida desde el principio para ese propósito. Así, esta región cambiará dinámicamente cada vez que cambiamos de país, sin necesidad de recargar toda la página.


Agregando jQuery

Para poder utilizar las funciones de jQuery debemos incluir la siguiente línea:

<?php echo Tag::js('jquery/jquery.min'); ?>

en todos los archivos que necesitemos. Si vamos a utilizar jQuery en muchas interfaces, sería lógico incluirla en la plantilla. Pesa menos de 80KB en su versión comprimida.

En la versión actual de kumbia se incluye la versión 1.4.2 de jQuery. La versión más nueva al momento de escribir estas líneas es la 1.4.3, que puede descargarse de la web oficial.


Manos a la obra

Voy a explicar por capas lo que debemos hacer:

Modelo: a nuestro modelo state, debemos agregar una función que busque estados en la base de datos, por la clave del país.

Vista: debemos agregar una nueva vista, en la que tendremos nuestro combo, la función para manejar el evento onChange() del combo, el DIV en el cual mostraremos la tabla de estados.

Controlador: el controlador recibirá de la vista el id del país, debe realizar la búsqueda de estados por país programada ya en el modelo y debe enviar la tabla a la vista nuevamente.


Comencemos por lo más sencillo: el modelo. Necesitamos una función muy sencilla, que reciba el id de un país y busque en BD todos los estados cuyo país sea ese. Podemos utilizar la función find(), pero no lo haré, escribiré mi propio SQL:

public function getStatesWithCountry($page, $ppage=1000)
{
$sqlquery = "SELECT state.id, state.state_name, country.country_name FROM state INNER JOIN country ON(state.country_id = country.id)";
return
$this->paginate_by_sql($sqlquery, "page: $page", "per_page: $ppage");
}

Como vemos, una función muy sencilla. Siguiendo en orden de simplicidad, pasaré al controlador. A nuestro state_controller, agregamos una función index2() cuyo código es el siguiente:

public function index2()
{
$state = new State();

Load::models('country');
$country = new Country();
$listCountries = $country->getCountriesBySql();

$this->country_names = array();
foreach (
$listCountries as $item)
{
$this->country_names[$item->id] = $item->country_name;
}
if(
Input::hasPost('country_id'))
{
$this->listStates = $state->getStatesByCountry(Input::post('country_id'));
View::response('view');
View::select('response');
}
}


En esta función lo que hacemos es:

1) Buscar en BD los países, meter esos países en un arreglo que utilizaremos en nuestra vista para llenar el combo. Nótese que utilizo una función getCountriesBySql(), que hace simplemente un "SELECT * FROM country".
2) La parte importante. Si la función recibe por POST un 'country_id', entonces mediante la función que recien creamos (getStatesByCountry()) llenamos un arreglo con los estados que se encuentran dentro del país especificado y finalmente utilizamos las funciones mágicas: View::response('view') y View::select('response'). Con la primera indicamos que queremos que la respuesta se muestre en la misma vista y con la segunda indicamos el nombre del archivo phtml (en esta caso response.phtml) que embederemos en nuestra vista.

Ahora pasemos a las vistas. Recordemos que tenemos que crear nuestra vista principal (que llamamos index2) y una vista que contendrá el código html del contenido que mostraremos dentro de index2. Comenzamos con la primera:

<script>
$(document).ready(function()
{
$('.target').change(function()
{
var url = $(location).attr('href');
var key = $('.target option:selected').val();
$.post( url,
{ country_id: key },
function(data){
var capa = $('#result');
capa.html(data);
capa.hide();
capa.show('slow');
});
});
});
</script>

<div class="content">
<?php echo View::content(); ?>
<h3>States</h3>

<?php echo Form::open() ?>
<?php

echo
Form::select('country_id', $country_names, 'class="target"'); ?>
<?php echo Form::close() ?>

<!-- Este div sera luego sustituido con la respuesta -->
<div id="result">
<br>
Seleccione un pa&iacute;s
</div>

</div>

Lo primero que encontramos es un código JavaScript que se encargará de manejar el evento OnChange del combobox. Este trozo de código lo codifiqué en base a lo que se consigue sobre .change() en la documentación oficial de jQuery. Básicamente lo que hace es:
1) Cada vez que se produzca un cambio sobre (TODOS) los objetos de clase "target", se hace un POST a su misma dirección Es aquí cuando el controlador recibe el country_id y realiza la búsqueda y devuelve el código HTML de la vista 'response'.
2) Sustituye el código HTML que se encuentra dentro de (TODOS) los objetos con id "result" con la data devuelta.

Aunque creo que no hace falta (y sería bueno hacerlo de ejercicio), muestro a continuación el código de response.phtml:

<table>
<col class="first-column" />
<col span="2" />
<col class="last-column" />
<tr class="first-row">
<th>Season</th>
<th>League</th>
<th></th>
</tr>
<?php foreach ($listStates as $item) : ?>

<tr>
<td><?php echo $item->state_name ?></td>
<td><?php echo $item->country_3166 ?></td>
<td><?php echo Html::linkAction("edit/$item->id/", 'Edit') ?></td>
</tr>

<?php endforeach; ?>
</table>

Es un simple trozo de html en el que mostramos una tabla, con los datos que nos deja el controlador en la variable $listStates.

jQuery es una de las bibliotecas de JavaScript más utilizadas en estos días. Facilita mucho el trabajo y como vemos se integra fácilmente con kumbia. Recomiendo aprender a trabajar con ella. Acá dejo un par de enlaces que pueden ayudar:



Y por supuesto la documentación oficial, que es imprescindible:



Hasta la próxima entrada!